Правильная обработка обновлений NSUbiquityKeyValueStore на разных устройствах?

Мое приложение хранит одну пару ключ-значение в iCloud, используя NSUbiquityKeyValueStore, массив объектов. Весь массив сохраняется в iCloud при внесении изменений в любой объект в массиве. Это прекрасно работает, если у каждого устройства есть возможность отменить последнее обновление, прежде чем вносить изменения локально. В противном случае локальное изменение может быть передано в iCloud до того, как будут обновлены последние обновления других устройств, и эти обновления будут потеряны на всех устройствах. Это недостаток моего приложения или недостаток iCloud, и как я могу предотвратить этот сценарий?

4 ответа

В противном случае локальное изменение может быть передано в iCloud до того, как будут сброшены последние обновления других устройств.

Я столкнулся с подобной проблемой на этой неделе с проектом, над которым я работаю. Я просто позаботился о том, чтобы я ничего не передавал на сервер iCloud, пока не получил свое первое обновление от iCloud. Кроме того, FWIW, я установил поддельную пару ключ-значение сразу после инициализации, чтобы она немедленно обновлялась.

Идея HackyStack о локальном флаге также является хорошим решением; если изменение приходит, вы можете спросить пользователя, хотят ли они его использовать или нет. (вроде как Kindle спрашивает, хотите ли вы обновить до последней страницы).

Вы должны прочитать документацию для -[NSUbiquitousKeyValueStore синхронизации]. Это дает вам хорошее представление о том, когда его использовать и каковы его пределы. В частности, обратите внимание на тот факт, что он не дает никаких обещаний относительно фактической синхронизации данных и подразумевает, что обновления загружаются в iCloud только пару раз в минуту, самое большее (и это может относиться к устройству как в целом, а не только ваше приложение).

Механизм хранения ключ-значение должен быть очень простым и использоваться только для несущественных данных, обычно для информации о конфигурации вашего приложения. Вы не должны хранить пользовательские данные в этом, в принципе, или что-нибудь похожее на это. Для таких данных используйте файловые API-интерфейсы iCloud. Они более сложны, но с их помощью вы сможете лучше понять состояние синхронизации ваших данных, и, что важнее всего, вы сможете получать уведомления о конфликтах и ​​предоставлять свой собственный обработчик слияния.

Является ли это недостатком моего приложения или недостатком iCloud, и как я могу предотвратить возникновение этого сценария?

Это недостаток приложения и ожидаемое поведение от iCloud. Объяснить это можно по-разному, но в целом это будет непросто. Особенно с >2 устройствами существуют сценарии, в которых конфликтующие изменения никогда не будут представлены устройству для разрешения, поскольку, вообще говоря, поведение iCloud - «выигрывает последнее изменение» (см. Мое более подробное описание ниже). Некоторые мысли:

  • вместо использования массива объектов используйте отдельные ключи для каждого объекта. Очевидно, это зависит от семантики вашего приложения, но если объекты по существу независимы, то это обычно дает вашему приложению ожидаемое поведение.
  • если все пункты взаимосвязаны, то вам придется заниматься разрешением конфликта самостоятельно. Лучший способ сделать это будет сильно зависеть от семантики вашего приложения и данных. Например, возможно, вы могли бы добавить метку времени в свой массив или к некоторым объектам в массиве. Вы можете использовать новые имена ключей для каждого сохранения, чтобы все устройства в конечном итоге получали все ключи и могли разрешать конфликты (очевидно, это может быстро прожечь хранилище!). Разрешение конфликтов может не стоить делать в зависимости от того, что вы уже храните локально, чтобы помочь с этим.

Фон

Недавно у меня была причина исследовать тему конфликтов изменений с некоторой (утомительной) глубиной. Я нашел некоторую информацию в двух старых видеороликах WWDC, которые расширяют текущую документацию Apple, в частности, WWDC11 Adopting iCloud Storage, часть 1 ( в настоящее время доступно здесь , можно найти здесь) в местах 17:38 и позже (например, 19:27). Другой — обзор хранилища iCloud на WWDC12 ( первоначально здесь через здесь) в 6:30 и 10:55. Впоследствии я проверил поведение, описанное ниже, запустив два устройства: iPhone 8 под управлением iOS 15.2 и iPad Air 2 под управлением iOS 12.4 с тестовой программой и большим количеством журналов консоли в Xcode. Далее следует мое лучшее предположение о предполагаемом поведении и механизме разрешения конфликтов.

