«Не удалось вызвать назначенный инициализатор в классе NSManagedObject» NSManagedObject и NSCoding
Я пытаюсь:
- Загрузите массив пользовательских объектов из файла JSON.
- Сохраните его в CoreData.
- Получите его с помощью NSFetchRequest.
Примечание. Каждый элемент в массиве пользовательских объектов с именем «Куб» также содержит вложенный массив пользовательских объектов с именем «Алгоритм», которые также соответствуют NSCoding (показанному во фрагменте кода ниже).
Шаги 1 + 2, похоже, работают нормально. Ошибка возникает при извлечении объектов верхнего уровня с именем «Куб», особенно сразу после вызова self.init() внутри вложенного класса «Алгоритм».
Класс алгоритма:
@objc(Algorithm)
public class Algorithm: NSManagedObject, Decodable, Encodable, NSSecureCoding {
enum CodingKeys: String, CodingKey {
case imageNumber, alg, alternativeAlgs, type, tags, isFavorite, optimalMoves, name }
public static var supportsSecureCoding = true
public func encode(with coder: NSCoder) {
coder.encode(imageNumber, forKey: CodingKeys.imageNumber.rawValue)
coder.encode(optimalMoves, forKey: CodingKeys.optimalMoves.rawValue)
coder.encode(alg, forKey: CodingKeys.alg.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
coder.encode(type, forKey: CodingKeys.type.rawValue)
coder.encode(isFavorite, forKey: CodingKeys.isFavorite.rawValue)
coder.encode(alternativeAlgs, forKey: CodingKeys.alternativeAlgs.rawValue)
coder.encode(tags, forKey: CodingKeys.tags.rawValue) }
required convenience public init?(coder: NSCoder) {
self.init()
imageNumber = coder.decodeObject(forKey: CodingKeys.imageNumber.rawValue) as? String ?? ""
optimalMoves = coder.decodeObject(forKey: CodingKeys.optimalMoves.rawValue) as? String ?? ""
alg = coder.decodeObject(forKey: CodingKeys.alg.rawValue) as? String ?? ""
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String ?? ""
type = coder.decodeObject(forKey: CodingKeys.type.rawValue) as? String ?? ""
isFavorite = coder.decodeBool(forKey: CodingKeys.isFavorite.rawValue)
alternativeAlgs = coder.decodeObject(forKey: CodingKeys.alternativeAlgs.rawValue) as? [String] ?? []
tags = coder.decodeObject(forKey: CodingKeys.tags.rawValue) as? [String] ?? [] }
required convenience public init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else {
throw DecoderConfigurationError.missingManagedObjectContext
}
self.init(context: context)
let container = try decoder.container(keyedBy: CodingKeys.self)
self.imageNumber = try container.decode(String.self, forKey: .imageNumber)
self.optimalMoves = try container.decode(String.self, forKey: .optimalMoves)
self.alg = try container.decode(String.self, forKey: .alg)
self.name = try container.decode(String.self, forKey: .name)
self.type = try container.decode(String.self, forKey: .type)
self.isFavorite = try container.decode(Bool.self, forKey: .isFavorite)
self.alternativeAlgs = try container.decode([String].self, forKey: .alternativeAlgs) as [String]
self.tags = try container.decode([String].self, forKey: .tags) as [String] }
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(imageNumber, forKey: .imageNumber)
try container.encode(optimalMoves, forKey: .optimalMoves)
try container.encode(alg, forKey: .alg)
try container.encode(name, forKey: .name)
try container.encode(type, forKey: .type)
try container.encode(isFavorite, forKey: .isFavorite)
try container.encode(alternativeAlgs, forKey: .alternativeAlgs)
try container.encode(tags, forKey: .tags) }
}
extension Algorithm {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Algorithm> {
return NSFetchRequest<Algorithm>(entityName: "Algorithm")
}
@NSManaged public var alg: String
@NSManaged public var alternativeAlgs: [String]
@NSManaged public var imageNumber: String
@NSManaged public var isFavorite: Bool
@NSManaged public var name: String
@NSManaged public var optimalMoves: String
@NSManaged public var tags: [String]
@NSManaged public var type: String
@NSManaged public var cube: Cube?
}
extension Algorithm : Identifiable {
}
Прочитав комментарии людей, я понял, что должен заменить self.init() на назначенный NSManagedObject init, но я не нашел правильного способа сделать это. Приложение аварийно завершает работу после замены self.init() следующим кодом:
let context = CoreDataManager.shared.container.viewContext
self.init(context: context)
// self.init()
FWI — в инспекторе файлов CoreData.xcdatamodeled массив алгоритмов определяется как Transformable с помощью пользовательского преобразователя — AlgorithmDataTransformer:
@objc(AlgorithmDataTransformer)
public final class AlgorithmDataTransformer: ValueTransformer {
override public func transformedValue(_ value: Any?) -> Any? {
guard let array = value as? [Algorithm] else { return nil }
do {
return try NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true)
} catch {
assertionFailure("Failed to transform `Algorithm` to `Data`")
return nil
}
}
override public func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
do {
return try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: Algorithm.self, from: data as Data)
} catch {
assertionFailure("Failed to transform `Data` to `Algorithm`")
return nil
}
}
}
extension AlgorithmDataTransformer {
/// The name of the transformer. This is the name used to register the transformer using `ValueTransformer.setValueTrandformer(_"forName:)`.
static let name = NSValueTransformerName(rawValue: String(describing: AlgorithmDataTransformer.self))
/// Registers the value transformer with `ValueTransformer`.
public static func register() {
let transformer = AlgorithmDataTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}