简体   繁体   English

iCloud NSUbiquitousKeyValueStore 初始同步/访问延迟 - 如何处理?

[英]iCloud NSUbiquitousKeyValueStore initial sync/access delay - how to handle?

I'm using NSUbiquitousKeyValueStore to store some app settings.我正在使用 NSUbiquitousKeyValueStore 来存储一些应用程序设置。 My logic is: when I save data locally, I save it to NSUbiquitousKeyValueStore also as a backup.我的逻辑是:当我在本地保存数据时,我也将它保存到 NSUbiquitousKeyValueStore 作为备份。 When I need settings, I read locally and I only use iCloud key-value store if no data is found locally (after app is reinstalled, for example).当我需要设置时,我会在本地读取,并且只有在本地找不到数据时才使用 iCloud 键值存储(例如,在重新安装应用程序之后)。 If user has several devices sharing one icloud id, he can write settings on one device and download them to another (I warn him about rewrite).如果用户有多个设备共享一个 icloud id,他可以在一台设备上编写设置并将它们下载到另一台设备(我警告他重写)。

I have a strange issue.我有一个奇怪的问题。 Steps:脚步:

  1. Installed an app and save its data to NSUbiquitousKeyValueStore.安装了一个应用程序并将其数据保存到 NSUbiquitousKeyValueStore。 Made sure data is there.确保数据在那里。
  2. Removed the app (assuming data is still persists in iCloud).删除了应用程序(假设数据仍然存在于 iCloud 中)。
  3. Waited several minutes just in case, then installed and launched the app from inside Xcode.等了几分钟以防万一,然后从 Xcode 中安装并启动了应用程序。
  4. Tried to read a settings key using [[NSUbiquitousKeyValueStore defaultStore] dataForKey: @"mykeyname"] - sometimes it's ok, but sometimes key is not found!尝试使用 [[NSUbiquitousKeyValueStore defaultStore] dataForKey: @"mykeyname"] 读取设置键 - 有时没问题,但有时找不到键!
  5. Waited for 15 seconds, tried again.等了 15 秒,再试一次。 Success.成功。 Confused.使困惑。

So it seems like ios needs some time to make remote key-value storage for my app available locally for dataForKey: call.因此,ios 似乎需要一些时间来为我的应用程序在本地提供远程键值存储以供 dataForKey: 调用。 If I'd wrote such a system (actually I did - some time ago, in another life) there obviously must be a delay before asking and receiving a key-value data.如果我写了这样一个系统(实际上我写了 - 前一段时间,在另一个生活中)在询问和接收键值数据之前显然必须有一个延迟。 So I'd like to have some notification saying: "we finished downloading/syncing key-value storage on first start" or something similar.所以我想有一些通知说:“我们在第一次启动时完成了下载/同步键值存储”或类似的东西。

