user1107173
user1107173

Reputation: 10744

Core Data & Xcode 11: Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer

Just moved to Xcode 11 and getting the following crash at launch:

CoreData: fault: One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName. Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer instead. At some point, Core Data will default to using "NSSecureUnarchiveFromData" when nil is specified, and transformable properties containing classes that do not support NSSecureCoding will become unreadable.

CoreData: warning: Property 'color' on Entity 'Group' is using nil or an insecure NSValueTransformer. Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformer instead.

I'm creating an NSPersistentContainer at launch using the code below:

private let container: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())
    let storeDescription = NSPersistentStoreDescription(url: getStoreURLWithUserName())
    storeDescription.shouldMigrateStoreAutomatically = true
    storeDescription.shouldInferMappingModelAutomatically = true
    container.persistentStoreDescriptions = [storeDescription]
    return container
}()

Error occurs right after this line is executed:

let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())

I also have a property called 'Colorin aGroup` entity that's transformable:

@NSManaged public var color: UIColor?
@NSManaged public var hexColorValue: String?

Below is how set the property:

public var hexColor: String? {
    get {
        return self.hexColorValue
    }
    set {
        self.hexColorValue = newValue
        if let str = newValue {
            self.color = UIColor(hex: str)
        }
    }
}

This is what the property looks like in Core Data:

enter image description here

I am not sure how to recover from this crash. This was working fine with Xcode 10

Upvotes: 36

Views: 21785

Answers (7)

elemento
elemento

Reputation: 342

BEWARE !!!

I can confirm that both:

  • NSSecureUnarchiveFromData
  • NSSecureUnarchiveFromDataTransformer

Break iCloud Sync ⚠️🚫⚠️. Do not use them.

Leave the Value Transformer Name Field empty (inspector) and keep the warning.

Upvotes: 0

SouthernYankee65
SouthernYankee65

Reputation: 1269

Swift 5.4.2

This worked for me.

EDIT Link to the article is here.

  • Click on the .xcdatamodeld file in the project navigator
  • Click on the Entity that has a Transformable Attribute
  • Click on the Transformable Attribute
  • Click the 'Show Data Model Inspector' icon
  • Enter 'NSSecureUnarchiveFromDataTransformer' in the Transformer field - EDIT Make this 'NSSecureUnarchiveFromData' as of Swift 5.5.2.

editor_orientation_preview

Your warnings/errors should go away. If not, try cleaning your build folder and rebuild.

Upvotes: 42

napIT-Apps
napIT-Apps

Reputation: 153

For Objective-C and iOS 14, the following solution works for UIColor attributes.

  • First add a new subclass of NSSecureUnarchiveFromDataTransformer

    @interface ColorValueTransformer : NSSecureUnarchiveFromDataTransformer

  • Add the following static method to your implementation file:

    @implementation ColorValueTransformer
    
    + (NSArray<Class> *)allowedTopLevelClasses {
              return @[UIColor.class]; 
    }
    
    @end
    
  • Open your data model (e.g. datamodel..xcdatamodeld)

  • Select the entity and the related attribute which needs the new Transformer

  • Open the Data Model Inspector

  • Add the class name (e.g. ColorValueTransformer) as Transformer to that attribute

  • Change the Custom Class to UIColor

  • Build and run…

Upvotes: 3

John B
John B

Reputation: 159

This is related to a migration from NSCoding to the NSSecureCoding protocol. The default ValueTransformer adopts NSCoding, so the only solution that worked for me was to write my own Transformer that adopts the NSSecureUnarchiveFromDataTransformer protocol.

I should say that my own experience is from trying to define an Attribute with the Transformer type to persist a custom class that adopted NSCoding. I was initially met with a warning similar to the OP's error. I was able to suppress the warning by changing the Transformer field on the attribute to "NSSecureUnarchiveFromData" as others have mentioned, but I then received an error along the lines of: Not able to save to CoreData. SQLCore dispatchRequest Object of class “ ” not among allowed top level class list... as mentioned here. The suggestion to change the Attribute to a Relationship was undesirable in my case.

