Core Data+CloudKit не подключается к CloudKit
Я обновляю приложение Core Data до iOS13, чтобы использовать синхронизацию Core Data+CloudKit для поддержки отдельных пользователей на нескольких устройствах. Синхронизация должна происходить автоматически, и на промежуточном этапе моей разработки она действительно сработала. Теперь он не работает, поскольку телеметрия CloudKit не регистрирует никакой активности, и я не могу понять, почему это не работает.
В моих предыдущих версиях приложения я предоставлял небольшое количество строк меток в UserDefaults и разрешал пользователям изменять их, а обновленные версии возвращались в UserDefaults. Это был ярлык, чтобы избежать необходимости управлять вторым объектом Core Data для небольшого количества объектов.
С тех пор я понял, что это не будет работать в реализации с несколькими устройствами, потому что тот факт, что локальная база данных Core Data пуста, больше не означает, что это первое использование для этого пользователя. Вместо этого каждое новое устройство должно обращаться к облачному источнику данных, чтобы найти используемые пользователем строки меток, и использовать значения по умолчанию, предоставленные приложением, только если у пользователя еще нет чего-то другого.
Я следовал инструкциям Apple по настройке основных данных с помощью CloudKit, и сначала синхронизация работала нормально. Но затем я понял, что поведение синхронизации было неправильным, и что вместо предварительного заполнения из строк, хранящихся в UserDefaults, мне действительно нужно было предоставить предварительно заполненную базу данных Core Data (файлы.sqlite). Я реализовал это, и теперь приложение отлично работает локально после копирования связанных файлов.sqlite при первом локальном запуске.
Но по какой-то причине это изменение привело к тому, что синхронизация CloudKit перестала работать. Теперь я не только не получаю никаких автоматических обновлений на других устройствах, но и не получаю результатов в телеметрии панели управления CloudKit, поэтому похоже, что синхронизация CloudKit никогда не запускается. Это странно, потому что локально я получаю уведомления об "удаленных" изменениях, которые только что произошли локально (функция, которую я перечисляю как #selector для уведомления, вызывается локально, когда я сохраняю новые данные локально).
Я не понимаю, в чем проблема / решение. Вот мой соответствующий код.
//In my CoreDataHelper class
lazy var context = persistentContainer.viewContext
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let appName = Bundle.main.infoDictionary!["CFBundleName"] as! String
let container = NSPersistentCloudKitContainer(name: appName)
//Pre-load my default Core Data data (Category names) on first launch
let storeUrl = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!.appendingPathComponent(appName + ".sqlite")
let storeUrlFolder = FileManager.default.urls(for: .applicationSupportDirectory, in:.userDomainMask).first!
if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: appName, withExtension: "sqlite")
let seededDataUrl2 = Bundle.main.url(forResource: appName, withExtension: "sqlite-shm")
let seededDataUrl3 = Bundle.main.url(forResource: appName, withExtension: "sqlite-wal")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
try! FileManager.default.copyItem(at: seededDataUrl2!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-shm"))
try! FileManager.default.copyItem(at: seededDataUrl3!, to: storeUrlFolder.appendingPathComponent(appName + ".sqlite-wal"))
}
let storeDescription = NSPersistentStoreDescription(url: storeUrl)
storeDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
//In the view controllers, we'll listen for relevant remote changes
let remoteChangeKey = "NSPersistentStoreRemoteChangeNotificationOptionKey"
storeDescription.setOption(true as NSNumber, forKey: remoteChangeKey)
container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
//This is returning nil but I don't think it should
print(storeDescription.cloudKitContainerOptions?.containerIdentifier)
return container
}()
//In my view controller
let context = CoreDataHelper.shared.context
override func viewDidLoad() {
super.viewDidLoad()
//Do other setup stuff, removed for clarity
//enable CloudKit syncing
context.automaticallyMergesChangesFromParent = true
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(reportCKchange),
name: NSNotification.Name(
rawValue: "NSPersistentStoreRemoteChangeNotification"),
object: CoreDataHelper.shared.persistentContainer.persistentStoreCoordinator
)
updateUI()
}
@objc
fileprivate func reportCKchange() {
print("Change reported from CK")
tableView.reloadData()
}
Примечание. Я обновил цель до iOS13+.
2 ответа
Я думаю, что недавно созданный NSPersistentStoreDescription
не имеет cloudKitContainerOptions
по умолчанию.
Чтобы установить их, попробуйте:
storeDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: <<Your CloudKit ID>>)
Я также обнаружил, что ВСЕ атрибуты ваших сущностей должны быть необязательными или иметь значение по умолчанию, иначе это просто не сработает. Было бы хорошо, если бы это было яснее в документации...