'self.init' isn't called on all paths before returning from initializer
Asked Answered
U

2

5

I'm working with CoreData and JSON parsing with Decodable following this and encountered an error at the end of the initializer saying 'self.init' isn't called on all paths before returning from initializer:

import Foundation
import CoreData

extension CodingUserInfoKey {
    static let managedObjectContext = CodingUserInfoKey(rawValue: "managedObjectContext")!
}

@objc(Task)
public class Task: NSManagedObject, Decodable {
    
    enum CodingKeys: String, CodingKey {
        case diff, title, desc, doc
    }
    
    required convenience public init(from decoder: Decoder) throws {
        guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else {
            print("Decode context error")
            return
        }
        guard let entity = NSEntityDescription.entity(forEntityName: "Task", in: context) else {
            print("Decode entity error")
            return
        }
        self.init(entity: entity, insertInto: context)
        do {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.diff = try container.decode(String.self, forKey: .diff)
            self.title = try container.decode(String.self, forKey: .title)
            self.desc = try container.decode(String.self, forKey: .desc)
            self.doc = try container.decode(String.self, forKey: .doc)
        } catch {
            print("Decode key error")
        }
    }
    
}

Am I missing something?

Unreflective answered 28/8, 2020 at 15:7 Comment(1)
If either of your guards fail, you'll return from the method without calling self.init. Those are the "paths" its referring toSilassilastic
M
5

You should probably throw a custom error in guard statements instead of just returning. Also you should remove do-catch from decoder function calls:

enum ManagedObjectError: Error {
    case decodeContextError
    case decodeEntityError
}

required convenience public init(from decoder: Decoder) throws {
    guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else {
        throw ManagedObjectError.decodeContextError
    }
    guard let entity = NSEntityDescription.entity(forEntityName: "Task", in: context) else {
        throw ManagedObjectError.decodeEntityError
    }
    self.init(entity: entity, insertInto: context)
    
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.diff = try container.decode(String.self, forKey: .diff)
    self.title = try container.decode(String.self, forKey: .title)
    self.desc = try container.decode(String.self, forKey: .desc)
    self.doc = try container.decode(String.self, forKey: .doc)
}
Melatonin answered 28/8, 2020 at 15:18 Comment(0)
A
3

As it notes, you haven't called self.init on all paths. For example, if context is nil, then you return without calling self.init.

If you want to exit this initializer without creating an instance, you'll need to throw an error, not just return.

Since this init throws, it also doesn't make sense to catch errors and then discard them. Just let them throw to the caller.

Aldarcy answered 28/8, 2020 at 15:17 Comment(2)
you were about half a minute faster than me :)Melatonin
@Melatonin You gave actual code rather than just describing it. That's the more useful answer.Aldarcy

© 2022 - 2025 — McMap. All rights reserved.