thatGuyNeil
thatGuyNeil

Reputation: 55

'Failed to call designated initializer on NSManagedObject class' NSManagedObject and NSCoding

I'm attempting to:

  1. Load an array of custom objects from JSON file.
  2. Save it in CoreData.
  3. Fetch it using NSFetchRequest.

Note: Each element in the array of custom objects named 'Cube' also contains a nested array of custom objects named 'Algorithm', which also conform to NSCoding (shown in code snippet below).

Steps 1 + 2 seem to work fine. The error occurs when fetching the top level Entities named 'Cube', specifically right after the self.init() is called inside the nested class 'Algorithm'.

Algorithm class:

@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 {

}

After reading people's comments I am aware I should replace the self.init() with NSManagedObject's designated init, but I haven't found the right way to do so. The app crashes after replacing the self.init() with the following code:

let context = CoreDataManager.shared.container.viewContext
self.init(context: context)
//        self.init()

FWI - in the CoreData.xcdatamodeled file inspector the algorithms array is defined as Transformable with a custom transformer - 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)
}
}

Upvotes: 1

Views: 104

Answers (0)

Related Questions