У меня есть приложение, которое использует NSPersistentCloudKitContainer
для синхронизации данных с iCloud. Пользователи сообщали о случайной потере данных, после чего я начал отладку своей реализации.
Часть реализации моего приложения показывает всплывающее окно, которое отображает информацию и имеет кнопку подтверждения. Когда пользователь нажимает эту кнопку, один или несколько экземпляров пользовательского подкласса NSManagedObject
изменяются.
В то же время NSPersistentCloudKitContainer
запускает зеркальный процесс, когда приложение подает в отставку и становится активным. Это происходит, когда пользователь нажимает вышеупомянутую кнопку подтверждения. Это выполняется путем выполнения запросов NSCloudKitMirroringExportRequest
и NSCloudKitMirroringImportRequest
.
Другими словами, в моем приложении данные изменяются (явно через код) и отражаются (неявно через CloudKit) одновременно, и вот где все go не так.
По сути, это то, что происходит:
- Свойство (
NSTimeInterval
) экземпляра NSManagedObject
подкласса увеличивается ( например, 0 + 1 = 1). NSPersistentCloudKitContainer
неявно начинает зеркальное отображение данных, когда приложение становится активным или уходит в отставку. - Во время этого процесса, но до его завершения свойство снова увеличивается (например, 1 + 1 = 2).
- Внутренне процесс зеркального отображения завершается.
На этом этапе можно ожидать, что значение свойства будет соответствовать последнему (локальному) изменению (следовательно, в мой пример 2). Но вместо этого он соответствует начальному приращению (следовательно, в моем примере 1), перезаписывая мое последнее (локальное) изменение, вызывающее потерю данных. По-видимому, NSCloudKitMirroringImportRequest
возвращается в состояние начала процесса зеркалирования, что также можно увидеть в журналах:
CoreData: debug: CoreData+CloudKit: -[PFCloudKitImporterZoneChangedWorkItem applyAccumulatedChanges:error:]
[…]
Importing updated records:
(
"<CKRecord: 0x10225b560; recordID=5458F9C8-7BE2-4563-92DE-650ED4C643F5:(com.apple.coredata.cloudkit.zone:__defaultOwner__), recordChangeTag=23, values={\n \"CD_activity\" = \"3E497A4C-6467-48F4-B7F4-6F1B2B7BC779\";\n \"CD_date\" = \"2020-04-26 07:28:00 +0000\";\n \"CD_duration\" = \"1\";\n \"CD_entityName\" = Mutation;\n}, recordType=CD_Mutation>"
)
На данном этапе я не уверен, является ли это ошибкой в NSPersistentCloudKitContainer
или мое приложение, но оно определенно приводит к ненадежным данным. В настоящее время у меня есть следующие вопросы:
- Почему
NSPersistentCloudKitContainer
(особенно в результате NSCloudKitMirroringImportRequest
) импортирует «обновленные» записи, в то время как эти записи изменились только локально? Другими словами: у меня уже есть последняя версия записей. Поскольку эти «обновленные» записи имеют устаревшие значения, что переписывает мои новые изменения, происходит потеря данных. Пропуск этих обновлений решит мою проблему. Есть ли способ контролировать это поведение? - Кажется, что (явное) изменение данных и (неявное) их зеркальное отображение одновременно вызывает проблемы. В моем случае это происходит автоматически из-за
NSPersistentCloudKitContainer
планирования процесса зеркалирования, когда приложение становится активным или уходит в отставку. Есть ли способ контролировать это поведение? Могу ли я, например, отключить процесс зеркального отображения, когда приложение становится активным или уходит в отставку? Я заметил NSCloudKitMirroringDelegateOptions
по умолчанию, среди прочего, automaticallyScheduleImportAndExportOperations:YES
. Могу ли я изменить это? И могу ли я сам инициировать процесс зеркалирования в тот момент, когда мутация данных менее вероятна?
Наконец, подтверждение вышесказанного можно найти в том факте, что когда я не подключен к Inte rnet (следовательно, CloudKit не может отражать данные), все работает как положено.