Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
    }

}
like image 370
amleszk Avatar asked Nov 24 '14 16:11

amleszk


People also ask

How do I create a subclass in NSManagedObject?

From the Xcode menu bar, choose Editor > Create NSManagedObject Subclass. Select your data model, then the appropriate entity, and choose where to save the files. Xcode places both a class and a properties file into your project.

Can we have multiple managed object contexts in Core Data?

Most apps need just a single managed object context. The default configuration in most Core Data apps is a single managed object context associated with the main queue. Multiple managed object contexts make your apps harder to debug; it's not something you'd use in every app, in every situation.

What is the purpose of managed object context NSManagedObjectContext?

A managed object context is an instance of NSManagedObjectContext . Its primary responsibility is to manage a collection of managed objects. These managed objects represent an internally consistent view of one or more persistent stores.

What is @NSManaged?

@NSManaged indicates that the variables will get some values when we run the app. Coredata automatically creates getter and setter for such props. It silences the compiler for warnings. NSmanaged is subclass of NSObject. @NSManaged means extra code will be given to these props at runtime.


2 Answers

(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.

like image 177
Martin R Avatar answered Oct 02 '22 08:10

Martin R


In Swift 2 there is a very smart solution using a protocol and a 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 protocol Fetchable and implement the property entityName.

Now the function MyEntity.createInContext(…) will return the proper type without further type casting.

like image 26
vadian Avatar answered Oct 02 '22 08:10

vadian