Bing
Bing

Reputation: 181

ManagedObjectModel subclass doesn't work in Swift

To summarize what I've done:

  1. Created a project named 2048.

  2. Created a subclass of NSManagedObject

    class BestScore: NSManagedObject {
        @NSManaged var bestScoreModel: BestScoreModel
        func update(score: Int) {
            self.bestScoreModel!.score = score
        }
    }
    
  3. Created a subclass of NSManagedObjectModel

    class BestScoreModel: NSManagedObjectModel {
        @NSManaged var score: Int
    }
    
  4. Created a Data Model under Core Data tab. Named the file as 2048.xcdatamodeld. Added an entity BestScoreModel with one attribute "score" and defined the type as Integer 16. Besides, I also updated the class of the entity as 2048.BestScoreModel according to the official document.

  5. In the controller class, I added following variables

    class HomeViewController: UIViewController {
    
        var bestScore: BestScore?
    
        @lazy var context: NSManagedObjectContext = {
            let serviceName = NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleName") as String
            let modelURL = NSBundle.mainBundle().URLForResource(serviceName, withExtension: "momd")
            let model = NSManagedObjectModel(contentsOfURL: modelURL)
            if model == nil {
                println("Error initilizing model from : \(modelURL)")
                abort()
            }
            let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
            let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
            let storeURL = (urls[urls.endIndex-1]).URLByAppendingPathComponent("\(serviceName).sqlite")
            var error: NSError? = nil
            var store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error)
            if store == nil {
                println("Failed to load store at \(storeURL) with error: \(error?.localizedDescription)")
                abort()
            }
            var context = NSManagedObjectContext()
            context.persistentStoreCoordinator = coordinator
            return context
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let entity: NSEntityDescription = NSEntityDescription.entityForName("BestScoreModel", inManagedObjectContext: context)
            bestScore = BestScore(entity: entity, insertIntoManagedObjectContext: context)
            bestScore.update(0)
        }
    }
    

The application was built successfully but when I ran the simulator, it threw exceptions with below

    2014-07-13 00:56:59.944 2048[76600:4447122] -[NSManagedObject update:]: unrecognized selector sent to instance 0x10bc55d20
    2014-07-13 00:56:59.948 2048[76600:4447122] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject update:]: unrecognized selector sent to instance 0x10bc55d20'

I'm completely new to iOS developing and have no experience before. I wanna take Swift as an opportunity for start writing apps without learning objective-c. Please let me know if I misconfigured anything.

Appreciate for your help.

PS: I get the implementation of the managed object context from here. Big thanks to the author!

Upvotes: 2

Views: 3110

Answers (2)

Mikhail Grebionkin
Mikhail Grebionkin

Reputation: 3836

Try to specify module name for entity in your .xcdatamodeld file. See Implementing Core Data Managed Object Subclasses section in "Using Swift with Cocoa and Objective-C (Swift 2.1)" book for detailed description were you can find it.

Upvotes: 0

Martin R
Martin R

Reputation: 539745

The NSManagedObjectModel describes the collection of all entities used in your application, and there is usually no reason to subclass it.

I would suggest that you use the same name for both the entity and the corresponding managed object subclass. (This is also what Xcode does when you create an Objective-C managed object subclass)

The Swift managed object subclass (which Xcode cannot create automatically yet) would simply look like this:

class BestScore: NSManagedObject {
    @NSManaged var score: NSNumber
}

I have used the NSNumber type instead of Int16, because scalar accessor methods in Swift managed object subclasses do not yet work correctly, compare How to use Core Data Integer 64 with Swift Int64?.

A new BestScore object would then be created with

bestScore = NSEntityDescription.insertNewObjectForEntityForName("BestScore", inManagedObjectContext: context) as BestScore

and you can access its properties directly, without the need for an update() function:

bestScore!.score = 0

Additional Remarks: It would make sense to define the bestScore property as an implicitly unwrapped optional, because you expect it to have a value after viewDidLoad:. That saves you many explicit unwrappings later:

var bestScore: BestScore!

Also, if the application is run a second time, you probably want to reload the existing best score, instead of creating a new object. That means that you have to execute a fetch request first, and insert a new object only if none was found:

let request = NSFetchRequest(entityName: "BestScore")
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)

if !result || result.count == 0 {
    // Error or no object found: create new one:
    bestScore = NSEntityDescription.insertNewObjectForEntityForName("BestScore", inManagedObjectContext: context) as BestScore
    bestScore.score = 0
} else {
    // Use existing object:
    bestScore = result[0] as BestScore
}
let score = bestScore.score.integerValue // NSNumber --> Integer

Upvotes: 2

Related Questions