More digging came up with this blog post that details the "reason" for all of this, and gives a solution that worked for me. The blog actually uses the case of UIColor in the example, but it works for any custom class as well.

Say you have a CustomClass that you want to store as a Transformable Attribute in some Entity. If you're like me, you may have adopted NSCoding and received the aforementioned error. The solution would be to adopt NSSecureCoding instead and define an NSSecureUnarchiveFromDataTransformer subclass:

@objc(CustomClassValueTransformer)
final class CustomClassValueTransformer: NSSecureUnarchiveFromDataTransformer {

    static let name = NSValueTransformerName(rawValue: String(describing: CustomClass.self))

    // Make sure `CustomClass` is in the allowed class list,
    // AND any other classes that are encoded in `CustomClass`
    override static var allowedTopLevelClasses: [AnyClass] {
        // for example... yours may look different
        return [CustomClass.self, OtherClass.self, NSArray.self, NSValue.self]
    }

    /// Registers the transformer.
    public static func register() {
        let transformer = CustomClassValueTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }
}

Then make sure to set the Transformer field on your Attribute to "CustomClassValueTransformer" and the Custom Class field to "CustomClass" and you should be good to go.

Upvotes: 13

Dale
Dale

Reputation: 3323

I received the same warning messages when updating to Xcode 11, however in my case they are just warnings, but no crash.

In order to work out the best solution, I tried creating a stripped down sample app with just a single entity containing a transformable attribute. But it seems that no matter what I tried I could not reproduce the problem. The I copied the model file from my main app to the demo app, and of course that failed.

So I got to the point where I just had two model files and a simple unit test which does nothing more than open the model and create a persistent store container:

 func testDataModels() {
     openDataModel(named: "samplemodel")
     openDataModel(named: "appmodel")
 }

 func openDataModel(named name: String) {
    print("Opening \(name)")
     guard let url = findFile(forResource: name, withExtension: "momd"),
           let managedObjectModel = NSManagedObjectModel(contentsOf: url)
     else {
         XCTFail("Unable to find \(name) data model")
         return
     }
    print(url)
     _ = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
 }

 func findFile(forResource name: String, withExtension ext: String) -> URL? {
     if let url = Bundle(for: type(of: self)).url(forResource: name, withExtension: ext) {
         return url
     }
     return Bundle.main.url(forResource: name, withExtension: ext)
 }

The appmodel causes the error messsages but the sample model does not. Even when I stripped the appmodel down to a single Entity it continues to generate the errors.

Comparing the contents of the samplemodel with the appmodel (show package contents in Finder), there is a hidden file called .xccurrentversion in the samplemodel but not in the appmodel. The file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>_XCCurrentVersionName</key>
    <string>samplemodel.xcdatamodel</string>
</dict>
</plist>

So I created a similar file for the appmodel and put it in the package folder. Surprisingly, that silences the warning messages! Conversely, deleting the .xccurrentversion file from the samplemodel causes the error messages to be generated. This will allow testing of the problem in isolation.

So this may be a short term fix. In the meantime, I need to work out how to migrate to secure coding.

Upvotes: 1

Ratneshwar Singh
Ratneshwar Singh

Reputation: 591

Setting Transformer property to NSSecureUnarchiveFromDataTransformer solved the warning in my case. For this select the attribute & set its transformer type to NSSecureUnarchiveFromDataTransformer & run again by pressing commond+R.

Thanks, Ratneshwar

Upvotes: 28

Angel Olvera
Angel Olvera

Reputation: 67

For the transformable attribute, you need to set its type in the Custom Class field.

For instance, I have a transformable field which stores an array of numbers and its Custom Class is declared as [Int16]. This is most likely the cause of the crash. And as @vadian mentioned before, you don't need both fields.

After your crash is fixed, you can get rid of the warning by setting the Transformer field to NSSecureUnarchiveFromData (you simply type this into the field)

Upvotes: 3

Related Questions