I got a message in log 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
. I am not sure how to convert this class to use NSSecureCoding. I am not sure which class is throwing this error. Could you please help me on how to convert this?
I am using requiringSecureCoding: true
. Doesn't this make this secure?
class ZoneInfo: NSObject, NSCoding {
var zoneID: CKRecordZone.ID
var serverChangeToken: CKServerChangeToken? = nil
enum CodingKeys: String, CodingKey {
case zoneID
case serverChangeToken
}
func encode(with coder: NSCoder) {
coder.encode(zoneID.encode(), forKey: CodingKeys.zoneID.rawValue)
coder.encode(serverChangeToken?.encode(), forKey: CodingKeys.serverChangeToken.rawValue)
}
required init?(coder: NSCoder) {
self.zoneID = CKRecordZone.ID.decode(coder.decodeObject(forKey: CodingKeys.zoneID.rawValue) as! Data)!
if let tokenData = coder.decodeObject(forKey: CodingKeys.serverChangeToken.rawValue) as? Data {
self.serverChangeToken = CKServerChangeToken.decode(tokenData)
}
}
init(zone: CKRecordZone, serverChangeToken: CKServerChangeToken? = nil) {
self.zoneID = zone.zoneID
self.serverChangeToken = serverChangeToken
}
init(zoneID: CKRecordZone.ID, serverChangeToken: CKServerChangeToken? = nil) {
self.zoneID = zoneID
self.serverChangeToken = serverChangeToken
}
static func decode(_ data: Data) -> ZoneInfo? {
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: ZoneInfo.self, from: data)
}
func encode() -> Data {
return (try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)) ?? Data()
}
}
Thanks.
Your ZoneInfo
class should conform to NSSecureCoding
, not NSCoding
.
Your encode(with:)
is needlessly double-encoding the properties and your init?(coder:)
is needlessly double-decoding the properties.
Here's a corrected version of your class:
class ZoneInfo: NSObject, NSSecureCoding {
// Added to support NSSecureCoding
static var supportsSecureCoding: Bool { return true }
var zoneID: CKRecordZone.ID
var serverChangeToken: CKServerChangeToken? = nil
enum CodingKeys: String, CodingKey {
case zoneID
case serverChangeToken
}
// Updated to avoid the double-encoding
func encode(with coder: NSCoder) {
coder.encode(zoneID, forKey: CodingKeys.zoneID.rawValue)
coder.encode(serverChangeToken, forKey: CodingKeys.serverChangeToken.rawValue)
}
// Updated to avoid the double-decoding and to properly handle an invalid zoneID
required init?(coder: NSCoder) {
if let zoneID = coder.decodeObject(of: CKRecordZone.ID.self, forKey: CodingKeys.zoneID.rawValue) {
self.zoneID = zoneID
} else {
return nil
}
self.serverChangeToken = coder.decodeObject(of: CKServerChangeToken.self, forKey: CodingKeys.serverChangeToken.rawValue)
}
init(zone: CKRecordZone, serverChangeToken: CKServerChangeToken? = nil) {
self.zoneID = zone.zoneID
self.serverChangeToken = serverChangeToken
}
init(zoneID: CKRecordZone.ID, serverChangeToken: CKServerChangeToken? = nil) {
self.zoneID = zoneID
self.serverChangeToken = serverChangeToken
}
static func decode(_ data: Data) -> ZoneInfo? {
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: ZoneInfo.self, from: data)
}
func encode() -> Data {
return (try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: true)) ?? Data()
}
}
Since I can't test this with an actual instance of CKServerChangeToken
, I was only able to verify that the above works with a zoneID. If you have an issue at runtime when you try to unarchive an instance of ZoneInfo
with a real CKServerChangeToken
value, let me know in a comment what the error is so I can update the answer if needed.
Note that the use of CodingKeys
isn't normally used with NSSecureCoding
. It's normally used for Swift Codable
. Your use here does work, it's just a bit confusing.
I also suggest that your encode
method return Data?
instead of returning an empty Data
instance on failure. Returning empty data will just lead to issues when you try to unarchive that empty data.