«Не удалось вызвать назначенный инициализатор в классе NSManagedObject» NSManagedObject и NSCoding

Я пытаюсь:

  1. Загрузите массив пользовательских объектов из файла JSON.
  2. Сохраните его в CoreData.
  3. Получите его с помощью 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)
}
}

0 ответов

Другие вопросы по тегам