As far as I understand I can work with NSUbiquitousKeyValueStore in main thread synchronously (which is convenient for me).据我所知,我可以在主线程中同步使用 NSUbiquitousKeyValueStore (这对我来说很方便)。 But [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returns a valid url, and then I get "key isn't found".但是 [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] 返回一个有效的 url,然后我得到“找不到密钥”。 So I can't rely on it.所以我不能依赖它。 Is there a way to be sure NSUbiquitousKeyValueStore works an is downloaded?有没有办法确保 NSUbiquitousKeyValueStore 工作并下载? It's important especially with slow internet.这很重要,尤其是在互联网速度较慢的情况下。

UPDATE更新

Adding [[NSUbiquitousKeyValueStore defaultStore] synchronize] (as written in apple docs) to init and load was helped a little.将 [[NSUbiquitousKeyValueStore defaultStore] 同步](如苹果文档中所写)添加到初始化和加载中有所帮助。 Still there are many questions to iCloud. iCloud 仍然有很多问题。

Yesterday I've successfully saved data to the key-value store on phone 1 and restored on phone 2. Today I've deleted app on phone 2 and tried to restore the data.昨天我已经成功地将数据保存到手机 1 上的键值存储并在手机 2 上恢复。今天我删除了手机 2 上的应用程序并尝试恢复数据。 But even [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returned valid URL and I called [[NSUbiquitousKeyValueStore defaultStore] synchronize] I get nil when call dataForKey: MY_DATA_KEY.但是即使 [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] 返回了有效的 URL 并且我调用了 [[NSUbiquitousKeyValueStore defaultStore] synchronize] 我在调用 dataForKey: MY_DATA_KEY 时得到 nil。

When I tried to restore data from icloud on phone 1 (app is still installed) it succeeds, but when I reinstalled on this phone the app restore doesn't succeed any more.当我尝试从手机 1 上的 icloud 恢复数据(仍安装应用程序)时,它成功了,但是当我在此手机上重新安装时,应用程序恢复不再成功。

Temporary solution is: "turn off iCloud->Documents&Data - turn off and on network - turn on Documents&Data", but also you should wait several minutes, and then it should work.临时解决方案是:“关闭iCloud->文档和数据-关闭并打开网络-打开文档和数据”,但您应该等待几分钟,然后它应该可以工作。

So, questions:所以,问题:

  1. do you have such problems with iCloud?你的iCloud有这样的问题吗?
  2. Is there any way to find out is data not available or just not downloaded yet?有什么方法可以找出数据不可用或尚未下载吗?
  3. Is there any known "latency" of iCloud? iCloud 是否有任何已知的“延迟”? I've heard about 7 seconds, but it's obviously not true.我听说过大约 7 秒,但这显然不是真的。
  4. It seems that when app isn't unistalled updates of iCloud data are pretty fast (seconds), but when you reinstall the app icloud needs several minutes to actualize key-value store.似乎当应用程序未卸载时,iCloud 数据的更新速度非常快(几秒钟),但是当您重新安装应用程序时,icloud 需要几分钟来实现键值存储。 Is there any way to force this process?有没有办法强制这个过程?

PS Below is my CloudHelper for your reference - pretty simple c++ class to write/read binary data to/from iCloud key-value store. PS下面是我的 CloudHelper 供您参考 - 非常简单的 C++ 类,用于向/从 iCloud 键值存储写入/读取二进制数据。 It is not compilable, I've adapted it for SO somewhat to make more clear - removed my engine related code.它不可编译,我已经对其进行了一些调整以使其更加清晰 - 删除了与引擎相关的代码。 Still if you remove MySystem::... calls it works pretty well.尽管如此,如果你删除 MySystem::... 调用它工作得很好。 Except that I mentioned before.除了我之前提到的。

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;
}

UPDATE 2更新 2

It's 2016. Android has become ios's evil twin, the humanity has discovered gravitational waves, Higgs have received his nobel, Microsoft has bought and killed Nokia. 2016年,安卓变成了ios的邪恶双胞胎,人类发现了引力波,希格斯获得了诺贝尔奖,微软收购并杀死了诺基亚。 But iCloud is still as stupid as it was.但是 iCloud 仍然像以前一样愚蠢。

Finally I've made my own stack of network services on several VPS.最后,我在几个 VPS 上制作了自己的网络服务堆栈。 I refused to use third-party services, because most of them are unstable and unpredictable.我拒绝使用第三方服务,因为它们中的大多数都不稳定且不可预测。 And yet I need iCloud.然而我需要 iCloud。 Because another die-born child of apple does not work.因为苹果的另一个死生子不起作用。 SecKeyChain.安全密钥链。 Its service dies when my game starts.当我的游戏开始时,它的服务就停止了。 So I decided to store random UUID in cloud to distinguish users (there is no device id anymore) even after reinstall.所以我决定在云中存储随机 UUID 以区分用户(不再有设备 ID),即使在重新安装后也是如此。 But what could go wrong?但是会出什么问题呢? Everything!一切! I've spend two days to make this stupid s*it to deploy without errors, and now it loses my data from time to time!我花了两天时间让这个愚蠢的 s*it 部署没有错误,现在它不时丢失我的数据!

Thank you Apple, thank, thank, thank!谢谢苹果,谢谢,谢谢,谢谢! La-la-la!啦啦啦! Hip-hip hooray!太好啦! (sounds of circus music, fading into weeping) (马戏音乐的声音,逐渐变成哭泣)

Conclusion结论

Temporary solution is: - call synchronize before get data from key-value store - to be sure it would work "turn off iCloud->Documents&Data - turn off and again on network - turn on Documents&Data", but also you should wait several minutes before iCloud downloads all needed data临时解决方案是: - 在从键值存储中获取数据之前调用同步 - 确保它可以“关闭 iCloud-> 文档和数据 - 关闭并再次在网络上 - 打开文档和数据”,但你也应该等待几分钟之前iCloud 下载所有需要的数据

Note: when app is installed and already worked (saved/loaded) with key-value store updates of iCloud data are pretty fast (7-15 sec), but when you reinstall the app it seems that icloud needs several minutes to actualize key-value store.注意:当应用程序已安装并且已经使用(保存/加载)iCloud 数据的键值存储更新时,速度非常快(7-15 秒),但是当您重新安装应用程序时,icloud 似乎需要几分钟来实现键值-价值存储。

I'd be glad to hear your thoughts, because icloud looks like almost unusable feature.我很高兴听到您的想法,因为 icloud 看起来几乎无法使用。 But I don't want to set up my own server to merely get the same functionality.但我不想设置自己的服务器来获得相同的功能。

I am setting one dummy key to NSUbiquitousKeyValueStore and calling synchronize on app launch.我正在为 NSUbiquitousKeyValueStore 设置一个虚拟键,并在应用程序启动时调用同步。 The result is not 100% solution but somewhat better.结果不是 100% 的解决方案,而是更好一些。 You can try this.你可以试试这个。

Because obviously your app shouldn't hang while waiting for a slow netork.因为显然您的应用程序不应该在等待缓慢的网络时挂起。 It's all in the iCloud Design Guide .这一切都在iCloud 设计指南中

Register for NSUbiquitousKeyValueStoreDidChangeExternallyNotification , call -synchronize, and hopefully a notification should eventually arrive.注册NSUbiquitousKeyValueStoreDidChangeExternallyNotification ,调用NSUbiquitousKeyValueStoreDidChangeExternallyNotification ,希望最终会NSUbiquitousKeyValueStoreDidChangeExternallyNotification通知。

If the data is already up-to-date, I don't think you get a notification, and I don't think there's an wasy way to know how old the data is.如果数据已经是最新的,我认为您不会收到通知,而且我认为没有一种方法可以知道数据的年龄。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM