iCloud Key-Value Storage

The iCloud Key-Value Storage is the Apple analog to well-known unity PlayerPrefs system. With the only one small difference, values that you place in key-value storage are available to every instance of your app on all of a user’s various devices. If one instance of your app changes a value, the other instances see that change and can use it to update their configuration.

Enable Key-Value Storage

Before you can use key-value storage you need to enable iCloud API under Foundation framework settings, as shown in a picture below:

And that is all the setup you need.

You may notice that "iCloud Capability" is added to the XCode requirements. If you want to understand more of what is happening "under the hood"  you can read more about  Request Access to iCloud Using Xcode Capabilities.

Prepare Your App to Use the iCloud Key-Value Store

Any device running your app, and attached to a user’s iCloud account, can upload key-value changes to that account. To keep track of such changes, register for the StoreDidChangeExternallyNotification event of the ISN_NSUbiquitousKeyValueStore class during app launch.  Then, to ensure your app starts off with the newest available data, obtain the keys and values from iCloud by calling the Synchronize method. (You never need to call the Synchronize method again during your app’s life cycle, unless your app design requires fast-as-possible upload to iCloud after you change a value.)

The following code snippet shows how to prepare your app to use the iCloud key-value store. Place code like this within your app initialization flow:

ISN_NSUbiquitousKeyValueStore.StoreDidChangeExternallyNotification.AddListener((notification) => {
    // get changes that might have happened while this
    // instance of your app wasn't running
});

ISN_NSUbiquitousKeyValueStore.Synchronize();

In your handler method for the ISN_NSStoreDidChangeExternallyNotification notification, examine the user info dictionary and determine if you want to write the changes to your app’s local user defaults database. It’s important to decide deliberately whether or not to change your app’s settings based on iCloud-originated changes, as explained next.

Resolving Key-Value Conflicts

When a device attached to an iCloud account tries to write a value to key-value storage, iCloud checks to see if any recent changes have been made to the key-value store by other devices. If no changes have been made recently, the ISN_NSUbiquitousKeyValueStore writes the pending local changes to the server. If changes have been made recently, it does not write the local values to the server. Instead, it generates a  StoreDidChangeExternallyNotification notification to force your app to update itself based on the updated server values.

When your handler for the StoreDidChangeExternallyNotification notification runs, validate the new values coming from the server to be sure that they make sense. If the new data does not match the local state of your app, consider whether data coming from another device might be out of date. For example, if the user is on level 13 of a game on one device and on level 1 on another one, the instance of the game set to level 13 might want to write its changes again.

Read and Write Data to Key-Value Storage

Similar to Unity PlayerPrefs you write data using the following methods of ISN_NSUbiquitousKeyValueStore class:

  • SetString
  • SetInt
  • SetBool
  • SetFloat
  • SetLong
  • SetULong
  • SetBytes
  • SetDateTime
  • SetObject

And you can get ISN_NSKeyValueObject with the KeyValueStoreObjectForKey method. See some read / write examples below:

//string
ISN_NSUbiquitousKeyValueStore.SetString("string_key", "string value");
ISN_NSKeyValueObject kvObject = ISN_NSUbiquitousKeyValueStore.KeyValueStoreObjectForKey("string_key");
Debug.Log("key: " + kvObject.Key + " value: " + kvObject.StringValue);

//float
ISN_NSUbiquitousKeyValueStore.SetFloat("float_key", 3.14f);
ISN_NSKeyValueObject kvObject = ISN_NSUbiquitousKeyValueStore.KeyValueStoreObjectForKey("float_key");
Debug.Log("key: " + kvObject.Key + " value: " + kvObject.FloatValue);

//bytes
byte[] data = Encoding.UTF8.GetBytes("bytes value");
ISN_NSUbiquitousKeyValueStore.SetBytes("bytes_key", data);
ISN_NSKeyValueObject kvObject = ISN_NSUbiquitousKeyValueStore.KeyValueStoreObjectForKey("bytes_key");
Debug.Log("key: " + kvObject.Key + " value: " + kvObject.BytesArrayValue);

It's also interesting how you can use SetObject method.  Let's say we have a PlayerInfo class that holds basic information about out current player:

[Serializable]
class PlayerInfo
{
   public string Name;
   public int PlayerLevel;
   public List<string> PlaterFreinds;
}

We can save and load a whole class with the ISN_NSUbiquitousKeyValueStore, see the example

PlayerInfo player  = new PlayerInfo();
ISN_NSUbiquitousKeyValueStore.SetObject("player", player);
ISN_NSKeyValueObject kvObject = ISN_NSUbiquitousKeyValueStore.KeyValueStoreObjectForKey("player");
player = kvObject.GetObject<PlayerInfo >("player");
Debug.Log("Player name: " + player.Name);

Consider this as the replacement to setArray & setDictionary method of Apple NSUbiquitousKeyValueStore class.

Data Size Limits for Key-Value Storage

The total space available in your app’s iCloud key-value storage is 1 MB per user. The maximum number of keys you can specify is 1024, and the size limit for each value associated with a key is 1 MB. For example, if you store a single large value of exactly 1 MB for a single key, that fully consumes your quota for a given user of your app. If you store 1 KB of data for each key, you can use 1000 key-value pairs.

The maximum length for a key string is 64 bytes using UTF8 encoding. The data size of your cumulative key strings does not count against your 1 MB total quota for iCloud key-value storage; rather, your key strings (which at maximum consume 64 KB) count against a user’s total iCloud allotment.

Don’t Use Key-Value Storage in Certain Situations

Every app submitted to the App Store or Mac App Store should adopt key-value storage, but some types of data are not appropriate for key-value storage. In a document-based app, for example, it is usually not appropriate to use key-value storage for state information about each document, such as a current page or current selection. In addition, avoid using key-value storage for data essential to your app’s behavior when offline; instead, store such data directly into the local user defaults database.