Solve CoreData Warning: ‘NSKeyedUnarchiveFromData’ should not be used to for un-archiving and will be removed in a future release. (By Using NSSecureCoding)
Hint : Replace NSCoding with NSSecureCoding.
If you have used NSCoding and transformable in your project , then check out your console logs you will be able to see the below warning:
‘NSKeyedUnarchiveFromData’ should not be used to for un-archiving and will be removed in a future release.
So the next questions comes on the developer mind
How to get rid of this warning?
NSCoding:
Here is the example of the sample class which has used NSCoding:
class Person: NSObject,NSCoding
{
var title:String?
var id:Int?
func encode(with aCoder: NSCoder)
{ /* Imagine this is donesis */ }
required init?(coder aDecoder: NSCoder) {
title = coder.decodeObject(forKey: "title") as? String
id = coder.decodeObject(forKey: "id") as? Int }
}
Create Entity person with data attribute Transformable in xcdatamodelId as below:
For the above Entity, the model class looks as below:
import Foundation
import CoreData@objc(Person)public class Person: NSManagedObject {}extension Person { @nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
} @NSManaged public var data: Any?}
In the above class, data can be of any type like it could be a array that could be used for json parsing and it could contain title and id as such:
{
"data": [
{
"title": "Bob",
"id": 1
},
{
"title": "James",
"id": 2
}
]
}
Now, when you will run the above project which has NSCoding and transformable attributes then in your console logs you will be able to see the below warning:
‘NSKeyedUnarchiveFromData’ should not be used to for un-archiving and will be removed in a future release.
Inorder to fix we will use NSSecureCoding as suggested by Apple.
NSSecureCoding:
A protocol that enables encoding and decoding in a manner that is robust against object substitution attacks.
With NSSecureCoding the above Person class will be as below:
class Person: NSObject, NSSecureCoding
{ static var supportsSecureCoding: Bool {
get { return true }
} var title:String?
var id:Int?
func encode(with aCoder: NSCoder)
{ /* Imagine this is donesis */ }
required init?(coder aDecoder: NSCoder)
{
title = coder.decodeObject(of: NSString.self, forKey: "name") as String?
id = coder.decodeObject(of: NSNumber.self, forKey: "id") as? Int
}
}
The NSSecureCoding
protocol directly inherits from NSCoding
- and as the name implies it performs the same functions as its parent but in a more secure fashion. Using it, we’ll avoid any arbitrary code execution attacks as mentioned above.
A correct implementation of the the NSSecureCoding protocol in your class, means you have to use: decodeObject(of: Class, for: Key)
instead of the usual decodeObject(for: Key).
Custom (non top-level) classes:
For the above example, We will create a subclass of NSSecureUnarchiveDataTransformer, and add your class to the allowedTopLevelClasses
array Let’s write a custom ValueTransformer for our Person
class above.
import Foundation@objc(CustomClassValueTransformer)final class CustomClassValueTransformer:NSSecureUnarchiveFromDataTransformer {
static let name = NSValueTransformerName(rawValue:String(describing: CustomClassValueTransformer.self)) override static var allowedTopLevelClasses: [AnyClass] { return [NSObject.self,NSArray.self] }/// Registers the transformer.public static func register() { let transformer = CustomClassValueTransformer() ValueTransformer.setValueTransformer(transformer, forName: name) }}
We have used NSArray.self in allowed classes to parse ‘data’ of json which is of array type and NSObject.self for string and integer dataTypes.
Here’s what’s happening:
First, we define a name for our value transformer, and then we define a list of classes that would be allowed, which is what the unarchiver uses to validate that the class is correct, and lastly, we create a register()
method that will allow us to register the new value transformer during CoreData initialization.
Register your value transformer:
A good place to register value transformer is during your CoreData stack initialization, but make sure you do it before setting up your Persistent Container.
For example If you have a Custom CoreData Class then you can initialize as follow:
import Foundationimport CoreDataclass CoreDataManager {static var shared = CoreDataManager()let persistentContainer: NSPersistentContainer init() { persistentContainer = NSPersistentContainer(name: "YourCoreDataName")CustomClassValueTransformer.register()persistentContainer.loadPersistentStores { description, error inif let error = error {fatalError("Core Data Store failed: \(error.localizedDescription)") } }}}
Lastly, configure your model to use your brand new ValueTransformer as follows:
Conclusion:
Now when you will run the project, the Xcode warning will disappear and everything will work fine and your data is stored in more secure manner.