Ошибка компилятора классов CoreData и Codable: «self.init» не вызывается на всех путях до возврата из инициализатора - PullRequest
0 голосов
/ 01 марта 2019

После следующих инструкций из этого ответа: https://stackoverflow.com/a/46917019/6047611

Я сталкиваюсь с ошибкой компилятора 'self.init' isn't called on all paths before returning from initializer. super.init() не допускается.Однако вызов self.init(entity: entity, insertInto: context) и затем инициализация всех свойств в классе каким-то образом не полностью инициализирует класс.

Я потерян.Я довольно новичок в CoreData и мне пока не очень комфортно, поэтому я надеюсь, что это проблема моего собственного невежества.Любые идеи о том, как исправить эту ошибку?

import Foundation
import CoreData

class Product: NSManagedObject, Encodable, Decodable
{
    @NSManaged var mongoID:[String:String]
    @NSManaged var title:String
    @NSManaged var productID:Int
    @NSManaged var mpn:String
    @NSManaged var listPrice:Float
    @NSManaged var price:Float
    @NSManaged var uom:String
    @NSManaged var uomQty:Int
    @NSManaged var inventory:Float
    @NSManaged var minSaleQty:Int
    @NSManaged var desc:String

    @NSManaged var categories:[String]
    @NSManaged var imageURL:String
    @NSManaged var upc:String
    @NSManaged var quantity:Int
    @NSManaged var disc:Bool

    enum CodingKeys: String, CodingKey {
        case mongoID = "mongoID"
        case title = "title"
        case productID = "productID"
        case mpn = "mpn"
        case listPrice = "listPrice"
        case price = "price"
        case uom = "uom"
        case uomQty = "uomQty"
        case inventory = "inventory"
        case minSaleQty = "minSaleQty"
        case desc = "desc"
        case categories = "categories"
        case imageURL = "imageURL"
        case upc = "upc"
        case quantity = "quantity"
        case disc = "disc"
    }

    required convenience init(from decoder:Decoder) throws
    {
        guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext else { print("failed context get"); return }
        guard let entity = NSEntityDescription.entity(forEntityName: "Product", in: context) else { print("failed entity init"); return }


        self.init(entity: entity, insertInto: context)

        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.mongoID = try container.decodeIfPresent([String:String].self, forKey: .mongoID) ?? ["$id":"nil"]
        self.title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
        self.productID = try container.decodeIfPresent(Int.self, forKey: .productID) ?? 0
        self.mpn = try container.decodeIfPresent(String.self, forKey: .mpn) ?? ""
        self.listPrice = try container.decodeIfPresent(Float.self, forKey: .listPrice) ?? 0.0
        self.price = try container.decodeIfPresent(Float.self, forKey: .price) ?? 0.0
        self.uom = try container.decodeIfPresent(String.self, forKey: .uom) ?? ""
        self.uomQty = try container.decodeIfPresent(Int.self, forKey: .uomQty) ?? 0
        self.inventory = try container.decodeIfPresent(Float.self, forKey: .inventory) ?? 0.0
        self.minSaleQty = try container.decodeIfPresent(Int.self, forKey: .minSaleQty) ?? 0
        self.desc = try container.decodeIfPresent(String.self, forKey: .desc) ?? ""

        self.categories = try container.decodeIfPresent([String].self, forKey: .categories) ?? [""]
        self.imageURL = try container.decodeIfPresent(String.self, forKey: .imageURL) ?? ""
        self.upc = try container.decodeIfPresent(String.self, forKey: .upc) ?? ""
        self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) ?? 0
        self.disc = try container.decodeIfPresent(Bool.self, forKey: .disc) ?? false
    }//'self.init' isn't called on all paths before returning from initializer

    public func encode(to encoder: Encoder) throws
    {

    }
}


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

1 Ответ

0 голосов
/ 01 марта 2019

Эта ошибка компилятора не связана с Core Data.Это вызвано двумя guard операторами, которые могут return до вызова self.init.

В приведенном ниже утверждении, если context равно nil, условие else выведет «не удалось получить контекст», а затем return:

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext 
else { print("failed context get"); return }

Вы пытаетесь вернутьсядо того, как self.init был вызван.Это не разрешеноВаш удобный инициализатор должен возвращать правильно инициализированный объект.

Однако у вас есть выход на тот случай, если вы не сможете выполнить ни одно из утверждений guard: вы можете throw исключение.В этом случае вызывающая сторона несет ответственность за обработку исключения любым разумным способом.

Для этого вам необходимо создать enum, соответствующий протоколу Error, например:

enum ProductError: Error {
    case contextMissing
    case entityCreationFailed
}

Затем вы можете переписать guard операторы следующим образом:

guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext 
else { print("failed context get"); throw ProductError.contextMissing }

При создании Product вы можете сделать это:

let product = try? Product(from: decoder)  
//product is an optional, might be nil

Или это:

if let product = try? Product(from: decoder) {
    //product is not an optional, cannot be nil
}
...