0

In objc I have created extension of NSManagedObject for creating objects. Now all subclasses can use it to create objects of its own type. I have tried something similar in swift, but quite as I would like, by using generics.

extension NSManagedObject {

    // MARK: - Creation Methods

    class func create<T: NSManagedObject>(in context: NSManagedObjectContext) -> T {
        let entity = NSEntityDescription.entity(forEntityName: T.description(), in: context)!
        return T(entity: entity, insertInto: context)
    }
}

While in Person class which is subclass of NSManagedObject, I can call it like:

let person = Person.create(context)

person will be NSManagedObject type, and I will have to cast it to Person, to access properties like name, phone etc...

I would like to somehow avoid this and make method create return instancetype of sorts, but I am unsure how to do this?

MegaManX
  • 8,766
  • 12
  • 51
  • 83

1 Answers1

1

Rather than an extension of NSManagedObject I'd prefer a protocol extension

protocol Managed
{
    associatedtype ManagedType: NSManagedObject = Self
    static var entityName : String { get }
    static func create(in context: NSManagedObjectContext) -> ManagedType
}

extension Managed where Self : NSManagedObject
{
    static var entityName : String {
        return NSStringFromClass(self).components(separatedBy: ".").last!
    }

    static func create(in context: NSManagedObjectContext) -> ManagedType
    {
        return NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as! ManagedType
    }
}

Make all NSManagedObject subclasses adopt the protocol Managed

vadian
  • 274,689
  • 30
  • 353
  • 361
  • If I call let person = Person.create(in: context), and then person.name, I get error - Value of type 'NSManagedObject' has no member 'name' – MegaManX Aug 01 '18 at 13:39
  • I updated the answer suggesting a different (and working) approach. – vadian Aug 01 '18 at 13:48
  • Isn't that essentially https://stackoverflow.com/a/34004264/1187415 (updated for Swift 3/4)? – Martin R Aug 01 '18 at 14:32
  • @MartinR I checked your answer, but that unsafeDowncast, doesn't look like cleanest solution. I get what are you trying. Solution with Managed protocol seems better IMO. To be honest I am kinda disapointed, that this couldn't be solved more cleanly with generics – MegaManX Aug 01 '18 at 15:09
  • @MegaManX: This protocol-based solution has a forced cast `as! ManagedType` which is as good or as bad as a `unsafeDowncast`. Note that there is another answer https://stackoverflow.com/a/33583941/1187415 to the duplicate question, using a custom initializer without casting "tricks". – Martin R Aug 01 '18 at 16:20