Is NSUbiquitousKeyValueStore certified to work without a connection? - objective-c

The documentation doesn't seem very clear about this, but can I rely on NSUbiquitousKeyValueStore to persist data locally when the device is not connected, or should I also use a NSUserDefaults?

From the iCloud design doc with regards to Key Value storage
Always effectively available. If a device is not attached to an account, changes created on the device are pushed to iCloud as soon as the device is attached to the account.
(in the table at the bottom of the page)

Related

Saving data on phone in a Cordova app

I am making a mobile app using Cordova and I need to save some sensitive and not so sensitive data inside the phone. I am a bit lost on what is the best way to do it.
I need to save:
A JSON web-token (for authentication).
A response from server (I save this to populate my page in case the GET request fails).
Coordinates information when user is logging data to the app (for later upload to a server from with in the app). These will be many separate logs, and can be large in size for local storage ~5-10 MB.
Till now i have been successfully saving everything I need to the local storage but I don't think that is the correct way to do it. So that is why I need some help in deciding what is the best course to take from security point of view.
Saving server response is just for better UI experience and static in size so I guess local storage is a good option to use.
But web-tokens and GPS logs is sensitive information and I dont want to keep it in the local storage as it is accessible from outside the app.
What other options do I have?
Cordova still doesn't have encrypted storage.
Is saving to files a good approach? This here says that data contained inside cordova.file.applicationStorageDirectory is private to the app.So can I use it to save the logs and the token?
The plugin also lists the file systems for Android and iOS and lists which of those are private.
I am currently working with android phones but want to extend the app to iOS later. I have never worked with file systems and caches before so I am a bit lost.

Proper handling of NSUbiquityKeyValueStore updates across devices?

My app stores a single key-value pair in iCloud using NSUbiquityKeyValueStore, an array of objects. The entire array is saved to iCloud when a change is made to any object in the array. This works great as long as each device has an opportunity to pull down the latest update before a change is made locally. Otherwise the local change can get pushed up to iCloud before other devices' latest updates have been pulled down, and those updates get lost across all devices. Is this my app's shortcoming or iCloud's shortcoming, and how can I prevent this scenario from occurring?
Otherwise the local change can get pushed up to iCloud before other devices' latest updates have been pulled down
I ran into a similar issue this week with a project I'm working on. I just made sure that I didn't push anything up to the iCloud server until I received my first update from iCloud. Also, FWIW, I set a fake key-value pair right after initialization so that it updates immediately.
HackyStack's idea of a local flag is also a good solution; if a change comes in you can ask the user if they want to use it or not. (sorta like how Kindle asks if you want to update to the latest page).
I'm not sure I fully understand the exact issue, but I believe the answer is either a category on NSObject (where you could have a "version" property) to check the "version" of the object OR you need another key-value pair to store on iCloud for "version" that can be compared to one stored locally on the device (lastUpdateVersion) to know where you stand. If you could give me an exact real world example of your problem I could answer better... It could be that you don't even need a "version" but rather a flag (BOOL).
You should read the documentation for -[NSUbiquitousKeyValueStore synchronize]. It gives you a decent idea of when to use it and what its limits are. In particular, pay attention to the fact that it makes no promises on when it actually synchronises the data, and implies that updates are uploaded to iCloud only a couple of times at a minute, at most (and that may apply to the device as a whole, not just your app).
The key-value storage mechanism is intended to be very simple and used only for non-essential data, typically configuration information about your app. You shouldn't store user data in it, basically, or anything that resembles it. For that kind of data, use the file-based iCloud APIs. They're more complicated, but with them you have more insight into the sync state of your data, and most importantly you can be notified of conflicts and provide your own merge handler.
Is this my app's shortcoming or iCloud's shortcoming, and how can I prevent this scenario from occurring?
This is an app shortcoming and expected behaviour from iCloud. You can account for this in various ways, but in general, this won't be easy. Especially with >2 devices, there are scenarios where conflicting changes will never be presented to a device to do resolution, as generally speaking the iCloud behaviour is "last change wins" (see my longer description below). Some thoughts:
instead of using an array of objects, use individual keys for each object. Obviously this depends on the semantics of your app, but if the objects are essentially independent, then this generally will give your app the behaviour it expects 🎉
if all the items are interlinked, then you will have to do your own conflict resolution. The best way to do this will depend heavily on your app + data semantics. E.g. maybe you could add a timestamp to your array, or to some objects in the array. You could use new key names for every save so that all devices eventually get all keys and can resolve conflicts (obviously this could chew through storage quickly!). Resolving conflicts might not be worth doing depending what you're already storing locally to help with this
Background
I recently had reason to research the topic of NSUbiquitousKeyValueStore change conflicts in some (tedious) depth. I found some information in two old WWDC videos that expand on current Apple documentation, specifically WWDC11 Adopting iCloud Storage, part 1 (currently available here, found via here) at locations 17:38 and subsequently (e.g. 19:27). Another is a WWDC12 iCloud Storage Overview talk (here originally via here) at 6:30 and 10:55. I subsequently verified the behaviour described below by running two devices, an iPhone 8 running iOS 15.2 and an iPad Air 2 running iOS 12.4 with a test program and lots of console logging in Xcode. What follows is my best guess of the intended behaviour and mechanism for conflict resolution.
Summary
When an individual key is saved by a device using NSUbiquitousKeyValueStore.default.set(value, forKey: key), a hidden timestamp is included with the key with the device time of that call. If/when the operating system syncs with the iCloud replica of the key value store, it examines the timestamps for each key and, if the iCloud timestamp is earlier in time, it saves the new key value and timestamp into the iCloud key value store. If the key value is saved, devices that are currently registered and online to receive notifications will be notified that this key has changed and can fetch the new value if they wish. If iCloud does NOT save the key value, NO notification will happen on any device, and the change is simply dropped.
Notes
If all devices on this iCloud account are online while in use (caveat low power mode, poor internet connection etc.), the result is generally exactly what you want: the app makes a change, it is saved in iCloud, it propagates to other devices. Notifications happen as expected, if a device has registered for them.
If device A saves a value while it is offline, and another device B later saves a value while it is online, then device A goes online, the change from device A is ignored, as iCloud now has a newer value with a later timestamp. B will never be notified of A's change. However, if A has registered for changes, A will get notified of the newer B value and can then decide if it should re-submit its value.
Because of this "last in wins" behaviour, multiple values that belong together should thus be saved together as a dictionary or array, as suggested in various Apple docs and talks.
Values that don't interact should be saved as individual keys - thus allowing most recent changes from multiple devices to successfully intermingle.
There is no automated way to test these behaviours. Back in Xcode 9 days, it was possible to UI script two simulators to verify sync worked as expected, but that hasn't worked in a while, which leaves manual testing as a poor and tedious substitute.
NSUbiquitousKeyValueStore is a great solution for many scenarios beyond simple app settings. Personally, I'd like to see more keys (e.g. 10k instead of 1k), but the general ease of setup and separated storage from a customer's iCloud quota is generally a joy.
There's no perfect solution in a real world environment where devices are not always reliably connected. Indeed, some customers may intentionally keep, say, an older iPad, mostly offline to save battery between intermittent usage. If you can keep your synced data in small discrete units and save it one value per key, sync will generally work as expected.

