In iOS 12, the NSKeyedArchiver's initializer init(forWritingWith:) was deprecated. Xcode 10 recommends replacing it with the new initializer init(requiringSecureCoding:). The problem is that this initializer only sets the value of the requiresSecureCoding property of the NSCoder object, but it doesn't set the NSMutableData object that will contain the encoded data. The following is the original code propose by Apple to encode the metadata of a CKRecord (CloudKit record):
let data = NSMutableData()
let coder = NSKeyedArchiver.init(forWritingWith: data)
coder.requiresSecureCoding = true
record.encodeSystemFields(with: coder)
coder.finishEncoding()
The encodeSystemFields method of the CKRecord class requires an NSKeyedArchiver object (an NSCoder subclass) and the encoded data is stored in the NSMutableData object associated with this object. If I replace the init(forWritingWith:) initializer by the init(requiringSecureCoding:) initializer, I get an NSKeyedArchiver object, but this object is not associated with any NSMutableData object and therefore I don't get the record's metadata. I'm not sure how to complete the code to get the data produced by the NSKeyedArchiver object into an NSMutableData object.
For a few releases now NSKeyedArchiver
has had an encodedData
method which calls -finishEncoding
on the archiver and returns to you the finalized encoded data that the archiver has created. This was how you would get the finished data when initializing via -[NSKeyedArchiver init]
:
let coder = NSKeyedArchiver()
/* encode stuff */
let data = coder.encodedData
This obviated the need for the NSMutableData
variant, and with this update, the mutable data variant has been deprecated, favoring the newer paradigm. So rather than writing
let data = NSMutableData()
let coder = NSKeyedArchiver.init(forWritingWith: data)
coder.requiresSecureCoding = true
record.encodeSystemFields(with: coder)
coder.finishEncoding()
you'd write
let coder = NSKeyedArchiver(requiringSecureCoding: true)
record.encodeSystemFields(with: coder)
let data = coder.encodedData
The manual assignment to .requiresSecureCoding
and the manual finishEncoding()
call are both no longer necessary.
Note this dance is only necessary when calling CKRecord.encodeSystemFields(with:)
, which explicitly takes an NSCoder
in order to encode only a subset of itself. In the general case of encoding an object, you would go with the new NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:)
method:
let data = try NSKeyedArchiver.archivedData(withRootObject: /* object */, requiringSecureCoding: true)
which is equivalent to
let coder = NSKeyedArchiver(requiringSecureCoding: true)
coder.encodeObject(/* object */, forKey: NSKeyedArchiveRootObjectKey)
let data = coder.encodedData
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With