Beta: How to replace NSKeyedArchiver's initializer init(forWritingWith:) in iOS 12 to encode the metadata of a CKRecord?

Hi, guys. 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.

Accepted Reply

Well, the answer, in case someone also finds this problem, was very simple (thanks Itai Ferber!). Since a while ago, we have the encodedData property available. This property returns the encoded data and calls the finishEncoding() method, so there is no need to provide an NSMutableData object or execute that method anymore. We just have to create the NSKeyArchiver object with the new initializer, encode the data, and then get it from the property.


let coder = NSKeyedArchiver(requiringSecureCoding: true)
/* encode things */
let data = coder.encodedData


Thanks again to Itai for his quick response.

Replies

        let recordName = "testNAME"
        let recordID = CKRecord.ID(recordName: recordName, zoneID: CKRecordZone.ID(zoneName: "testZoneName", ownerName: CKCurrentUserDefaultName))
        let record = CKRecord(recordType: "Category", recordID: recordID)



       do {
             let data = try NSKeyedArchiver.archivedData(withRootObject: record, requiringSecureCoding: true)
        } catch {
            print("archiving failed \(error)")
        }


or


  let data = try? NSKeyedArchiver.archivedData(withRootObject: record, requiringSecureCoding: true)

Hi, Manuel. Thanks, but that's not what I'm looking for. I need a way to encode the record's metadata, not the record. The metadata not only includes the record's name and zone, but also the date and the token required to know which record is older. For that, Apple provides the encodeSystemFields(with: coder) method. The problem is that this method takes an NSCoder object that now I can't provide because the NSKeyedArchiver.init(forWritingWith: data) initializer was deprecated. You can read more here (go the the end of the page):


https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitQuickStart/MaintainingaLocalCacheofCloudKitRecords/MaintainingaLocalCacheofCloudKitRecords.html


Thanks anyway.

Well, the answer, in case someone also finds this problem, was very simple (thanks Itai Ferber!). Since a while ago, we have the encodedData property available. This property returns the encoded data and calls the finishEncoding() method, so there is no need to provide an NSMutableData object or execute that method anymore. We just have to create the NSKeyArchiver object with the new initializer, encode the data, and then get it from the property.


let coder = NSKeyedArchiver(requiringSecureCoding: true)
/* encode things */
let data = coder.encodedData


Thanks again to Itai for his quick response.