ICloud + CoreData - как избежать дублирования дублированных данных?
У меня проблема с приложением iCloud shoebox и надеюсь, что кто-то может мне помочь (я провел много часов, борясь с ним зря).
Приложение: - простое приложение стиля библиотеки - содержит набор категорий (Cat1.. CatN), каждый из которых содержит элементы (Item1... ItemM). Я использовал Apple iPhoneCoreDataRecipes для настройки стека iCloud CoreData.
Все работает почти отлично с iCloud за исключением - должно быть множество предварительно заполненных пустых категорий, которые пользователь может начать использовать, как только он открыл приложение в первый раз (он также может быть в автономном режиме в это время). И вот дьявол.
Вот что я делаю - как только мой persistentStoreCoordinator настроен, я отправляю уведомление
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName: @"RefetchAllDatabaseData"
object: self
userInfo: nil];
});
который получен моим MasterViewController. Когда уведомление получено, MasterViewController проверяет количество категорий в хранилище. Если количество доступных категорий равно 0 - вставляются предварительно заполненные категории.
FYI - я использую NSMergeByPropertyObjectTrumpMergePolicy для моего ManagedObjectContext
Проблема: Это хорошо работает для 1-го устройства. Но для второго устройства категории по умолчанию из iCloud часто принимаются позже, чем persistentStoreCoordinator был настроен (и категории по умолчанию, вставленные вторым устройством). В итоге у меня есть два набора категорий с одинаковыми именами на обоих устройствах.
Любые идеи, как это можно решить?
Пробованные решения: Я попытался решить две стратегии. Оба начинаются одинаково. После того, как я позвоню
[moc mergeChangesFromContextDidSaveNotification: note];
Я звоню
[self materializeKeysWithUserInfo: note.userInfo forContext: moc];
большое спасибо Jose Ines Cantu Arrambide от https://devforums.apple.com/thread/126670?start=400&tstart=0 для своего ссылочного кода - По существу
materializeKeysWithUserInfo:forContext:
получить managedObjectIds из note.userInfo и получить соответствующие объекты из ManagedObjectContext, помещая их в словарь.
Стратегия 1:
- Во всех моих категориях есть метки времени создания.
- Вставить из iCloud, получить пары категорий с одинаковым именем, если есть
- Выберите более старые повторяющиеся категории
- перемещать свои объекты в более новые повторяющиеся категории.
- удалить старые повторяющиеся категории
Эта стратегия эффективно удаляет дубликаты на обоих устройствах еще до того, как они появятся в интерфейсе пользователя НО
1) предметы с 1-го устройства теряются на 2-м устройстве - когда они поступают на второе устройство, их родительская категория отсутствует, а поле их категории равно нулю, поэтому я не знаю, где их поставить.
2) за короткое время предметы, потерянные на втором устройстве, также теряются на первом из-за конфликтов.
3) некоторые элементы, происходящие из 2-го устройства, также теряются из-за конфликтов.
Я пытался предпочесть более старые категории против новее, но это не дало эффекта
Стратегия 2:
- Во всех моих категориях есть метки времени создания.
- Все категории имеют устаревшее логическое поле, установленное в NO при создании
- Вставить из iCloud, получить пары категорий с одинаковым именем, если есть
- Выберите более старые повторяющиеся категории
- перемещать свои объекты в более новые повторяющиеся категории.
- отметьте более старые категории с устаревшим = YES
Эта стратегия почти всегда удаляет дубликаты на обоих устройствах еще до того, как они появятся в интерфейсе пользователя НО
большинство (или все) элементов с обоих устройств теряются из-за кучки конфликтов по категориям и элементам.
Некоторые заключительные мысли:
Похоже, что эта стратегия не работает, поскольку мы запускаем одновременное изменение содержимого обоих устройств, тогда как iCloud не подходит для такого шаблона.
В моих тестах у меня одновременно работало оба устройства. Я не могу игнорировать случай, когда счастливый пользователь, который только что купил его 2-й iDevice, устанавливает мое приложение на 2-ом устройстве (с первым устройством, использующим приложение), и потерял все свои предметы за считанные минуты.
Любые идеи, как эта ситуация может быть решена? Как вы думаете, iCloud + CoreData готов к производству?
Стратегия 3
Я попытался поместить предварительно заполненную базу данных (скопировать ее из пакета) в соответствующий путь. Это частично сработало - у меня больше нет дублированных категорий. НО элементы, добавленные в предварительно заполненные категории, не синхронизируются между устройствами.
iCloud не знает данные, которые существуют в базе данных до установки iCloud - мое второе устройство получает элементы, вставленные на 1-е устройство в предварительно заполненных категориях, с категорией = nil.
Элементы дополнительных категорий (а также сами категории), вставленные в хранилище после настройки iCloud, правильно синхронизируются.
Ответы
Ответ 1
Стратегия 1 с некоторыми изменениями оказалась рабочим решением (с некоторыми недостатками).
Условные обозначения:
- 1st device - началось онлайн без какого-либо контента в iCloud
- 2nd device - запускается позже первого и OFFLINE. Затем он становится онлайн после добавления некоторых элементов.
Итак, обновленная стратегия:
-
Во всех моих категориях создаются метки времени
-
Категории не могут быть переименованы (добавлены или удалены - это очень важно)
-
Все мои объекты имеют поле categoryName строки, которое получает свое значение при создании элемента и обновляется всякий раз, когда элемент перемещается в другую категорию - эта избыточная информация помогает добиться успеха;
При вставке новых категорий:
-
При вставке из iCloud я получаю пары категорий с одинаковым именем, если есть
-
Выберите более новые повторяющиеся категории (у них, скорее всего, будет меньше предметов, чем у старых, поэтому у нас будет меньше танец в iCloud)
-
Переместите свои элементы, если они есть, в старые повторяющиеся категории
-
Удалить более новые повторяющиеся категории
При вставке новых Элементов - если элемент принадлежит категории удален:
-
CoreData пытается объединить его, а не удается, поскольку нет родительской категории (много ошибок в консоли). Он обещает вставить его позже.
-
Через некоторое короткое время он объединяется и вставляет элемент в хранилище, но с NIL категория
-
Здесь мы выбираем наш элемент вверх, узнаем его родительскую категорию из categoryName и помещаем в нужную категорию
VOILA! - нет дубликатов и всех счастливых
Несколько примечаний:
- Я получаю танец предметов, принадлежащих 2-му устройству (те, которые будут поставляться с нулевой категорией к 1-му устройству) на обоих устройствах. Через пару минут все стабилизируется.
- Ничего не потеряно, но
- Танец происходит только при первой синхронизации iCloud второго (или любого другого последующего устройства).
- Если второе устройство запускается онлайн в первый раз, вероятность того, что случай с дублирующимися категориями будет отображаться только на 25% - протестирован на 3G-соединении - так что танец не должен влиять на большинство пользователей li >