4

I am using Core Data for persistent storage and I am getting the error listed below. I have looked up the message and I know it has something to do with the fact that I am using the transformable and a custom class. Despite my research I am not sure how to fix it. My attempt at conforming to the NSSecureCoding protocol failed miserably. I am posting my original code because I think it might be easier to try and solve the issue from scratch rather than trying to fix my poor attempt at NSSecureCoding. Thank you in advance! Any help is much appreciated.

'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release

My Entity: enter image description here

My Custom Class:

public class SelectedImages: NSObject, NSCoding {
    public var images: [SelectedImage] = []
    enum Key: String {
        case images = "images"
    }
    init(images: [SelectedImage]) {
        self.images = images
    }
    public func encode(with aCoder: NSCoder) {
        aCoder.encode(images, forKey: Key.images.rawValue)
    }
    public required convenience init?(coder aDecoder: NSCoder) {
        let mImages = aDecoder.decodeObject(forKey: Key.images.rawValue) as! [SelectedImage]
        self.init(images: mImages)
    }
}

public class SelectedImage: NSObject, NSCoding {
    public var location: Int = 0
    public var duration: Int = 10
    public var localIdentifier: String = ""
    
    enum Key: String {
        case location = "location"
        case duration = "duration"
        case localIdentifier = "localIdentifier"
    }
    init(location: Int, duration: Int, localIdentifier: String) {
        self.location = location
        self.duration = duration
        self.localIdentifier = localIdentifier
    }
    public override init() {
        super.init()
    }
    public func encode(with aCoder: NSCoder) {
        aCoder.encode(location, forKey: Key.location.rawValue)
        aCoder.encode(duration, forKey: Key.duration.rawValue)
        aCoder.encode(localIdentifier, forKey: Key.localIdentifier.rawValue)
    }
    public required convenience init?(coder aDecoder: NSCoder) {
        let mlocation = aDecoder.decodeInt32(forKey: Key.location.rawValue)
        let mduration = aDecoder.decodeInt32(forKey: Key.duration.rawValue)
        let mlocalIdentifier = aDecoder.decodeObject(forKey: Key.localIdentifier.rawValue) as! String
        self.init(location: Int(mlocation), duration:Int(mduration), localIdentifier:String(mlocalIdentifier))
    }
}

View Controller:

guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let userEntity = NSEntityDescription.entity(forEntityName: "EntityTest", in: managedContext)!
let selectedImages = NSManagedObject(entity: userEntity, insertInto: managedContext) as! EntityTest
let mImages = SelectedImages(images: selectionArrays)
selectedImages.setValue(mImages, forKeyPath: "image")
        
do {
    try managedContext.save()
    print("Images saved to core data")
} catch let error as NSError {
    print("Could not save. \(error), \(error.userInfo)")
}
    let newViewController = PickerTest6()
    self.navigationController?.pushViewController(newViewController, animated: true)
Part_Time_Nerd
  • 994
  • 7
  • 26
  • 56
  • https://www.kairadiagne.com/2020/01/13/nssecurecoding-and-transformable-properties-in-core-data.html – TonyMkenu Nov 20 '20 at 07:35
  • 1
    I read that post and it was what I based my original attempt on as well as some info from here: https://developer.apple.com/forums/thread/107533 but everything I tried has failed. To a Swift newb like myself that blog post is not very clear. The article has several grammar/format errors that create an opaque explanation. Again I am new to Swift so that may just be me. That is why I was hoping someone could show me a possible solution with what I currently have. – Part_Time_Nerd Nov 20 '20 at 14:25
  • 1
    HAve you found a solution? – crost Feb 22 '21 at 21:37

1 Answers1

3

I haven't run this code, but it should work.

  1. Make changes to the SelectedImage class.
public class SelectedImage: NSObject, NSSecureCoding { // Class must inherit from NSSecureCoding
    public static var supportsSecureCoding: Bool = true // It's the required property
    
    public var location: Int = 0
    public var duration: Int = 10
    public var localIdentifier: String = ""
    
    private enum CodingKeys: String {
        case location, duration, localIdentifier
    }
    
    public override init() {
        super.init()
    }
    
    init(location: Int, duration: Int, localIdentifier: String) {
        self.location = location
        self.duration = duration
        self.localIdentifier = localIdentifier
    }
    
    public required init?(coder: NSCoder) {
        self.location = coder.decodeInteger(forKey: CodingKeys.location.rawValue)
        self.duration = coder.decodeInteger(forKey: CodingKeys.duration.rawValue)
        
        // Now instead of decodeObject(forKey:) you should use decodeObject(of: forKey:).
        self.localIdentifier = coder.decodeObject(of: NSString.self, forKey: CodingKeys.localIdentifier.rawValue) as String? ?? ""
    }
    
    public func encode(with coder: NSCoder) {
        coder.encode(location, forKey: CodingKeys.location.rawValue)
        coder.encode(duration, forKey: CodingKeys.duration.rawValue)
        coder.encode(localIdentifier, forKey: CodingKeys.localIdentifier.rawValue)
    }
}
  1. Create the SelectedImageTransformer class.
@objc(SelectedImageTransformer)
final class SelectedImageTransformer: NSSecureUnarchiveFromDataTransformer {
    static let name = NSValueTransformerName(rawValue: String(describing: SelectedImageTransformer.self))
    
    override class var allowedTopLevelClasses: [AnyClass] {
        return super.allowedTopLevelClasses + [SelectedImage.self]
    }

    public class func register() {
        let transformer = SelectedImageTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }
}
  1. Edit the CoreData model as follows.

enter image description here

  1. Call the register method in AppDelegate (if you use the UIKit) before initializing the persistent container.
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
    // Register the transformer
    SelectedImageTransformer.register()
    
    let container = NSPersistentContainer(name: "AppName")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

For more details, you can read this or this article.

Iaenhaall
  • 404
  • 5
  • 9