我正在使用NSUbiquitousKeyValueStore存储一些应用程序设置。我的逻辑是:在保存数据到本地时,同时将其保存到NSUbiquitousKeyValueStore作为备份。需要设置时,我首先从本地读取,如果本地没有找到数据(例如,应用程序重新安装后),我才会使用iCloud键值存储。如果用户有多个共享一个iCloud ID的设备,他可以在一个设备上编写设置,并将它们下载到另一个设备上(我会警告他关于覆盖现有数据)。
我有一个奇怪的问题。步骤:
- 安装应用程序并将其数据保存到NSUbiquitousKeyValueStore。确保数据存在。
- 删除应用程序(假定数据仍然保留在iCloud中)。
- 等待几分钟,然后从Xcode内部安装和启动应用程序。
- 尝试使用[[NSUbiquitousKeyValueStore defaultStore] dataForKey:@"mykeyname"]读取设置键 - 有时可以,但有时找不到键!
- 等待15秒,然后再次尝试。成功了。困惑了。
因此,似乎iOS需要一些时间才能使远程键值存储对于dataForKey:调用在本地可用。 如果我写过这样的系统(实际上我做过-在另一生命中的某个时候),显然在请求和接收键值数据之前必须有一定的延迟。因此,我希望有一些通知说:“我们在第一次启动时完成了下载/同步键值存储”或类似的东西。
据我所了解,我可以在主线程同步地使用NSUbiquitousKeyValueStore(这对我很方便)。但是[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]会返回有效的URL,然后我会得到“未找到密钥”。所以我不能依赖它。有没有办法确保NSUbiquitousKeyValueStore正在工作并已下载?特别是在网络缓慢的情况下这很重要。
更新
添加[[NSUbiquitousKeyValueStore defaultStore] synchronize](如苹果文档中所述)到init和load有所帮助。仍然有关于iCloud的许多问题。
昨天我成功将数据保存到手机1的键值存储中,并在手机2上还原。 今天我删除了手机2上的应用程序并尝试还原数据。但是,即使[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]返回有效的URL并调用[[NSUbiquitousKeyValueStore defaultStore] synchronize],在调用dataForKey:MY_DATA_KEY时,我也会得到nil。
当我尝试从iCloud上的手机1还原数据时(应用程序仍然安装),它成功了,但是当我重新安装此应用程序时,恢复不再成功。
临时解决方案是:“关闭iCloud->文档和数据-关闭并打开网络-打开文档和数据”,但是您还应等待几分钟,然后它应该正常工作。
所以问题是:
- 你是否也遇到了iCloud的这些问题?
- 有没有办法找出数据是不可用还是还未下载?
- iCloud是否存在已知的“延迟”?我听说过7秒,但显然不是真的。
- 似乎当应用程序未被卸载时,iCloud数据的更新非常快(几秒钟),但当重新安装应用程序时,icloud需要几分钟才能实际化键值存储。有没有办法强制执行此过程?
P.S. 以下是我的CloudHelper供您参考-一个相当简单的c ++类,可用于将二进制数据写入/从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's 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的邪恶孪生兄弟,人类发现了引力波,希格斯获得了他的诺贝尔奖,微软买下并扼杀了诺基亚。但iCloud仍然像以前一样愚蠢。
最终,我在多个VPS上构建了自己的网络服务堆栈。我拒绝使用第三方服务,因为它们大多不稳定且难以预测。但我需要iCloud。因为苹果的另一个失败之作SecKeyChain不起作用。当我的游戏开始时,其服务就会死掉。因此,我决定在云中存储随机UUID以区分用户(不再有设备ID),即使重新安装后也能保留这些信息。但是,什么可能会出错呢?一切都可能!我花了两天时间让这个愚蠢的东西在没有错误的情况下部署,现在却时不时地丢失我的数据!
谢谢你,Apple,谢谢,谢谢,谢谢!啦啦啦!万岁!(音乐声逐渐消失为哭泣声)