Я использую NSUbiquitousKeyValueStore для хранения некоторых настроек приложения. Моя логика: когда я сохраняю данные локально, я сохраняю его в NSUbiquitousKeyValueStore также в качестве резервной копии. Когда мне нужны настройки, я читаю локально, и я использую только хранилище ключей iCloud, если данные не найдены локально (после того, как приложение переустановлено, например). Если у пользователя есть несколько устройств, разделяющих один idloud id, он может записывать настройки на одном устройстве и загружать их в другой (я предупреждаю его о перезаписи).
У меня странная проблема. Шаги:
- Установил приложение и сохранил его данные в NSUbiquitousKeyValueStore. Убедитесь, что данные есть.
- Удалено приложение (предполагается, что данные все еще сохраняются в iCloud).
- Ожидалось несколько минут на всякий случай, затем установил и запустил приложение изнутри Xcode.
- Пробовал читать ключ настроек, используя [[NSUbiquitousKeyValueStore defaultStore] dataForKey: @ "mykeyname" ] - иногда это нормально, но иногда ключ не найден!
- Ждал 15 секунд, попробовал еще раз. Успех. Confused.
Итак, похоже, что ios нужно некоторое время, чтобы сделать удаленное хранилище ключей для моего приложения доступным локально для dataForKey: call. Если бы я написал такую систему (на самом деле я сделал - некоторое время назад, в другой жизни), очевидно, должна быть задержка перед запросом и получением данных с ключом. Поэтому я хотел бы получить уведомление о том, что "мы закончили загрузку/синхронизацию хранилища ключей при первом запуске" или что-то подобное.
Насколько я понимаю, я могу работать с NSUbiquitousKeyValueStore в основном потоке синхронно (что мне удобно). Но [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil] возвращает действительный url, а затем я получаю "ключ не найден". Поэтому я не могу полагаться на это. Есть ли способ убедиться, что NSUbiquitousKeyValueStore работает, загружен? Это особенно важно при медленном доступе в Интернет.
UPDATE
Добавление [[NSUbiquitousKeyValueStore defaultStore] synchronize] (как написано в яблочных документах) для инициализации и загрузки помогло немного. Все еще есть много вопросов для iCloud.
Вчера я успешно сохранил данные в хранилище ключей на телефоне 1 и восстановил по телефону 2. Сегодня я удалил приложение на телефоне 2 и попытался восстановить данные. Но даже [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil] возвратил действительный URL-адрес, и я вызвал [[NSUbiquitousKeyValueStore defaultStore] synchronize] Я получаю nil, когда call dataForKey: MY_DATA_KEY.
Когда я пытался восстановить данные из icloud на телефоне 1 (приложение все еще установлено), это удается, но когда я переустановил на этом телефоне, восстановление приложения больше не удалось.
Временное решение: "выключить iCloud- > Documents & Data - выключить и включить сеть - включить Documents & Data", но также вы должны подождать несколько минут, а затем он должен работать.
Итак, вопросы: 1. У вас есть такие проблемы с iCloud? 2. Есть ли способ узнать, недоступны данные или еще не загружены? 3. Есть ли известная "латентность" iCloud? Я слышал около 7 секунд, но это, очевидно, не так. 4. Похоже, что когда приложение не является однозначным, обновления данных iCloud довольно быстрые (в секундах), но при повторной установке приложения icloud требуется несколько минут для актуализации хранилища ключей. Есть ли способ заставить этот процесс?
P.S. Ниже мой CloudHelper для вашей справки - довольно простой класс С++ для записи/чтения двоичных данных в/из хранилища ключей iCloud. Он не компилируется, я немного адаптировал его для SO, чтобы сделать более понятным - удалил свой код, связанный с двигателем. Тем не менее, если вы удалите MySystem::..., это работает очень хорошо. Кроме того, что я упоминал ранее.
class CloudHelper
{
public:
static bool init();
static void deInit();
//save our data to iCloud with
static int saveData(unsigned char* data, int from, int count);
//get our data from iCloud
static unsigned char * loadData(int *retsize, int * retint);
//does iCloud work for us
static bool isEnabled();
//do we have our key in iCloud
static int isAvailable();
static const int RESULT_OK = 0;
static const int RESULT_NO_CONNECTION = 1;
static const int RESULT_NOT_FOUND = 2;
static const int RESULT_SYNC_ERROR = 3;
private:
static bool enabled;
static NSURL *ubiq;
};
bool CloudHelper::enabled = false;
NSURL *CloudHelper::ubiq = NULL;
#define MY_DATA_KEY @"my_data_key"
int CloudHelper::saveData(unsigned char* data, int from, int count)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
NSData *d = [[[NSData alloc] initWithBytes:(data + from) length:count] autorelease];
[[NSUbiquitousKeyValueStore defaultStore] setData:d forKey: MY_DATA_KEY)];
if ([[NSUbiquitousKeyValueStore defaultStore] synchronize] != TRUE)
return RESULT_SYNC_ERROR;
return RESULT_OK;
}
return RESULT_NO_CONNECTION;
}
unsigned char * CloudHelper::loadData(int *retsize, int * retint)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
{
if (retsize != NULL)
*retsize = d.length;
if (retint != NULL)
*retint = RESULT_OK;
return d.bytes;
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NOT_FOUND;
}
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NO_CONNECTION;
}
return NULL;
}
int CloudHelper::isAvailable()
{
int result = RESULT_NO_CONNECTION;
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
result = RESULT_OK;
else
result = RESULT_NOT_FOUND;
}
else
result = RESULT_NO_CONNECTION;
return result;
}
void CloudHelper::deInit()
{
enabled = false;
[ubiq release];
}
bool CloudHelper::init()
{
enabled = false;
NSURL *ubiq_ = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
if (ubiq)
{
enabled = true;
ubiq = [ubiq_ retain]; //save for further use
}
else
{
//is implemented elsewhere: this writes a local file with a counter, and if it is < REMINDER_COUNT allows us to show a warning to users
bool allow = MySystem::isAllowToShowDialog();
if (allow)
{
//determines network state with Apple Reachability
if (!MySystem::isNetworkAvailable())
MySystem::showMessageBox(@"Network error"); //No network
else
MySystem::showMessageBox(@"You should log into your iCloud account to be able to backup your settings."); //No login
}
}
return enabled;
}
ОБНОВЛЕНИЕ 2
В 2016 году. Android стал ios злым двойником, человечество обнаружило гравитационные волны, Хиггс получил своего нобеля, Microsoft купила и убила Nokia, все видели отдельно Amber Heard и Дженнифер Лоуренс голыми, отдельно. Но iCloud по-прежнему так глуп.
Наконец, я создал собственный стек сетевых сервисов на нескольких VPS. Я отказался использовать сторонние сервисы, потому что большинство из них нестабильны и непредсказуемы. И все же мне нужен iCloud. Потому что другой умирающий ребенок яблока не работает. SecKeyChain. Его сервис умирает, когда начинается моя игра. Поэтому я решил хранить случайный UUID в облаке, чтобы отличать пользователей (больше нет идентификатора устройства) даже после переустановки. Но что может пойти не так? Все! Я потратил два дня, чтобы сделать это глупое, чтобы оно развертывалось без ошибок, и теперь оно время от времени теряет мои данные!
Спасибо, Apple, спасибо, спасибо, спасибо! Ла-ла-ла! Гип-гип ура! (звуки цирковой музыки, угасающие в плач)