Storing Preferences in iCloud
An app can use the iCloud key-value store to share small amounts of data with other instances of itself on the user’s other computers and iOS devices. The key-value store is intended for simple data types like those you might use for preferences. For example, a magazine app might store the current issue and page number being read by the user so that other instances of the app can open to the same page when launched. You should not use this store for large amounts of data or for complex data types.
To use the iCloud key-value store, do the following:
In Xcode, configure the
com.apple.developer.ubiquity-kvstore-identifier
entitlement for your app.In your code, create the shared
NSUbiquitousKeyValueStore
object and register for change notifications.Use the methods of
NSUbiquitousKeyValueStore
to get and set values.
Key-value data in iCloud is limited to simple property-list types (strings, numbers, dates, and so on).
Strategies for Using the iCloud Key-Value Store
The key-value store is not intended for storing large amounts of data. It is intended for storing configuration data, preferences, and small amounts of app-related data. To help you decide whether the key-value store is appropriate for your needs, consider the following:
Each app is limited to 1 MB of total space in the key-value store. (There is also a separate per-key limit of 1 MB and a maximum of 1024 keys are allowed.) Thus, you cannot use the key-value store to share large amounts of data.
The key-value store supports only property-list types. Property-list types include simple types such as
NSNumber
,NSString
, andNSDate
objects. You can also store raw blocks of data inNSData
objects and arrange all of the types usingNSArray
andNSDictionary
objects.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.
The key-value store is not a replacement for preferences or other local techniques for saving the same data. The purpose of the key-value store is to share data between apps, but if iCloud is not enabled or is not available on a given device, you still might want to keep a local copy of the data.
If you are using the key-value store to share preferences, one approach is to store the actual values in the user defaults database and synchronize them using the key-value store. (If you do not want to use the preferences system, you could also save the changes in a custom property-list file or some other local storage.) When you change the value of a key locally, write that change to both the user defaults database and to the iCloud key-value store at the same time. To receive changes from external sources, add an observer for the notification NSUbiquitousKeyValueStoreDidChangeExternallyNotification
and use your handler method to detect which keys changed externally and update the corresponding data in the user defaults database. By doing this, your user defaults database always contains the correct configuration values. The iCloud key-value store simply becomes a mechanism for ensuring that the user defaults database has the most recent changes.
Configuring Your App to Use the Key-Value Store
In order to use of the key-value store, an app must be explicitly configured with the com.apple.developer.ubiquity-kvstore-identifier
entitlement. You use Xcode to enable this entitlement and specify its value for your app, as described in Adding iCloud Support in App Distribution Guide.
When you enable key-value store, Xcode automatically fills in a default value for the containers field that is based on the bundle identifier of your app. For most apps, the default value is what you want. However, if your app shares its key-value storage with another app, you must specify the bundle identifier for the other app instead. For example, if you have a lite version of your app, you might want it to use the same key-value store as the paid version.
Enabling the entitlement is all you have to do to use the shared NSUbiquitousKeyValueStore
object. As long as the entitlement is configured and contains a valid value, the key-value store object writes its data to the appropriate location in the user’s iCloud account. If there is a problem attaching to the specified iCloud container, any attempts to read or write key values will fail. To ensure the key-value store is configured properly and accessible, you should execute code similar to the following early in your app’s launch cycle:
NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore]; |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(updateKVStoreItems:) |
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification |
object:store]; |
[store synchronize]; |
Creating the key-value store object early in your app’s launch cycle is recommended because it ensures that your app receives updates from iCloud in a timely manner. The best way to determine if changes have been made to keys and values is to register for the notification NSUbiquitousKeyValueStoreDidChangeExternallyNotification
. And at launch time, you should call the synchronize
method manually to detect if any changes were made externally. You do not need to call that method at other times during you app’s execution.
For more information about how to configure entitlements for an iOS app, see Adding Capabilities in App Distribution Guide.
Accessing Values in the Key-Value Store
You get and set key-value store values using the methods of the NSUbiquitousKeyValueStore
class. This class has methods for getting and setting preferences with scalar values of type Boolean, long long
, and double
. It also has methods for getting and setting keys whose values are NSData
, NSDate
, NSString
, NSNumber
, NSArray
, or NSDictionary
objects.
If you are using the key-value store as a way to update locally stored preferences, you could use code similar to that in Listing 3-1 to coordinate updates to the user defaults database. This example assumes that you use the same key names and corresponding values in both iCloud and the user defaults database. It also assumes that you previously registered the updateKVStoreItems:
method as the method to call in response to the notification NSUbiquitousKeyValueStoreDidChangeExternallyNotification
.
Listing 3-1 Updating local preference values using iCloud
- (void)updateKVStoreItems:(NSNotification*)notification { |
// Get the list of keys that changed. |
NSDictionary* userInfo = [notification userInfo]; |
NSNumber* reasonForChange = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey]; |
NSInteger reason = -1; |
// If a reason could not be determined, do not update anything. |
if (!reasonForChange) |
return; |
// Update only for changes from the server. |
reason = [reasonForChange integerValue]; |
if ((reason == NSUbiquitousKeyValueStoreServerChange) || |
(reason == NSUbiquitousKeyValueStoreInitialSyncChange)) { |
// If something is changing externally, get the changes |
// and update the corresponding keys locally. |
NSArray* changedKeys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey]; |
NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore]; |
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; |
// This loop assumes you are using the same key names in both |
// the user defaults database and the iCloud key-value store |
for (NSString* key in changedKeys) { |
id value = [store objectForKey:key]; |
[userDefaults setObject:value forKey:key]; |
} |
} |
} |
Defining the Scope of Key-Value Store Changes
Every call to one of the NSUbiquitousKeyValueStore
methods is treated as a single atomic transaction. When transferring the data for that transaction to iCloud, the whole transaction either fails or succeeds. If it succeeds, all of the keys are written to the store and if it fails no keys are written. There is no partial writing of keys to the store. When a failure occurs, the system also generates a NSUbiquitousKeyValueStoreDidChangeExternallyNotification
notification that contains the reason for the failure. If you are using the key-value store, you should use that notification to detect possible problems.
If you have a group of keys whose values must all be updated at the same time in order to be valid, save them together in a single transaction. To write multiple keys and values in a single transaction, create an NSDictionary
object with all of the keys and values. Then write the dictionary object to the key-value store using the setDictionary:forKey:
method. Writing an entire dictionary of changes ensures that all of the keys are written or none of them are.
Copyright © 2013 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2013-10-22