Save CKServerChangeToken to Core Data
Asked Answered
R

1

5

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

Reputable answered 14/11, 2016 at 12:47 Comment(0)
W
24

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")
            }
        }
    }
}
Wearable answered 29/11, 2016 at 18:21 Comment(5)
Thanks @richardpiazza. Can anyone confirm that you are indeed supposed to cache the change token for longer than the run of the app--ie, not just in-memory? The documentation seems ambiguous to me.Josphinejoss
@Josphinejoss The only reason you wouldn't want to supply the change token in the call is to force retrieval of all changes in the record zone. Take a look at the CloudKit Best Practices video from WWDC 2016. Here's a link to the relevant time index: developer.apple.com/videos/play/wwdc2016-231/?time=822Wearable
The WWDC 2016 video "CloudKit Best Practices, at 15'45" says it needs to be cached across runs precisely so you can know how up to date your app's data cache is. See developer.apple.com/videos/play/wwdc2016/231Himself
Since iOS12 this will get a warning, because "NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken" is deprecated.Sulfonal
Thanks for the iOS 12 update! the docs are broken for the NSKeyedArchiver changesLucite

© 2022 - 2024 — McMap. All rights reserved.