0

I am trying to fetch data grouped by a given column. It works well when I have data. I want to handle the case when I have no data, because it raises an NS error that I could not catch in swift do catch block. I've seen the answers on creating an ObjC wrapper but I it does not apply to my case because I need to return an Array of String.

let request = self.fetchRequest()
request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true

if let results = try? context().fetch(request), // raises exception in testing, but runs fine when run on simulator.
  let dics = results as? [NSDictionary] {
  var resultsArray: [Any] = []
  for dic in dics {
    if let propValue = dic[attribute] {
      resultsArray.append(propValue)
    }
  }
  return resultsArray
}

How might I do this?

John Doe
  • 1,364
  • 1
  • 12
  • 19
  • Until I find a real solution, to move forward, I will fetch twice. First to check if there are any data, run the actual fetch. If no data, initialize to an empty array. – John Doe Nov 13 '16 at 07:32

2 Answers2

0

It's recommended to wrap Core Data fetch lines always in a do - catch block because on success it returns reliably a non-optional array of the specified return type.

Swift has got a strong type system. Casting to unspecified Any or Foundation type NSDictionary doesn't help the compiler and could cause unnecessary compiler errors.

Assuming both key and value of the dictionary are String cast the dictionary to Swift type [String:String]. To forced unwrap the dictionary is absolutely safe because the Core Data model cannot be changed at runtime.

flatMap returns a non-optional array of all values which are not nil.

var resultsArray = [String]()
do {
   let results = try context().fetch(request) as! [[String:String]]
   resultsArray = results.flatMap {$0[attribute] as? String}       
} catch {
   print(error)
}
return resultsArray
vadian
  • 274,689
  • 30
  • 353
  • 361
  • I did the do-try-catch but the problem is the exception being thrown, swift cannot catch it because it is ObjC type exception. The `fetch()` call throws it. It works on simulator, but I worry it will break easily in the future, that is why I want it to pass my integration test. I did find a solution though I will post it as an answer, sorry if I wasted your time. – John Doe Nov 13 '16 at 10:16
0

Based on this answer

It was not immediately obvious to me that it can be done like this:

let request = self.fetchRequest()

request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true


var result: [Any] = []
do {
  try ObjC.catchException {
    let results = try? context().fetch(request)
    if let dics = results as? [NSDictionary] {
      var resultsArray: [Any] = []
      for dic in dics {
        if let propValue = dic[attribute] {
          resultsArray.append(propValue)
        }
      }
      result = resultsArray
    }
  }
} catch {}
return result

Ideally I wanted the array to be returned by ObjC.catchException unfortunately I have no solid grasp of ObjC yet. The scope of the result var looks too wide, I welcome any suggestion to improve it.

I wanted to keep everything in swift for uniformity but I guess I am stuck with this solution for now.

Community
  • 1
  • 1
John Doe
  • 1,364
  • 1
  • 12
  • 19