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)
А затем наблюдать за изменениями и обновлять пользовательский интерфейс в это время?