16

I'm new to CoreData, have read a few tutorials but have come up empty-handed. Most tutorials have centered around fetching from CoreData to populate a tableview, which is not what I'm doing at the moment.

Here's how I'm setting the data in CoreData. All types are Double except for date which is an NSDate

let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)

let managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Meditation", inManagedObjectContext: managedObjectContext!)
let meditation = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)

meditation.setValue(settings.minutes, forKey: "length")
meditation.setValue(settings.warmup, forKey: "warmup")
meditation.setValue(settings.cooldown, forKey: "cooldown")
meditation.setValue(NSDate(), forKey: "date")

// fetch stuff from CoreData
var request = NSFetchRequest(entityName: "Meditation")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!

for res in results {
    println(res)
}

Here's what I'm trying to do to get the results, but I'm not able to access things like .minutes, .date, etc. I know I'm also not properly getting the last item either, I was just trying to print out attributes on the object first.

I'd love help on how to fetch only the most recent object as well as show it's attributes

Thanks!

Zack Shapiro
  • 5,202
  • 14
  • 65
  • 121

1 Answers1

23

First, create a "Meditation.swift" file in Xcode with "Editor -> Create NSManagedObject Subclass ...". The generated file should look like

import Foundation
import CoreData

class Meditation: NSManagedObject {

    @NSManaged var length: NSNumber
    @NSManaged var warmup: NSNumber
    @NSManaged var cooldown: NSNumber
    @NSManaged var date: NSDate

}

Now you can use the properties directly instead of Key-Value Coding and create the object as

let meditation = NSEntityDescription.insertNewObjectForEntityForName("Meditation", inManagedObjectContext: managedObjectContext) as Meditation
meditation.length = ...
meditation.warmup = ...
meditation.cooldown = ...
meditation.date = NSDate()

var error : NSError?
if !managedObjectContext.save(&error) {
    println("save failed: \(error?.localizedDescription)")
}

When fetching the object, cast the result of executeFetchRequest() to [Meditation]:

let request = NSFetchRequest(entityName: "Meditation")

var error : NSError?
let result = managedObjectContext.executeFetchRequest(request, error: &error)
if let objects = result as? [Meditation] {
    for meditation in objects {
        println(meditation.length)
        println(meditation.date)
        // ...
    }
} else {
    println("fetch failed: \(error?.localizedDescription)")
}

Finally, to fetch only the latest object, add a sort descriptor to sort the results by date in descending order, and limit the number of results to one:

let request = NSFetchRequest(entityName: "Meditation")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
request.fetchLimit = 1
// ...

Then the objects array contains at most one object, which is the most recent one.

Martin R
  • 488,667
  • 78
  • 1,132
  • 1,248
  • Martin, this is an awesome answer. Thank you. I was missing the subclass which is really helpful. One thing: I'm getting an error on the line `managedObjectContext.save(&error)` related to the `&error` error pointer – Zack Shapiro Dec 17 '14 at 20:26
  • @ZackShapiro: You'll need `var error : NSError?` as in the fetch request, I have added it to the code. – Martin R Dec 17 '14 at 20:28
  • Thanks for the update. After running all of this, I get it to print out the `.length` and `.date` values, then it hits a breakpoint and tells me `CoreData: warning: Unable to load class named 'Meditation' for entity 'Meditation'. Class not found, using default NSManagedObject instead.` – Zack Shapiro Dec 17 '14 at 20:33
  • Stems from the `insertNewObjectForEntityForName` line – Zack Shapiro Dec 17 '14 at 20:34
  • @ZackShapiro: That's a well-known issue, see (for example) http://stackoverflow.com/questions/25076276/unabled-to-find-specific-subclass-of-nsmanagedobject. (Christer's answer is perhaps the easiest solution.) – Martin R Dec 17 '14 at 20:36
  • Ah. Got it. I fixed it by putting @objc(Meditation) above the class declaration in Meditation.swift. Thanks again for your help. This is a phenomenal answer. – Zack Shapiro Dec 17 '14 at 20:39