With iOS 12 Apple has started adopting NSSecureCoding across the entire platform. For Core Data this means that the default ValueTransformer, which uses NSCoding to transform a custom data type into a format that can be stored in the persistent store, at some point will change as well.

Because of this, you might have seen the following warning in your project:

CoreData: 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.

This week, let’s have a look at what this warning means and how we can solve it. We’ll start by going over some of the concepts of transformable properties and object serialization.

Transformable properties

Out of the box, Core Data supports a variety of data types, for example, integers, booleans, strings, etc. In most cases this is sufficient, but sometimes you want to store data of a different type for example, UIColor. You can do this by creating a Transformable property. When you declare a property as Transformable Core Data converts your custom data type into binary Data when it is saved to the persistent store and converts it back to your custom data type when fetched from the store. It does this through a value transformer.

A ValueTransformer is simply a class that transforms a value into another one. When you don’t define a custom transformer for your transformable property Core Data will use the default one. The default transformer works with NSCoding, a protocol that you implement on custom data types so it can be encoded and decoded. Encoding and decoding itself is done by NSKeyedArchiver and NSKeyedUnarchiver, which conforms to NSCoder. Because of this, the default transformer works on any type that conforms to the NSCoding protocol.

NSSecureCoding

One limitation of NSCoding is that it is vulnerable to substitution attacks. A substitution attack is when an attacker alters data, for example, stored on disk, with the goal of getting the app into a certain state in which it can be exploited. NSCoding is vulnerable for such an attack because it does not allow you to verify the class type before the object is constructed. At this point, the damage could already have been done.

/// Here we unarchive the data and validate that the object has the right structure.
/// As you can see we first need to construct `MyCustomObject` before we can validate it.
if let myCustomObject = decoder.decodeObjectForKey("myCustomObject") as MyCustomObject {
    // Use the custom type
}  

NSSecureCoding fixes this by allowing you to pass in the type information upfront, so that the archiver can do a class check before unarchiving. You can do this with the unarchivedObject(ofClass:from:) method on NSKeyedUnarchiver. If during unarchiving it encounters a different type of class or one that does not conform to NSSecureCoding it will stop and throw an exception. This will prevent bad things from happening.

Solving the warning

Now let’s move back to the warning. In the first sentence, it informs us that our data model contains one or more entities with a transformable property that uses the default value transformer. Either because we did not specify anything in the transformer field of the data model inspector or because it is set to NSKeyedUnarchiveFromDataTransformerName.

Unset value transformer Value transformer property set to default

It then tells us that in the future this will default to the NSSecureUnarchiveFromData transformer, which works with NSSecureCoding. And when that happens all transformable properties that do not adopt secure coding will become unreadable.

Conforming to NSSecureCoding

This means that the first thing we need to do is make sure that the data type of every transformable property in our data model conforms to secure coding. A lot of system types already support secure coding, but if it is a custom type you’ll need to do two things:

  1. Conform to the NSSecureCoding protocol:
final class MyCustomObject: NSSecureCoding {

    static var supportsSecureCoding = true
}

  1. Replace every call to decodeObject(forKey:) in your implementation of init(coder:) with decodeObjectOfClass:forKey:.

Setting a ValueTransformer

The new default value transformer, named NSSecureUnarchiveFromData, out of the box supports all plist types, for example, NSDate and NSString. So If you’re transformable property fits that requirement you can start using the new default transformer as follows:

Using the NSSecureUnarchiveFromData value transformer

For non-plist type, for example, UIColor, Core Data won’t be able to figure out which class the unarchiver should check for and will throw an exception. Because Swift doesn’t allow us to catch Objective-c and C++ exceptions this will make our app crash with a fatalError. We can solve this by creating a custom value transformer. Here is an example of a simple implementation:

// 1. Subclass from `NSSecureUnarchiveFromDataTransformer`
final class ColorValueTransformer: NSSecureUnarchiveFromDataTransformer {

    /// The name of the transformer. This is the name used to register the transformer using `ValueTransformer.setValueTrandformer(_"forName:)`.
    static let name = NSValueTransformerName(rawValue: String(describing: ColorValueTransformer.self))

    // 2. Make sure `UIColor` is in the allowed class list.
    override static var allowedTopLevelClasses: [AnyClass] {
        return [UIColor.self]
    }

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

Here’s what is happening:

  1. Just like the default transformer, we subclass from NSSecureUnarchiveFromDataTransformer.
  2. Make sure UIColor is in the allowed class list. This is the list that the archiver will check against when archiving or unarchiving the data.

The next thing you need to do is make sure Core Data knows that it should use the ColorValueTransformer for your transformable property.

The last step is to make the value transformer available by registering it through the register class function that we created. A good time to do this is right before setting up the persistent container.

Conclusion

Now that you know how all of this works you can go ahead and fix the warnings in your project. While you do that, it is a good idea to update any of your existing value transformers so that they also use NSSecureCoding. Doing this will make your app more secure.

Contact me on Twitter @kairadiagne if you have any questions, comments or feedback.