2

I see in "How (and when) do I use iCloud's encodeSystemFields method on CKRecord?" how to use NSKeyedArchiver to cache the salient fields of CKRecord. Is there a way to use JSONEncoder with encodeSystemFields? My other cache data is saved as JSON so I'd like encodeSystemFields to fit with that.

Unfortunately Data is not allowed in valid JSON despite being Codable, and fails the test JSONSerialization.isValidJSONObject, so I can't just jam the output of NSKeyedArchiver into my JSON serialization.

let d = Data()
d is Codable // true
// ...encodeSystemFields...
JSONSerialization.isValidJSONObject([d]) // false
let listofd = [d]
JSONSerialization.isValidJSONObject(listofd) // false
let dictofd = ["test":d]
JSONSerialization.isValidJSONObject(dictofd) // false

It doesn't matter if d is Data or NSMutableData. I get the same results.

Bob Peterson
  • 636
  • 7
  • 16

1 Answers1

2

The solution I found was to first serialize using NSKeyedArchiver to NSMutableData, cast it to Data, and use JSONEncoder to encode it again to JSON. It creates less than 2000 bytes of JSON. which overwhelmed my tiny record but that's life.

Here is a working playground for that:

import Foundation
import CloudKit

let zoneID1 = CKRecordZoneID(zoneName: "Test.1",
                             ownerName: CKCurrentUserDefaultName)
let recID1 = CKRecordID(recordName: "Test1",
                        zoneID: zoneID1)
let rec1 = CKRecord(recordType: "Test",
                    recordID: recID1)

let cacheNSData = NSMutableData()
let kArchiver = NSKeyedArchiver(forWritingWith: cacheNSData)
rec1.encodeSystemFields(with: kArchiver)
kArchiver.finishEncoding()

cacheNSData.length
let cacheData = cacheNSData as Data

let jEncoder = JSONEncoder()

struct Cache: Codable {
    var title: String
    var blob: Data
}
let cacheItem = Cache(title: "abc", blob: cacheData)

let jData = try jEncoder.encode(cacheItem)
jData.count

String(data: jData, encoding: .utf8)
Bob Peterson
  • 636
  • 7
  • 16