Search code examples
iosswiftcore-datansmanagedobject

NSManagedObject subclass with failable init in swift


I have a NSManagedObject subclass, which needs to be fulfilled before to return correctly (it has all of their fields as non-optionals), so when I do the init, first of all I use a guard to be sure of the data (from JSON) is correct as follow:

public init(context: NSManagedObjectContext, dictionary: [String: AnyObject]) throws {
    guard
    let modified_on = date(dictionary["modified_on"] as? String),
    let start_on = date(dictionary["start_on"] as? String),
    let end_on = date(dictionary["end_on"] as? String),
    let ticket_name = dictionary["ticket_name"] as? String
    else {
        throw ErrorParseModel.ErrorParsing
    }
...
...

The problem is that compiler returns me an error Super.init isn't called before returning from initializer because it's possible that this init method throws an exception without to call super.init before.

My dude is about how to proceed: 1. Should I call super.init before and create a "dummy object" before than guard? On this case, I should to remove this object from context before to throw exception? 2. Another test that I could do before of this guard is to be sure that entityName is correct as follows:

guard let entity = NSEntityDescription.entityForName(Ticket.entityName, inManagedObjectContext: context) else {
   return
}

On this case, I can't to create the object in the context because I don't have entity, so it's impossible (I think) to create a nil-entity object in one context.

Any suggestion about how can I proceed?

Thanks!


Solution

  • The "Super.init isn't called before returning from initializer" problem can be solved by implementing a convenience initializer instead of a dedicated initializer:

    public convenience init(context: NSManagedObjectContext, dictionary: [String: AnyObject]) throws {
        guard let entity = NSEntityDescription.entityForName(Ticket.entityName, inManagedObjectContext: context) else {
            throw ErrorParseModel.EntityNotFound
        }
        guard
            let modified_on = date(dictionary["modified_on"] as? String)
            // ...
        else {
            throw ErrorParseModel.ErrorParsing
        }
        self.init(entity: entity, insertIntoManagedObjectContext: context)
        self.modified_on = modified_on
    }
    

    However, in my opinion it does not make much sense to throw an error if the Core Data entity is not found. That would be a programming error and there is no sensible way to catch and resolve that problem at runtime. I would treat that as a fatal error, so that it is detected early while testing the app:

        guard let entity = NSEntityDescription.entityForName(Ticket.entityName, inManagedObjectContext: context) else {
            fatalError("Entity not found")
        }