1

I'm dealing with the scenario, where a user has previously deleted the app and has now re-installed it.

It was hitting my delta fetch function, which is receiving a lot of old subscription notifications, mostly deletes. But not downloading current records.

I'm now adding code to perform a fetch on each record type to download all the data.

I'd like to reset delta fetch server token, so the app doesn't have to process old subscriptions notifications. However I can't find how to do this, maybe it's not possible.

Jules
  • 7,568
  • 14
  • 102
  • 186

1 Answers1

0

Are you referring to CKServerChangeToken (documentation) when you say "delta fetch server token"? And are you attempting to sync within the CloudKit private database?

Assuming that is true, here is an example of how I fetch changes from the private database and keep track of the sync token:

//MARK: Fetch Latest from CloudKit from private DB
func fetchPrivateCloudKitChanges(){
  print("Fetching private changes...")

  //:::
  let privateZoneId = CKRecordZone.ID(zoneName: CloudKit.zoneName, ownerName: CKCurrentUserDefaultName)

  /----
  let options = CKFetchRecordZoneChangesOperation.ZoneOptions()
  options.previousServerChangeToken = previousChangeToken

  let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: [privateZoneId], optionsByRecordZoneID: [recordZoneID:options])

  //Queue up the updated records to process below
  var records = [CKRecord]()

  operation.recordChangedBlock = { record in
    records.append(record)
  }

  operation.recordWithIDWasDeletedBlock = { recordId, type in
    //Process a deleted record in your local database...
  }

  operation.recordZoneChangeTokensUpdatedBlock = { (zoneId, token, data) in
    // Save new zone change token to disk
    previousChangeToken = token
  }

  operation.recordZoneFetchCompletionBlock = { zoneId, token, _, _, error in
    if let error = error {
      print(error)
    }
    // Write this new zone change token to disk
    previousChangeToken = token
  }

  operation.fetchRecordZoneChangesCompletionBlock = { error in
    if let error = error {
      print(error
    }else{
      //Success! Process all downloaded records from `records` array above...
      //records...
    }
  }

  CloudKit.privateDB.add(operation)
}

//Change token property that gets saved and retrieved from UserDefaults
var previousChangeToken: CKServerChangeToken? {
  get {
    guard let tokenData = defaults.object(forKey: "previousChangeToken") as? Data else { return nil }
    return NSKeyedUnarchiver.unarchiveObject(with: tokenData) as? CKServerChangeToken
  }
  set {
    guard let newValue = newValue else {
      defaults.removeObject(forKey: "previousChangeToken")
      return
    }

    let data = NSKeyedArchiver.archivedData(withRootObject: newValue)
    defaults.set(data, forKey: "previousChangeToken")
  }
}

Your specific situation might differ a little, but I think this is how it's generally supposed to work when it comes to staying in sync with CloudKit.

Update

You could try storing the previousServerChangeToken on the Users record in CloudKit (you would have to add it as a new field). Each time the previousServerChangeToken changes in recordZoneFetchCompletionBlock you would have to save it back to iCloud on the user's record.

Clifton Labrum
  • 13,053
  • 9
  • 65
  • 128
  • Yes I do, however I need some means of getting th current token upon re-installing the app, not the first token. As I will do a fetch to get all data. I then want to use a a token for future changes. – Jules Dec 11 '18 at 21:16
  • If the user deletes the app on iOS, then all `UserDefaults` are deleted as well. As far as I know, there is no way to retain the token. What you could do instead is keep a table on CloudKit that matches the `Users` unique id (get it with `fetchUserRecordID()`) to a timestamp or a token. When that user accesses the app, fetch the timestamp or token and sync accordingly. That's the only way to preserve data for that user across app delete/install. – Clifton Labrum Dec 11 '18 at 21:20
  • OK thanks, however is there no way to mark all notifications as read? – Jules Dec 11 '18 at 21:51
  • You could query for data based on the timestamp associated with the user ID, so you'd only fetch the records after that date. This would just be an initial fetch with `CKQuery` and after that you could just use the `previousChangeToken` to know what to fetch. – Clifton Labrum Dec 12 '18 at 01:50
  • Erm, so basically just storing the 'previousChangeToken' against a device id provided by 'fetchUserRecordID()' ? would that device id be the same each time regardless if the app was deleted ? Thanks. If so can you update your answer so I can accept it :) – Jules Dec 12 '18 at 13:08
  • @Jules Done. :) – Clifton Labrum Dec 12 '18 at 18:27