0

I am storing UIBezierPaths in CoreData. To do this, I have to set the type of the field to Data; I wasn't able to get Transformable to work, and this does.

However, I get an error thrown in the encoding/decoding process when I retrieve or store these paths. The error is

2021-11-02 09:08:39.138674+0700 TestSVG[82461:1408543] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release

The methods that I'm using based on help provided here, are:

 func bezierToData(path: UIBezierPath) -> Data? {
        do {
            return try NSKeyedArchiver.archivedData(withRootObject: path, requiringSecureCoding: false)
        } catch {
            return nil
        }
    }
    
    func dataToBezier(src: Data) -> UIBezierPath? {
        do {
            return try NSKeyedUnarchiver.unarchivedObject(ofClass: UIBezierPath.self, from: src)!
        } catch {
            return nil
        }
    }

This seems to have been something that multiple changes have been imposed on by Apple. My needs are very simple, and what I'm doing works perfectly, but of course I don't want to run into future problems.

This answer : 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release applies to objects that are storable as Transformable, which I could not get working. Again, the advice here was to use Data and that has worked.

I've read all the answers that I can here and elsewhere, and because, basically, StackOverflow usually prioritizes answers accepted immediately after the question was asked, and has no method of updating when the accepted answer becomes obsolete… it can be a burning dumpster of confusion when it comes to getting relevant answers, at least for relative newbies like moi.

But I digress. Can anyone point me to current practice that will allow me to simply serialize and deserialize UIBezierPaths into a CoreData store?

Dan Donaldson
  • 1,061
  • 1
  • 8
  • 21
  • 1
    You might also read the documentation for [NSSecureUnarchiveFromDataTransformer](https://developer.apple.com/documentation/foundation/nssecureunarchivefromdatatransformer) it really seems to be built for what you are trying to do, but requires more setup than `NSKeyedUnarchiver`. – Scott Thompson Nov 02 '21 at 06:32

1 Answers1

0

I've tried this and it coded and decoded back successfully. No warnings about obsolete functions.

This should work for Binary Data

func bezierToData(path: UIBezierPath) -> Data? {
    try? NSKeyedArchiver.archivedData(withRootObject: path, requiringSecureCoding: true)
}

func dataToBezier(src: Data) -> UIBezierPath? {
    try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(src) as? UIBezierPath
}


let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 100, height: 200), cornerRadius: 10)
let pathData = bezierToData(path: path)

let unarchivedPath = dataToBezier(src: pathData!)

Another option is to create Transformable field and NSSecureUnarchiveFromDataTransformer to use in it. Don't forget to set this Transformer (and Custom class to be UIBeizerPath) in CoreData Data Model inspector.

import UIKit

class UIBezierPathToDataTransformer: NSSecureUnarchiveFromDataTransformer {
    override class func allowsReverseTransformation() -> Bool {
        return true
    }
    override class func transformedValueClass() -> AnyClass {
        return UIBezierPath.self
    }
    override class var allowedTopLevelClasses: [AnyClass] {
        return [UIBezierPath.self]
    }
    override func transformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else {
            fatalError("Wrong data type: value must be a Data object; received \(type(of: value))")
        }
        return super.transformedValue(data)
    }
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let path = value as? UIBezierPath else {
            fatalError("Wrong data type: value must be a UIBezierPath object; received \(type(of: value))")
        }
        return super.reverseTransformedValue(path)
    }
}

extension NSValueTransformerName {
    static let uiBezierPathToDataTransformer = NSValueTransformerName(rawValue: "UIBezierPathDataTransformer")
}

Also when add this to where you create persistentContainer (perhaps in AppDelegate):

lazy var persistentContainer: NSPersistentContainer = {
    // Register the transformer at the very beginning.
 ValueTransformer.setValueTransformer(UIBezierPathToDataTransformer(), forName: .uiBezierPathToDataTransformer) // <- HERE

let container = NSPersistentContainer(name: "CoreDataAttributes")
container.loadPersistentStores(completionHandler: { (_, error) in
    guard let error = error as NSError? else { return }
    fatalError("###\(#function): Failed to load persistent stores:\(error)")
})
  
container.viewContext.automaticallyMergesChangesFromParent = true
SampleData.generateSampleDataIfNeeded(context: container.newBackgroundContext())

return container
}()
Paul B
  • 3,989
  • 33
  • 46