How to instantiate subclasses of a managed entity in the NSManagedObject Swift extension?

When creating a helper extension helper for NSManagedObject to create a new subclass of the managed object, swift provides a Self type for the mimic instancetype , which is great, but I can't seem to typecast from AnyObject . The following code does not compile with the error "AnyObject" does not convert to "Self"

reference

 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) } } 
+17
swift core-data nsmanagedobject
Nov 24 '14 at 16:17
source share
3 answers

(Updated for Swift 3/4. Solutions for earlier versions of Swift can be found in the editing history.)

You can use unsafeDowncast to cast the return value of NSEntityDescription.insertNewObject() to Self (this is the type for 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, ie 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 correctly defines the type of obj object as YourEntity .

+26
Nov 24 '14 at 19:13
source share

Here is another approach to solving the problem that implements the initializer method (tested with Xcode 7.1):

 extension NSManagedObject { // Returns the unqualified class name, ie the last component. // Can be overridden in a subclass. class func entityName() -> String { return String(self) } convenience init(context: NSManagedObjectContext) { let eName = self.dynamicType.entityName() let entity = NSEntityDescription.entityForName(eName, inManagedObjectContext: context)! self.init(entity: entity, insertIntoManagedObjectContext: context) } } 

The init methods have an implicit return type of Self and no casting techniques are required.

 let obj = YourEntity(context: context) 

creates an object of type YourEntity .




Swift 3/4 update:

 extension NSManagedObject { // Returns the unqualified class name, ie the last component. // Can be overridden in a subclass. class func entityName() -> String { return String(describing: self) } convenience init(context: NSManagedObjectContext) { let eName = type(of: self).entityName() let entity = NSEntityDescription.entity(forEntityName: eName, in: context)! self.init(entity: entity, insertInto: context) } } 
+9
Nov 07 '15 at 2:36
source share

Swift 2 has a very smart solution using protocol and protocol extension

 protocol Fetchable { typealias FetchableType: NSManagedObject static var entityName : String { get } static func createInContext(context: NSManagedObjectContext) -> FetchableType } extension Fetchable where Self : NSManagedObject, FetchableType == Self { static func createInContext(context: NSManagedObjectContext) -> FetchableType { return NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext: context) as! FetchableType } } 

In each NSManagedObject subclass, add the Fetchable protocol and implement the entityName property.

Now the MyEntity.createInContext(…) function will return the correct type without subsequent casting.

+3
Nov 30 '15 at 17:19
source share



All Articles