cannot push coredata records to cloudkit

coredata pushed schema to cloudkit only for those entities for which I created records. But the record data did not get pushed. I set recordName as Queryable and modifiedTimestamp as both Queryable and sortable. Query does not show the records. I look at Xcode console gives this output for one of the entities that got pushed to cloudkit :

CoreData: debug: CoreData+CloudKit: -[PFCloudKitSerializer newCKRecordsFromObject:fullyMaterializeRecords:includeRelationships:error:](575): Serializer has finished creating record: <CKRecord: 0x13f40f920; recordType=CD_Contact, recordID=26809600-B329-4C17-B3E1-6EA5FC177F7C:(com.apple.coredata.cloudkit.zone:__defaultOwner__), values={
    "CD_contact" = "26809600-B329-4C17-B3E1-6EA5FC177F7C";
    "CD_contactEmail_ckAsset",
    "CD_contact",
    "CD_contactEmail",
    "<CKRecord: 0x13f40f920; recordType=CD_Contact, recordID=26809600-B329-4C17-B3E1-6EA5FC177F7C:(com.apple.coredata.cloudkit.zone:__defaultOwner__), recordChangeTag=5, values={\n    \"CD_email\" = Email;\n    \"CD_entityName\" = Contact;\n    \"CD_firstName\" = V;\n    \"CD_imageName\" = \"{ length=20834, sha256=d582bd2ccc7d93138b3a5ad4799443152860268e34f48ace54a0708f3e2f3aba }\";\n    \"CD_lastName\" = R;\n    \"CD_phone\" = 2;\n    \"CD_screenName\" = Vr;\n    \"CD_userRating\" = \"*****\";\n    \"CD_userType\" = Household;\n}>",

Also schema for some other entities that do not have any core data records did not get pushed to CloudKit. I thought the entire coredata schema should get pushed along with the records. Looks like it is pushing those entities that have some records but without pushing data. I reset the cloudkit environment and tried again, but issue is not resolved.

Also the AppDelegate never gets called. I thought the line below should have called AppDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate

Cross referenced persistenceController in AppDelegate. That did not help either

Code listed below. Please let me know how to fix the above two issues?

main
struct GreenApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate

    static var fcmToken: String?

    let gcmMessageIDKey = "gcm.Message_ID"

    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}
struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
//        for _ in 0..<10 {
//            let newItem = Item(context: viewContext)
//            newItem.timestamp = Date()
//        }
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "Green")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
        container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy // external changes trumping in-memory changes.
    }

}

Appdelegate code:


class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, ObservableObject {

    static var fcmToken: String?

    let gcmMessageIDKey = "gcm.Message_ID"
    let persistenceController = PersistenceController.shared

    func applicationDidFinishLaunching(_ application: UIApplication) {

        do {
            // Use the container to initialize the development schema.
            try persistenceController.container.initializeCloudKitSchema(options: [])
        } catch {
            // Handle any errors.
            fatalError("###\(#function): failed to load persistent stores: \(error)")
        }

        if #available(iOS 10.0, *) {
            // For iOS 10 display notification (sent via APNS)
            UNUserNotificationCenter.current().delegate = self

            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
            UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: { granted, error in

            })

        } else {
            let settings: UIUserNotificationSettings = UIUserNotificationSettings(types:[.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }
        DispatchQueue.main.async {
            application.registerForRemoteNotifications()
        }

    }
}

Replies

One thing to change is to make your PersistenceController a class instead of a struct.

Is your app delegate class name listed in the Info.plist file (NSExtension / NSExtensionPointIdentifier)? How this is done is, I guess, platform specific but at least Apple Watch extension delegate is specified there using WKExtensionDelegateClassName with value $(PRODUCT_MODULE_NAME).ExtensionDelegate where the ExtensionDelegate is the class name.

  • I changed persistentController to class and ran but did not make any difference and does not push the entity values to cloudkit. I see this in output console: it tries to create a fake CD_Contact and then at the end deletes it like this: "CD_Contact" = ( edObjectContext:andObservedStore:error:](191): Deleting record with id (CD_Contact): <CKRecordID: 0x2810815e0; recordName=CD_FAKE_Contact_272EF7A8-C51B-4D33-B054-8EDEBE566F90, zoneID=com.apple.coredata.cloudkit.zone:defaultOwner>

  • I guess you have enabled the CoreData debug flags: https://www.avanderlee.com/debugging/core-data-debugging-xcode/ since you see debug info in console.

    Does your model take into account that synching your model to Cloud does not support all the CoreData features, like uniqueness constraints? Any warnings about this in your project?

    I am just guessing, since that info gives me no other clues.

Add a Comment

I inserted debug statement and got bunch of output. I could not figure out anything. So I deleted the app from my phone, and that should have deleted the local core data except icloud data. Then I connected the phone and ran again, the deleted records were retrieved from cloud kit. So that means it exists on cloud kit. However when I go to cloudKit and query for that CkRecord in public database it does not return any records. However it is retrieved from cludkit The debug console shows:

  • Importing updated records:

"<CKRecord: 0x1371117d0; recordType=CD_Contact, recordID=C8665C72-1489-4A5B-A862-D8C74219B5AC:(com.apple.coredata.cloudkit.zone:defaultOwner), recordChangeTag=ly, values={\n "CD_email" = E;\n "CD_entityName" = Contact;\n "CD_firstName" = V;\n "CD_imageName_ckAsset" = "<CKAsset: 0x13710b2a0; referenceSignature={length = 21, bytes = 0x016f67dfd0e6f5e95265715be55e67638763588cb7}, uploadRank=0, path=/private~/Library/Caches/CloudKit/ddfda053c3b06e4b4fc14067715eb6799399b480/Assets/059AD8FD-1A39-4AA9-83F5-2114E670D8F6.011ae0acd6179011e5f5b153e01a6b8b6b3146912b, size=3241087, UUID=059AD8FD-1A39-4AA9-83F5-2114E670D8F6, signature={length = 21, bytes = 0x011ae0acd6179011e5f5b153e01a6b8b6b3146912b}, wrappedAssetKey=<24 bytes>>";\n "CD_lastName" = R;\n "CD_phone" = 2;\n "CD_screenName" = Be;\n "CD_uniqueId" = "102B2E06-79B5-45E7-A01C-2169412D08FB-4156-0000015E1DE289BE";\n "CD_userRating" = "*****";\n "CD_userType" = Household;\n}>" I was puzzled as to why cloudKit does not show any records. I have indexed recordName as Queryable, modifiedTimeStamp as queryable and sortable. I have only one defaultZone. By chance I decided to check private database and queried records from private database and lo and behold they were all there. So why are my records going to private database instead of public database. I have posted all my code above and nowhere you can see I have configured private database store. In fact I am using default code provided by Apple and some code from net and I assumed it would go to public database. Can you clarify why it is going to private database and how to point it to public database. I do want some records to go to private database though. So I Need the option to synchronize some data to public and some to private database. How to do this?

  • May be I should create configurations in coredata for public and private?

  • I did that and even the entity which is exclusively defined in public configuration of core data gets pushed to private database in cloudKit. How to synchronize core data entity to public database

Add a Comment