CoreData с CloudKit на watchOS не синхронизируется

Мне удалось получить CoreData с CloudKit, работающим с новым NSPersistentCloudKitContainer в версии моего приложения для iOS, и он автоматически синхронизировался во время работы приложения. Однако, когда я пошел настраивать приложение watchOS, я заметил, что синхронизация произойдет, только если я принудительно закрою и снова открою приложение для часов.

import Foundation
import CoreData

class DataManager : NSObject {
    static let shared = DataManager()

    #if os(watchOS)
    let transactionAuthorName = "watchOSApp"
    #else
    let transactionAuthorName = "iOSApp"
    #endif

    override private init() {
        super.init()
    }

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentCloudKitContainer(name: "Model")

        let cloudStoreUrl = applicationDocumentDirectory()!.appendingPathComponent("product.sqlite")

        let cloudStoreDescription = NSPersistentStoreDescription(url: cloudStoreUrl)
        cloudStoreDescription.shouldInferMappingModelAutomatically = true
        cloudStoreDescription.shouldMigrateStoreAutomatically = true
        cloudStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier:"iCloud.com.company.product")

        container.persistentStoreDescriptions = [cloudStoreDescription]

        container.loadPersistentStores(completionHandler: { storeDescription, error in
            if let error = error as NSError? {
                print("Error loading store. \(error)")
            }
        })

        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.automaticallyMergesChangesFromParent = true

        try? container.viewContext.setQueryGenerationFrom(.current)
        container.viewContext.transactionAuthor = transactionAuthorName

        return container
    }()
}

// MARK: - Core Data
extension DataManager {

    func applicationDocumentDirectory() -> URL? {
        return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier:"group.shiningdevelopers.h2o")
    }

    func managedObjectContext() -> NSManagedObjectContext {
        return persistentContainer.viewContext
    }

    func reset() {
        managedObjectContext().reset()
    }

    func saveContext () {
        let context = managedObjectContext()
        if context.hasChanges {
            do {
                try context.save()
            } catch let error as NSError {
                // Replace this implementation with code to handle the error appropriately.
                // Log this error for now to be able to glean more information
                print("Could not save. \(error), \(error.userInfo)")
            }
        }
    }
}

Следующие сценарии работают: - У пользователя запущены и приложение watchOS, и приложение iOS - Пользователь вносит изменения в данные в приложении для часов - Это изменение отражается в приложении iOS - У пользователя работает только приложение iOS - Пользователь вносит изменения в данные в приложении iOS - Пользователь открывает приложение watchOS из завершенного - Изменения отражаются в приложении watchOS

Следующий сценарий не работает - У пользователя запущено приложение watchOS и приложение iOS - Пользователь вносит изменения в данные в приложении iOS - Никакие изменения не попадают в приложение watchOS, даже после длительного ожидания - Только если я принудительно закрываю приложение и перезапускаю, происходит синхронизация

В случаях, когда синхронизация прошла успешно, я правильно вижу следующие журналы:

CoreData: debug: CoreData+CloudKit: 
-[PFCloudKitImporterZoneChangedWorkItem newMirroringResultByApplyingAccumulatedChanges:]_block_invoke_2(243): <PFCloudKitImporterZoneChangedWorkItem: 0x16530fb0> { ( "<CKRecordZoneID: 0x1656a920; zoneName=com.apple.coredata.cloudkit.zone, ownerName=__defaultOwner__>" ) } - Importing updated records: ( "<CKRecord: 0x16526280; recordType=CD_LogEntry, values={\n "CD_day" = 15;\n "CD_entityName" = LogEntry;\n "CD_glassesGoal" = 8;\n "CD_glassesLogged" = 13;\n "CD_lastModified" = "2019-09-15 18:56:08 +0000";\n "CD_month" = 9;\n "CD_year" = 2019;\n}, recordChangeTag=7d, recordID=2180D6A3-ACFC-4421-8CAF-6EE288DAAC2E:(com.apple.coredata.cloudkit.zone:defaultOwner)>" ) Deleted RecordIDs: {
}

Однако в последнем сценарии я не вижу никаких логов, все совершенно тихо. Я использую один и тот же код CoreData/CloudKit в версиях приложения для iOS и watchOS. Я также использую NSFetchedResultsController, чтобы убедиться, что мой пользовательский интерфейс обновлен, и, похоже, он работает с приложением iOS, но не с приложением watchOS. Не уверен, был ли какой-то шаг, который я пропустил при настройке расширения для часов.

У кого-нибудь получилась синхронизация с watchOS для работы? Любая помощь будет оценена.

3 ответа

У меня была такая же проблема, и я смог ее решить, настроив описание постоянного хранилища по умолчанию вместо определения нового:

lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentCloudKitContainer(name: "Model")
        // Change this variable instead of creating a new NSPersistentStoreDescription object
        guard let description = container.persistentStoreDescriptions.first else {
            fatalError("No Descriptions found")
        }

        let cloudStoreUrl = applicationDocumentDirectory()!.appendingPathComponent("product.sqlite")

        description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        description.setOption(true as NSObject, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
        description.url = cloudStoreUrl

        container.loadPersistentStores(completionHandler: { storeDescription, error in
            if let error = error as NSError? {
                print("Error loading store. \(error)")
            }
        })

        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.automaticallyMergesChangesFromParent = true

        try? container.viewContext.setQueryGenerationFrom(.current)
        container.viewContext.transactionAuthor = transactionAuthorName

        return container
    }()

Вы включили context.automaticallyMergesChangesFromParent в вашем контексте пользовательского интерфейса?

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

context.automaticallyMergesChangesFromParent = true

Вы пробовали добавить наблюдателя вроде:

// Observe Core Data remote change notifications.
    NotificationCenter.default.addObserver(
        self, selector: #selector(type(of: self).storeRemoteChange(_:)),
        name: .NSPersistentStoreRemoteChange, object: container)

А затем наблюдать за изменениями и обновлять пользовательский интерфейс в это время?

Другие вопросы по тегам