Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 11 - Core Data - UIColor no longers works as transformable attribute

I store colours in my binary Core Data store using a transformable attribute, specifying the class of the attribute as UIColor like so:

#import "CoreDataEntity+CoreDataClass.h"
#import <UIKit/UIKit.h>


NS_ASSUME_NONNULL_BEGIN

@interface CoreDataEntity (CoreDataProperties)

+ (NSFetchRequest<CoreDataEntity *> *)fetchRequest;

@property (nullable, nonatomic, retain) UIColor *transformable;
@property (nullable, nonatomic, copy)   NSString *string;

@end

NS_ASSUME_NONNULL_END

In the iOS 11 Beta this has stopped working with an error like this :

NSUnderlyingException=value for key 'NS.objects' was of unexpected class 'UIColor'. Allowed classes are '{(\n    NSDecimalNumber,\n    NSData,\n    NSUUID,\n    NSNumber,\n    NSDate,\n    NSArray,\n    NSOrderedSet,\n    NSDictionaryMapNode,\n    NSString,\n    NSSet,\n    NSDictionary,\n    NSURL,\n    NSNull\n)}'.}";
    NSUnderlyingException = "Can't read binary data from file";
}

I managed to replicate the specific problem in an XCode project on GitHub (Must be run with the XCode Beta twice to get the error).

In the demo project the store type is controlled by NSPersistentStoreDescription and setting it to NSBinaryStoreType, which I do in the AppDelegate in the exanple project, and I add objects in application didFinishLaunchingWithOptions, otherwise it's the standard template from an iOS11 app with core data. Plus a small datamodel and classes.

If you run the project twice, the first time it creates the datastore and everything is fine. The second time, the datastore tries to open and crashes the app. This problem only seems to be related to binary datastores from what I can tell, if I use an SQL backed datastore it works. However, my app is in the wild and uses binary.

I've reported it to Apple as a bug and sought help on the developer forums, but Apple has not acknowledged the bug and no help was coming.

I'm getting a bit worried as the iOS11 release date draws nearer and I have no solution, my app just won't work in iOS11.

I've tried changing the property to NSData and seeing if it was possible to just unarchive the data, but it seems it's still stored internally as a UIColor somehow and the database just won't open.

Can anyone see a workaround? I have the app in the wild, and possibly pushing out an update to convert the datastores before iOS11 could work for some, but that isn't going to guarantee all users get the fix and they could lose their data.

EDIT 1: Radar number : 33895450

EDIT 2: It just occured to me that this applies to any transformable attribute in core data, the values supported in the error message are just the default property types.

EDIT 3: Just out of curiosity I filled out all the fields for the transformable attribute (it was never required before). I added "NSKeyedUnarchiveFromData" to value transformer name of the core data entity, it should be the default, but you never know. No effect. It must be using the value transformer anyway to know that it's a UIColor. I filled in the custom class field to be UIColor, no effect.

Edit 5 : I noticed earlier that UIColor now supports NSSecureCoding, should security somehow be the issue somehow overlooked in the other store typed.

Edit : Now that iOS is released, i’ve used one of my TSIs to further escalate this. Do i get them back if i have to use one to get them to fix their software?

Edit : Apple got back to me on my TSI, they said it’s under investigation, there is no workaround, and to wait on the bug. They refunded my TSI because they couldn’t help.

Edit 8: Same problem on macOS High Sierra, with NSColor instead of UIColor.

Apple still have not given me any feedback on my actual bug report.

like image 321
George Brown Avatar asked Sep 01 '17 17:09

George Brown


People also ask

What is transformable type in Core Data?

Transformable attributes are useful for storing nonstandard object types within Core Data. For example, I provide code in this answer that lets you store UIImages as an attribute within Core Data. The image data is converted to and from an NSData instance that contains the image's PNG representation.

How do you use transformable Core Data?

To implement a Transformable attribute, configure it by setting its type to Transformable and specifying the transformer and custom class name in Data Model Inspector, then register a transformer with code before an app loads its Core Data stack.

How do I store UIColor in Core Data?

We create an extension for the Note class and define a computed property, color , of type UIColor? . We implement the getter and setter of the computed property to convert the value of the colorAsHex property. In the getter, we convert the value of the colorAsHex property to a UIColor instance.

What is Core Data in IOS?

Overview. Use Core Data to save your application's permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.


1 Answers

George did all the hard work. I only applied it to Swift. Here is my solution. I put it into my NSPersistentDocument descendant.

override func configurePersistentStoreCoordinator(for url: URL, ofType fileType: String, modelConfiguration configuration: String?, storeOptions: [String : Any]? = nil) throws {
    var options = storeOptions != nil ? storeOptions! : [String:Any]()
    if #available(OSX 10.13, *) {
        options[NSBinaryStoreSecureDecodingClasses] = NSSet(object: NSColor.self)
    }
    options[NSMigratePersistentStoresAutomaticallyOption] = true
    options[NSInferMappingModelAutomaticallyOption] = true
    try super.configurePersistentStoreCoordinator(for: url, ofType: fileType, modelConfiguration: configuration, storeOptions: options)
}

Now I can read my files again. Thanks George!

like image 54
Wizard of Kneup Avatar answered Oct 26 '22 19:10

Wizard of Kneup