1

First post here, and desperate for help.

I am hitting an issue when developing an app that uses the CloudKit Public DB. I am relatively new to this, and I may be doing something wrong. I have distilled the issue to use just the template app that comes when you create a new Core Data with iCloud app, and detailed the steps below. It may be me doing something stupid.

This was working in iOS 14, as soon as iOS 15 and XCode 13 came along, it stopped. I am using SwiftUI.

In a nutshell, syncing to the public DB works for the first run of the app only. Stopping and restarting the app seems to throw errors and cause the mirroring delegate to fail.

All assistance / advice gratefully received. The steps below recreate the issue:

  1. Create App with Use Core Data and Host in ICloud - interface is SWiftUI

  2. In project signings and capabilities add iCloud and Background Modes

  3. In newly appeared iCloud section enable Cloudkit and choose or create a container

  4. In newly appeared Background Modes section enable Remote Notifications

  5. In persistence.swift add :

import CloudKit

  1. Still in persistence.swift add the following:
guard let description = container.persistentStoreDescriptions.first else
{
fatalError("Something bad happened")
}

description.cloudKitContainerOptions?.databaseScope = .public

I have added this just before the following line:

  container.loadPersistentStores(completionHandler: { (storeDescription, error) in
  1. In the schema model, highlight the default configuration and check in the data model inspector that "use cloudkit" is checked

  2. Run the app and hit the + a few times to add some records.

  3. Check on Cloudkit Dashboard in the public database in the container selected / created in step 3. You will need to created indexes on __recordID and__modTime in order to view the records.

You should see that the records after a minute or so all appear in the public database. No errors are reported in the console window on Xcode.

  1. Stop and restart the app.

  2. This time when the plus button is clicked, the records do not upload to the public DB and the console is reporting error messages similar to that below:

CoreData: CloudKit: CoreData+CloudKit: -NSCloudKitMirroringDelegate _finishedRequest:withResult:: Finished request: <NSCloudKitMirroringExportRequest: 0x283666bb0> 9F190C45-7713-4ACE-96DB-867D99FF62ED with result: <NSCloudKitMirroringResult: 0x2838b9e20> success: 0 madeChanges: 0 error: Error Domain=NSCocoaErrorDomain Code=134406 "Request '9F190C45-7713-4ACE-96DB-867D99FF62ED' was aborted because the mirroring delegate never successfully initialized due to error: <CKError 0x2836aba50: "Partial Failure" (2/1011); "Failed to modify some record zones"; uuid = 3F399390-1F10-4198-BBBF-F3BD76E99591; container ID = "iCloud.askeit.com.WTBA"; partial errors: { com.apple.coredata.cloudkit.zone:defaultOwner = <CKError 0x2836a5290: "Server Rejected Request" (15/2027); server message = "Custom zones are not allowed in public DB"; op = 943153DA6AC169A1; uuid = XXXXXXXXXXXXXXXXXXXXXXXX> }>" UserInfo={NSLocalizedFailureReason=Request '9F190C45-7713-4ACE-96DB-867D99FF62ED' was aborted because the mirroring delegate never successfully initialized due to error: <CKError 0x2836aba50: "Partial Failure" (2/1011); "Failed to modify some record zones"; uuid = 3F399390-1F10-4198-BBBF-F3BD76E99591; container ID = "iCloud.askeit.com.WTBA"; partial errors: { com.apple.coredata.cloudkit.zone:defaultOwner = <CKError 0x2836a5290: "Server Rejected Request" (15/2027); server message = "Custom zones are not allowed in public DB"; op = 943153DA6AC169A1; uuid = XXXXXXXXXXXXXXXXXXXXXXXXX> }>}

So basically:

a) Am I doing something stupid or b) The code works elsewhere - so must be something with my setup or c) is this unexpected behaviour and I'll have to wait for Apple to sort it.

Thank-you in advance for all help offered.

Cheers,

Ian

Snark2021
  • 31
  • 5
  • What is a guy named 'container' and how is it set up? – El Tomato Oct 16 '21 at 09:24
  • Thanks for coming back to me. Create the project and add iCloud capability the target, the iCloud panel appears. It has two main sections, Service - where you check CloudKit and Containers. If you have no containers already, hit the plus and you will get a pop-up. Then give it a name like 'com.acme.test1' or something (make it useful for yourself, because you can't delete them as far as I know). When you save that, it will appear read for a few secs, and then grey. You will need to be signed into your iCloud account. Clicking the CloudKit Console button will show you it online. – Snark2021 Oct 16 '21 at 09:47
  • I've used CloudKit many times. Where does a guy named 'container' come from? It's a variable, and nobody else but you knows where it's set. – El Tomato Oct 16 '21 at 09:50
  • This was autogenerated when I created the project in Persistence.swift, it wasn't something I added. The name I gave my project was TestCK3. And this is what the code looked like: let container: NSPersistentCloudKitContainer init(inMemory: Bool = false) { container = NSPersistentCloudKitContainer(name: "TestCK3") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } – Snark2021 Oct 16 '21 at 10:13
  • I'm having a few probs with the markup - please excuse. In the model itself, I didn't change anything, just using the default Item entity. This obviously isn't my app code , which you could probably write a dissertation on things that could be improved, but this simple setup of the template demonstrates the issue I am hitting. – Snark2021 Oct 16 '21 at 10:18

1 Answers1

0

For those who stumble upon this, the issue now seems to be resolved in iOS 15.1. I installed Xcode 13.1 first and this did not solve the issue, but when I upgraded my test device, it worked. Whether it was iOS 15.1 or the combination of the two, I can't say for certain.

A follow-up. I'm still not convinced this is 100% correct. My code was working fine again, then seemed to deteriorate and then stopped working altogether. I have been able to get it going again. It appears to be linked to the Cloudkit schema. The initialise of the schema alone does not appear to be enough. I am pretty sure it was before, because I would do the intialise, then manually create the indexes after to see my records. To get this working again I had to pre-create the indexes as I was getting error messages saying the mirroring delegate had not successfully initialized due to error  "Invalid Arguments" (12/2015); server message = "Field 'recordName' is not marked queryable"; So recordID needs a queryable index, and modTime needs queryable and sortable. I also noticed that there seems to be a glitch in the Cloudkit Dashboard, and if you add the two indexes for modTime at the same time, one of them won't appear. So make sure you check after. If this continues to plague me as it has now for over a month, I'll add an update. If anyone spots that I am making a newb error, please leave a comment and let me know.

Snark2021
  • 31
  • 5
  • Still having problems on Xcode 13.2 and iOS 15.1.1. CloudKit dashboard also doesn't allow me to add a sortable index on modifiedTimestamp (I can only assume that's what's meant by __modTime?). CloudKit syncing is seeming really unstable for production. – PostCodeism Jan 04 '22 at 23:52
  • UPDATE: it turns out I could add a sortable and queryable index on modifiedTimestamp after a classic page refresh. So I did that on all CloudKit entities and it worked, Core Data is syncing with CloudKit. – PostCodeism Jan 05 '22 at 01:42
  • Yeah - sorry, only just saw your comment. __modTime is modifiedTimestamp as far as I know, it certainly seems to work. I am not sure why, but the naming of the columns in Cloudkit has changed back and forward from __modTime to modifiedTime. – Snark2021 Jan 15 '22 at 09:02