iCloud update policy on CoreData in development and production mode

as specified in the subject I am developing un application that made use of CoreData and its built in iCloud feature.
Whenever I connect my devices (an iPad and an iPhone) to xcode and run the application, I can see after few minutes the log of CoreData updating data. In both devices on iCloud section, I can see my application full entitled for iCloud.
First question: how often are iCloud content pushed (or pulled) to devices ? Is there a way to force this ?
Second, I noticed that the devices, once disconnected from Xcode, doesn't update their data anymore.
While if plugged again and run the app, the data get updated.
So the question, is iCloud in development mode (the app is not yet submitted to App Store) working only trough xcode, and if yes why ?
Or am I doing something wrong within the objective-c code itself ?
thanks
The updates are getting pushed to iCloud servers whenever you save CoreData moc.
The updates are getting pushed from iCloud to device automatically. There's no way to force it as far as I know.
It can take from seconds to dozens of minutes for an update from iCloud to appear on your device (also depends on your connection speed). Apple doesn't give any guarantees on when it will actually happen.
I haven't seen any difference in iCloud behavior whether app started fom XCode or not. Check your code and UI updates base on iCloud pushes.

How can I synchronize items with the cloud while my app is in the background?

In my iPad app, the user can enter data online or offline, storing the data in SQLite, and when the user goes online, he hits a "sync" button, whereupon the data will be synced up to the cloud.
If the user enters data offline, the data persists in the local SQLite DB. If the data is not yet synced, I set a badge to the number of records pending sync.
When the user starts up his iPad, he should get some alert on startup saying "There are 5 records pending to sync." Then all the pending records will be synced, without opening my app.
Has anyone come across a similar scenerio? Does anyone have any idea how I can do that?
Before iOS 5.0 it was not possible to synchronize data with your application when the application was not running. However there were some scenarios when the app could e.g finish a download when the App was terminated but this background-processing is rather limited.
Now if you want to sync to the cloud and have the same data available on another iOS device or on a Mac app then I guess iCloud might be exactly what you are looking for.
So a possible scenario with iCloud might look like this:
You have your iCloud enabled App installed on two iPads
On both iPads you are logged in with your iCloud account (this has to match since iCloud is tied to a given Apple ID
When your App stores some data on iPad1 it will automatically be synced to the cloud
On the same time this data is downloaded to the iPad2 (which has to have internet connection) while your application is not yet launched. This is only possible with iCloud
When you now start your App on iPad2 (even if it is now offline) you will have the current data available within your App.
However, if you are running your own server backend, and maybe need more control over the data on the server, iCloud might not (yet) fit your needs there. You might want to check out the documentation or the sample code for iCloud: iCloud for Developers (Apple Developer Account required)
If you chose not to use iCloud then there is currently no way that you can sync data while your application is not running.
Edit:
A scenario without iCloud could look like this:
You enter Data on iPad1 while offline and set the badge to the number of unsynced items
Later you have internet connection, so you start the app and the items can be synced to your server.
Your server stores the newly arrived entries and could now send a Push notification to your App which is installed on iPad2 telling the user that there are n new entries on the server.
From that notification the user could now decide to open the app and the new items could then be downloaded to the iPad2.
If the user dismisses the notification then no data is downloaded until he starts your App the next time.

Transferring images from one iPad to another

I have an app which displays an image when I say some word, and the word is from my dictionary which I had exclusively created for the app.
Now I want that image displayed on my friends iPad simultaneously, when it shows on my iPad. Is there any way for that?
Thanks and regards,
Christy
If both iPads are in the same local network, you can use cocoahttpserver to run a HTTP-Server on every device and announce the services via Bonjour. Once a new picture is available, that devices connects the server on the other devices and informs it, that new data is available. The second device than will connect the server running on the first device and download the picture.
the advantage of this approach: the protocols you are using are well-know and a lot of additionally tools are available.
But you can also have a similar, more downlevel appoach, where you design your own network protocol. In that case I'd use Asyncsocket
Use synchronize image with both ipad using internet or use APN to notify that image is ready.