Reputation: 10744
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 a
Group` 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:
I am not sure how to recover from this crash. This was working fine with Xcode 10
Upvotes: 36
Views: 21785
Reputation: 342
BEWARE !!!
I can confirm that both:
Break iCloud Sync ⚠️🚫⚠️. Do not use them.
Leave the Value Transformer Name Field empty (inspector) and keep the warning.
Upvotes: 0
Reputation: 1269
Swift 5.4.2
This worked for me.
EDIT Link to the article is here.
Your warnings/errors should go away. If not, try cleaning your build folder and rebuild.
Upvotes: 42
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
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
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
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
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