frank21 frank21 - 6 months ago 70
Swift Question

Fetching selected attribute in entities

I have a core-data entity with several attributes, and I want a list of all the objects in one attribute. My code looks like this:

let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!

let sortDesc = NSSortDescriptor(key: "username", ascending: true)

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]
fetchReq.valueForKey("username")

let en = NSEntityDescription.entityForName("Identities", inManagedObjectContext: context)

userList = context.executeFetchRequest(fetchReq, error: nil) as [Usernames]


But this gives me an NSException-error, and I can't figure out why, or how I'm supposed to do this. I've read the NSFetchRequest class description but couldn't make much sense out of it.

Any suggestions would be appreciated.

EDIT: After a tip from Bluehound I changed my code to this:

var userList = [Model]()
@IBAction func printUsers(sender: AnyObject) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!

let sortDesc = NSSortDescriptor(key: "friendID", ascending: true)

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]
fetchReq.propertiesToFetch = ["friendID"]

let en = NSEntityDescription.entityForName("Identities", inManagedObjectContext: context)

userList = context.executeFetchRequest(fetchReq, error: nil) as [Model]

println(userList)

}


The runtime error is gone but I still don't know if it works because I'm not sure how to convert the list to a list of strings.

As always, suggestions would be appreciated.

Answer

There are two possibilities: You can issue a normal fetch request and extract an array containing the wanted attribute from the result, using map():

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]

var error : NSError?
if let result = context.executeFetchRequest(fetchReq, error: &error) as? [Model] {
    let friendIDs = map(result) { $0.friendID }
    println(friendIDs)
} else {
    println("fetch failed: \(error!.localizedDescription)")
}

Swift 2:

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]

do {
    let result = try context.executeFetchRequest(fetchReq) as! [Model]
    let friendIDs = result.map { $0.friendID }
    print(friendIDs)
} catch let error as NSError {
    print("fetch failed: \(error.localizedDescription)")
}

Or you set the resultType to .DictionaryResultType and propertiesToFetch to the wanted attribute. In this case the fetch request will return an array of dictionaries:

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]
fetchReq.propertiesToFetch = ["friendID"]
fetchReq.resultType = .DictionaryResultType

var error : NSError?
if let result = context.executeFetchRequest(fetchReq, error: &error) as? [NSDictionary] {
    let friendIDs = map(result) { $0["friendID"] as String }
    println(friendIDs)
} else {
    println("fetch failed: \(error!.localizedDescription)")
}

Swift 2:

let fetchReq = NSFetchRequest(entityName: "Identities")
fetchReq.sortDescriptors = [sortDesc]
fetchReq.propertiesToFetch = ["friendID"]
fetchReq.resultType = .DictionaryResultType

do {
    let result = try context.executeFetchRequest(fetchReq) as! [NSDictionary]
    let friendIDs = result.map { $0["friendID"] as! String }
    print(friendIDs)
} catch let error as NSError {
    print("fetch failed: \(error.localizedDescription)")
}

The second method has the advantage that only the specified properties are fetched from the database, not the entire managed objects.

It has the disadvantage that the result does not include pending unsaved changes in the managed object context (includesPendingChanges: is implicitly set to false when using .DictionaryResultType).