I use CloudKit and Core Data with Swift to synchronize my data. Therefore I created Subscriptions and after getting a notification I fetch the new data from the cloud to update my Core Data. This is the recommended way to handle this update. For fetching data changes I can insert a CKServerChangeToken to fetch just the new stuff. My question is how to save the token to Core Data for later fetch requests? For CKRecords there is a method to save only the metadata but there isn't anything like this for CKServerChangeToken. Does anyone have an idea?
Best regards, Jannik
The CKServerChangeToken is an opaque data object that inherits from NSObject and conforms to the NSCopying protocol, which means that you can use a NSKeyedArchiver and NSKeyedUnarchiver to convert the token to an (NS)Data object.
A (NS)Data object can then be stored into a property on any NSManagedObject. Alternatively you could store this data in the (NS)UserDefaults. Here's one way to accomplish this as a swift extension to UserDefaults:
import Foundation
import CloudKit
public extension UserDefaults {
public var serverChangeToken: CKServerChangeToken? {
get {
guard let data = self.value(forKey: "ChangeToken") as? Data else {
return nil
}
guard let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken else {
return nil
}
return token
}
set {
if let token = newValue {
let data = NSKeyedArchiver.archivedData(withRootObject: token)
self.set(data, forKey: "ChangeToken")
} else {
self.removeObject(forKey: "ChangeToken")
}
}
}
}
With this extension you can get/set the CKServerChangeToken right from (NS)UserDefaults with:
let changeToken = UserDefaults.standard.serverChangeToken
UserDefaults.standard.serverChangeToken = `newToken`
As pointed out, the NSKeyedUn/Archiver calls were deprecated in iOS 12. Here is an updated example.
import Foundation
import CloudKit
public extension UserDefaults {
public var serverChangeToken: CKServerChangeToken? {
get {
guard let data = self.value(forKey: "ChangeToken") as? Data else {
return nil
}
let token: CKServerChangeToken?
do {
token = try NSKeyedUnarchiver.unarchivedObject(ofClass: CKServerChangeToken.self, from: data)
} catch {
token = nil
}
return token
}
set {
if let token = newValue {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: false)
self.set(data, forKey: "ChangeToken")
} catch {
// handle error
}
} else {
self.removeObject(forKey: "ChangeToken")
}
}
}
}