Резюме

Когда индивидуальный ключ сохраняется устройством с помощью NSUbiquitousKeyValueStore.default.set(value, forKey: key), скрытый ключ включается с устройством времени этого звонка. Если/когда операционная система синхронизируется с репликой iCloud хранилища значений ключей, она проверяет s для каждого ключа и, если iCloud находится раньше по времени, сохраняет новое значение ключа и в хранилище значений ключей iCloud. Если значение ключа сохранено, устройства, которые в настоящее время зарегистрированы и подключены к сети для получения уведомлений, будут уведомлены об изменении этого ключа и смогут получить новое значение, если захотят. Если iCloud НЕ сохраняет значение ключа, НИКАКОЕ уведомление не будет происходить ни на одном устройстве, и изменение просто отбрасывается.

Заметки

  • Если все устройства в этой учетной записи iCloud подключены к сети во время использования (осторожно, режим низкого энергопотребления, плохое подключение к Интернету и т. д.) , результат, как правило, именно тот, который вам нужен: приложение вносит изменения, они сохраняются в iCloud, они распространяются на другие устройства. устройства. Уведомления приходят, как и ожидалось, если устройство зарегистрировано для них.
  • Если устройство A сохраняет некоторое время, когда оно находится в автономном режиме, а другое устройство B позже сохраняет какое-то время, пока оно находится в сети, затем устройство A подключается к сети, изменение с устройства A игнорируется, так как теперь iCloud имеет более новую версию с более поздней версией. timestamp. B никогда не будет уведомлен об изменении A. Однако, если A зарегистрировался для внесения изменений, A получит уведомление о новых изменениях B. valueи затем может решить, следует ли повторно отправить свое значение.
  • Из-за этого поведения «выигрывает последним» несколько значений, которые принадлежат друг другу, должны быть сохранены вместе в виде словаря или массива, как это предлагается в различных документах и ​​выступлениях Apple.
  • Значения, которые не взаимодействуют друг с другом, должны быть сохранены как отдельные ключи, что позволит успешно смешивать самые последние изменения с нескольких устройств.
  • Не существует автоматизированного способа проверить это поведение. Еще в Xcode 9 дней можно было написать сценарий пользовательского интерфейса для двух симуляторов, чтобы убедиться, что синхронизация работает должным образом, но это не сработало некоторое время, что оставляет ручное тестирование плохой и утомительной заменой.
  • NSUbiquitousKeyValueStore— отличное решение для многих сценариев, выходящих за рамки простых настроек приложения. Лично я хотел бы видеть больше ключей (например, 10 КБ вместо 1 КБ), но общая простота настройки и отдельное хранилище от квоты клиента iCloud, как правило, радует.

Не существует идеального решения в реальной среде, где устройства не всегда надежно подключены. Действительно, некоторые клиенты могут намеренно держать, скажем, старый iPad в основном в автономном режиме, чтобы сэкономить заряд батареи между прерывистыми использованиями. Если вы можете хранить синхронизированные данные небольшими дискретными единицами и сохранять по одному значению для каждого ключа, синхронизация в целом будет работать должным образом.

Я не уверен, что полностью понимаю точную проблему, но я верю, что ответом является либо категория в NSObject (где у вас может быть свойство "версия"), чтобы проверить "версию" объекта, либо вам нужен другой ключ-значение пару для хранения в iCloud для "версии", которую можно сравнить с одной, хранящейся локально на устройстве (lastUpdateVersion), чтобы узнать, где вы находитесь. Если бы вы могли дать мне точный реальный пример вашей проблемы, я мог бы ответить лучше... Возможно, вам даже не нужна "версия", а скорее флаг (BOOL).

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