Search code examples
swiftcore-datansmanagedobject

How can I create instances of managed object subclasses in a NSManagedObject Swift extension?


When creating an extension helper to NSManagedObject to create a new managed object subclass, swift provides the Self type to mimic instancetype which is great, but i can't seem to typecast from AnyObject. The below code does not compile with error 'AnyObject' is not convertible to 'Self'

Help?

extension NSManagedObject
{
    class func createInContext(context:NSManagedObjectContext) -> Self {
        var classname = className()
        var object: AnyObject = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: context)
        return object
    }


    class func className() -> String {
        let classString = NSStringFromClass(self)
        //Remove Swift module name
        let range = classString.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch, range: Range<String.Index>(start:classString.startIndex, end: classString.endIndex), locale: nil)
        return classString.substringFromIndex(range!.endIndex)
    }

}

Solution

  • (Updated for Swift 3/4 now. Solutions for earlier Swift versions can be found in the edit history.)

    You can use unsafeDowncast to cast the return value of NSEntityDescription.insertNewObject() to Self (which is the type on which the method is actually called):

    extension NSManagedObject {
        class func create(in context: NSManagedObjectContext) -> Self {
            let classname = entityName()
            let object = NSEntityDescription.insertNewObject(forEntityName: classname, into: context)
            return unsafeDowncast(object, to: self)
        }
    
        // Returns the unqualified class name, i.e. the last component.
        // Can be overridden in a subclass.
        class func entityName() -> String {
            return String(describing: self)
        }
    }
    

    Then

    let obj = YourEntity.createInContext(context)
    

    works and the compiler infers the type of obj correctly as YourEntity.