Home > Articles > Mobile Application Development & Programming

  • Print
  • + Share This
This chapter is from the book

Recipe: Accessing the Ubiquitous Key-Value Store

iCloud’s key-value store lets you share small amounts of state information between devices using a ubiquitous property list. “Small” means you are limited to a total of 256Kb per key-value store and 64Kb per individual value.

Those limits mean you won’t be using this utility for sharing images and other large data items. They’re meant to save state such as the last page read, the default account name, or other such tiny bits of information that can propagate between devices to enhance the seamless movement of the user from one device to the next.

How Key-Value Stores Work

Your application’s ubiquitous key-value store is nothing more than a distributed defaults dictionary that can be reached from devices registered to the same iCloud account. This store isn’t meant to replace NSUserDefaults; it augments it by allowing you to develop applications that enable users to resume work regardless of the device used.

Imagine your user begins editing a ubiquitous document on a certain device and later launches your app on a second one. The key value store would let you load the right document (by storing the file name) and scroll it to the current position (by storing the current offset), so the user resumes interaction at the point that he or she left off.

If you want to use more extensive shared defaults, you can create those defaults in a property list file outside the ubiquitous Documents folder. This has two advantages. First, it lets you store whatever data you need without regard to the key-value store’s quota limitations. Second, it hides those defaults from the user. Data stored outside Documents cannot be directly seen or edited by the user but they remain visible to your application.

Accessing the Key-Value Store

You can retrieve the shared store from its class method, namely [NSUbiquitousKeyValueStore defaultStore]. Once retrieved, query it like you would user defaults, e.g.:

kv = [NSUbiquitousKeyValueStore defaultStore];
switchView.on = [kv boolForKey:@"switchIsOn"];

Similarly, set its value using the same dictionary-style method calls. As with user defaults, you must synchronize your changes to publish them from memory to the persistent iCloud store. Although the system may call synchronize automatically, make a habit of manually performing the synchronization. This ensures that your changes are pushed immediately rather than with a possible several-second delay.

- (void) toggleSwitch: (UISwitch *) aSwitch
{
    // Send switch update out to cloud
    [kv setBool:aSwitch.isOn forKey:@"switchIsOn"];
    [kv synchronize];
}

The previous sample code snippets implement a ubiquitous switch. When the user interacts with the switch on one device, the change propagates to all devices. In order for that change to register, your application must subscribe to a notification that informs it about key-value store updates.

Subscribing to Key-Value Notifications

Your application will want to know when the ubiquitous store has updated item values. Subscribe it to NSUbiquitousKeyValueStoreDidChangeExternallyNotification. This notification lets you determine when the store has updated and adjust your in-app settings to match.

The notification’s user info dictionary provides a list of affected keys as well as a reason why the update occurred. NSUbiquitousKeyValueStoreServerChange means the values simply updated. In addition, your application may be notified of a quota violation (NSUbiquitousKeyValueStoreQuotaViolationChange) or that local changes were discarded because they could not be sent to the server for an initial sync (NSUbiquitousKeyValueStoreInitialSyncChange).

Recipe 18-3 builds callbacks based on these various scenarios, allowing a client class to add informal callbacks. If you don’t care which key was updated here, just implement the kvStoreUpdated: callback, which is sent on any value change. The kvStoreUpdatedForKeys: callback sends an array of affected keys for more nuanced responses.

Recipe 18-3 Subscribing to Key-Value Update Notifications

[[NSNotificationCenter defaultCenter]
    addObserverForName:
       NSUbiquitousKeyValueStoreDidChangeExternallyNotification
    object:nil
    queue:[NSOperationQueue mainQueue]
    usingBlock:^(NSNotification __strong *notification) {
        NSDictionary *userInfo = [notification userInfo];

       NSUInteger reason = [[userInfo objectForKey:
            NSUbiquitousKeyValueStoreChangeReasonKey] intValue];
       NSArray *keys = [userInfo objectForKey:
       NSUbiquitousKeyValueStoreChangedKeysKey];

       // Perform updates only if there is a delegate to listen
       if (!delegate) return;

       if (reason == NSUbiquitousKeyValueStoreServerChange)
       {

            if (keys.count == 1)
               #SAFE_PERFORM_WITH_ARG(delegate,
                   @selector(kvStoreUpdatedForKey:),
                   [keys lastObject]);
           else if (keys.count)
               SAFE_PERFORM_WITH_ARG(delegate,
                   @selector(kvStoreUpdatedForKeys:),
                   keys);

           SAFE_PERFORM_WITH_ARG(delegate,
              @selector(kvStoreUpdated), nil);
        }

       else if (reason ==
           NSUbiquitousKeyValueStoreInitialSyncChange)
           SAFE_PERFORM_WITH_ARG(delegate,
             @selector(kvStorePerformedInitialSync), nil);
      else if (reason ==
          NSUbiquitousKeyValueStoreQuotaViolationChange)
          SAFE_PERFORM_WITH_ARG(delegate,
             @selector(kvStoreViolatedQuota), nil);
}];
  • + Share This
  • 🔖 Save To Your Account