I'm having nothing but trouble with NSUbiquitousKeyValueStore and getting it to reliably sync across two devices. It's starting to drive me mad. Mad.
I watch my code make a call to:
[[NSUbiquitousKeyValueStore defaultStore] setObject:_someData forKey:@"SomeData"];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
But more times than not, the data never shows up on the other device.
I've seen it sync within a few seconds and other times I've waited for an hour and never seen a sync. I plug the devices into xcode and start debugging and sometimes the data suddenly pops up and other times it doesn't. Sometimes it does.
After making this call, quit and restarting the App, I see the old data if I read it, not the new data I supposedly just posted. On the same device.
When the App starts up, I make a call to:
_someData = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"SomeData"];
I'm assuming there is no init code that needs to be run. No example I've found does.
When it works, it works quite well.
Then tonight I was looking at the device logs and found this right after my last exit (and attempt to send data):
Jul 8 21:32:03 unknown syncdefaultsd[17296] <Warning>: Error writing storage for com.mycompany.myapp to /private/var/mobile/Applications/4AC8C56E-6060-408B-84F9-F7EC336221D9/Library/SyncedPreferences/com.mycompany.myapp.plist: Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed. (Cocoa error 4.)" UserInfo=0xde80d40 {NSFilePath=/private/var/mobile/Applications/4AC8C56E-6060-408B-84F9-F7EC336221D9/Library/SyncedPreferences, NSUnderlyingError=0xde80b20"The operation couldn’t becompleted. No such file or directory"}
That path is not the valid path from my Apps sandbox (wrong GUID), so I wonder if this is the root of my problems. I do delete and re-install the App a lot during development, so I wonder if there is some old path cached somewhere and it's trying to sync that data. Is there a way to clear it is?
I know all my entitlements are set up correctly since it does work sometimes.
[[NSUbiquitousKeyValueStore defaultStore] synchronize]
is returning YES
.
Has anyone else seen this problem or that error? Is there something else I need to be doing when my App starts up and when I send data to the cloud? According to the docs and examples, it seems pretty easy.
I use this at launch and get immediate iCloud values or updateKVStoreItems: gets called in < 5 seconds.
- (void) initializeiCloud
{
NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateKVStoreItems:) name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:store];
if ([store boolForKey:@"fakeSyncBit"])
{
NSLog(@"in initiCloud setting syncbit NO");
[store setBool:NO forKey:@"fakeSyncBit"];
}
else
{
NSLog(@"in initiCloud setting syncbit YES");
[store setBool:YES forKey:@"fakeSyncBit"];
}
[store synchronize];
NSLog(@"icloud store is synchronized. updateKVStoreItems: should be called shortly");
}
iCloud key-value store is not intended for use with data which requires regular frequent synchronization and it appears Apple does not guarantee the timeliness of these operations. Apple states:
The key-value store is intended for storing data that changes infrequently. If the apps on a device make frequent changes to the key-value store, the system may defer the synchronization of some changes in order to minimize the number of round trips to the server. The more frequently apps make changes, the more likely it is that later changes will be deferred and not show up on other devices right away.
If it is okay that your app does not synchronize immediately and your interest in this is to speed testing/debugging then it appears this will be a necessary frustration unless you change your technique.
The documentation for NSUbiquitousKeyValueStore states how synchronize works in detail. I think the assumption from Roger is that when you call:
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
that it will upload the data straight away (assuming a network connection). This is NOT the case. Calling synchronize
simply takes the keys and values you have set in memory and persists them to the local on-disk cache. Once this is done, the system then notifies iCloud that there is data ready to be uploaded. The key piece of information is mentioned below the synchronize
method documentation:
This method does not force new keys and values to be written to iCloud. Rather, it lets iCloud know that new keys and values are available to be uploaded. Do not rely on your keys and values being available on other devices immediately. The system controls when those keys and values are uploaded. The frequency of upload requests for key-value storage is limited to several per minute.
Link to documentation
In short, the system controls when your keys and values are uploaded to iCloud and calling synchronize
simply stores a local cache of them and notifies iCloud of new data waiting to be uploaded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With