问题
After following instructions from this answer: https://stackoverflow.com/a/46917019/6047611
I am running into the compiler error 'self.init' isn't called on all paths before returning from initializer.
super.init()
isn't allowed. However, calling self.init(entity: entity, insertInto: context)
and then initializing all of the properties in the class somehow doesn't fully initialize the class.
I'm lost. I'm pretty new to CoreData and am not comfortable with it yet, so I'm hoping this is an issue of my own ignorance. Any ideas on how to fix this error?
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:
This compiler error is not related to Core Data. It is caused by the two guard
statements which can return
before self.init
is called.
In the statement below, if context
is nil, the else
condition will print "failed context get" and then return
:
guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext
else { print("failed context get"); return }
You are attempting to return before self.init
has been called. This is not allowed. Your convenience initializer must return a properly initialized object.
However, you have a way out in case either guard
statement cannot be satisfied: you can throw
an exception. It then becomes the responsibility of the caller to handle the exception in whatever way makes sense.
To do this, you would need to create an enum
that conforms to the Error
protocol, for example:
enum ProductError: Error {
case contextMissing
case entityCreationFailed
}
You could then rewrite the guard
statements like this:
guard let context = decoder.userInfo[CodingUserInfoKey.context!] as? NSManagedObjectContext
else { print("failed context get"); throw ProductError.contextMissing }
When creating Product
you can do this:
let product = try? Product(from: decoder)
//product is an optional, might be nil
Or this:
if let product = try? Product(from: decoder) {
//product is not an optional, cannot be nil
}
来源:https://stackoverflow.com/questions/54935422/coredata-and-codable-class-compiler-error-self-init-isnt-called-on-all-paths