Ubi–what!? With the
NSUbiquitousKeyValueStore
you can sync key-value store data across iOS and Mac devices, via iCloud. It’s perfect for persisting user preferences to iCloud, and it’s simple to set up.Feb 22, 2015 My correct icloud account is being used on my mac. However, only in the app store it is recognizing my old email address to my old icloud account. The text to login is 'greyed' out and does not allow me to log in to my current account. Any advice on how to fix this so I can update my apps? Full-resolution versions of your photos and videos will be uploaded to iCloud. How to share photos with iCloud Photo Library. Apple's photo service doesn't just provide online backups and sync for your images and video: The company also offers a free sharing service that allows you to send shared albums to friends and family (or create a publicly-shared website).
In this article you’ll learn:
- What a key-value store is
- How
NSUbiquitousKeyValueStore
is different thanUserDefaults
- How to read from and write to
NSUbiquitousKeyValueStore
- And how to sync data between iOS devices
Ready? Let’s go.
What’s A Key-Value Store?
A key-value store is a container that stores data items. Every data item has a key and a value. You can read a value from the key-value store by using its associated key.
The simplest key-value store is a dictionary or array, where values are associated with keys and indices respectively. You can compare it to an address book, where you look up a person’s address (a value) by using their name (a key).
Let’s look at an example:
let kvs = [
'name': 'John Appleseed',
'address': '1 Infinite Loop Cupertino, CA 95014',
'country': 'United States',
'planet': 'Earth',
'sector': 'Sector ZZ9 Plural Z Alpha'
]
print('sector = (kvs['sector']!)')
'name': 'John Appleseed',
'address': '1 Infinite Loop Cupertino, CA 95014',
'country': 'United States',
'planet': 'Earth',
'sector': 'Sector ZZ9 Plural Z Alpha'
]
print('sector = (kvs['sector']!)')
The dictionary
kvs
has five key-value pairs, such as key 'planet'
has value 'Earth'
. In the above example, the keys and values of kvs
both are of type String
.On the last line of the example, we’re printing out the value of
kvs['sector']
with Swift’s subscript syntax. It will print out the value for key 'sector'
, i.e. Sector ZZ9 Plural Z Alpha.Key-value stores are important in software development and apps, because they provide a simple way to store “flat” data. Unlike relational databases like MySQL, you can’t use a key-value store to organize relationships between data entries.
Associating keys with values in software development isn’t particularly novel or exciting. What makes key-value stores so interesting is that they come with a number of additional features, such as synchronization, distribution across devices, high-performance lookups, and even clustering for high-availability.
Plenty of key-value store tools exist outside of iOS development. Take Redis for example, which is an “in-memory data structure store.” It has replication and clustering, supports many different data formats, and it’s an all-round beast when it comes to web-based key-value storage.
In this article, we’re going to work with a key-value store that’s persisted via iCloud. It’s perfect to read and write key-value data, and sync it across iCloud-enabled devices. And it persists data between app installs, which makes it particularly handy for configuring apps.
Learn how to build iOS apps
Get started with iOS 14 and Swift 5
Sign up for my iOS development course, and learn how to build great iOS 14 apps with Swift 5 and Xcode 12.
Writing Data To iCloud Key-Value Store
The class we’re working with here is called
NSUbiquitousKeyValueStore
. It’s a bit of a mouth full, so let’s break it down:- “NS” stands for NeXTSTEP, which is the name of the operating system that ultimately found its way into macOS and iOS
- “Ubiquitous” is a fancy word that means “present everywhere” – quite a good name for a key-value store that’s synced with iCloud!
- “Key Value Store”, we’ve talked about that, and for short we’ll often call it KVS
The
NSUbiquitousKeyValueStore
is very similar to UserDefaults. In fact, UserDefaults
is a key-value store too. The big difference is that UserDefaults
only stores data locally in your app, and won’t sync it across devices.Just as
UserDefaults
, the NSUbiquitousKeyValueStore
has a .default
class property that we can use to read from and write to the default key-value store.Writing to the key-value store is as simple as:
The above code sets a value for a particular key. The iCloud key-value store can handle several data types, such as
Bool
, Data
, Dictionary
, Array
, String
, Double
, Date
and Int
.There are some limits to the size of data you can store:
- A maximum, overall key-value store size of 1 MB (per user)
- One key-value pair can’t be larger than 1 MB
- You can’t store more than 1024 key-value pairs
- A key can’t be larger than 64 bytes using UTF-8 encoding
If you’re only using the iCloud key-value store to save simple text-based configuration values, you won’t exceed these limits. If you’re looking to store large amounts of data, i.e. a user’s content, or images, don’t use
NSUbiquitousKeyValueStore
.To use
NSUbiquitousKeyValueStore
, you’ll need to distribute your app through the iOS or Mac App Store. You must also set the com.apple.developer.ubiquity-kvstore-identifier
entitlement in your app’s Entitlements. You can find your app’s entitlements in Xcode, by going to ProjectSettings -> Capabilities
. Enable the iCloud capability and check Key-value storage. You might need to enable iCloud for your App ID too, by going to Certificates, Identifiers & Profiles on developer.apple.com/account.Fun Fact:UTF-8 is a multi-byte encoding for text. You can compare encoding to using a dictionary to map byte values to human-readable characters. UTF-8 takes 1 to 4 bytes per character, with the first 128 ASCII characters only needing one byte. In short, you can fit 64 alphanumeric characters in 64 bytes of a UTF-8 string. You’ll need 2 bytes for diacritic characters (accents), and languages like Greek and Cyrillic. You’ll need 3 bytes for most Japanese, Chinese and Korean characters. And last but not least: 4 bytes for emoji… In short, if you use simple strings like
'address'
, you’ve got plenty of space.Reading Data From iCloud Key-Value Store
Reading data from the iCloud key-value store is simple, too. Like this:
Unlike the
set(_:forKey:)
function, the functions to read data from the key-value store use the type of the value explicitly in the function name. So, you can use these functions to read different types from the key-value store:array(forKey:)
for arrays (returns optional)bool(forKey:)
for booleans (returnsfalse
if key doesn’t exist)data(forKey:)
forData
objects (returns optional)dictionary(forKey:)
for dictionaries (returns optional)double(forKey:)
forDouble
values (returns0
if key doesn’t exist)longLong(forKey:)
for integers (returns0
if key doesn’t exist)object(forKey:)
for objects (returnsAny?
)string(forKey:)
for strings (returns optional)
Most of the functions above return optionals. If a key doesn’t exist, the return value is
nil
. Notable exceptions are bool(forKey:)
, double(forKey:)
and longLong(forKey:)
.If the function returns an optional, you can use the nil-coalescing operator
??
to provide a default value:And you can also use optional binding to validate data, like this:
Removing key-value pairs is as simple as:
Depending on the architecture of your app, it might be smart to create a separate API or interface to interact with
NSUbiquitousKeyValueStore
.What you don’t want, is sprinkling code that reads from and writes to the key-value store throughout your view controllers, for instance. A separate API will also help you migrate to another key-value store tool, if needed.
What’s also smart, is to create central structures for the key names you use in
NSUbiquitousKeyValueStore
, or UserDefaults
. Instead of working with strings directly, you organize any key strings you might use in your app in one nested struct. Like this:When you want to read from or write to the key-value store, using these constants will save you from accidentally making typos. Like this:
Make a typo in
kUseDta
and your code won’t compile, whereas for 'kUseDta'
you’re going to pull your hairs out searching for that typo…Responding To Data Changes From iCloud
The difference between
NSUbiquitousKeyValueStore
and UserDefaults
is that the ubiquitous key-value store (what a name!) synchronizes to iCloud. This essentially means that iOS will upload the key-value store to iCloud when you write something to it, and download that same key-value store into another install of the same app.![App store App store](/uploads/1/3/4/0/134047581/322600408.jpg)
The data in the key-value store is persisted between app installs – so it’ll be there even if you uninstall the app – and it’s synced between devices. You can even share the data store between iOS and Mac apps that have the same identifier.
Before we find out how synchronization works, what would you use a synced key-value store for? Some ideas:
![Download Download](/uploads/1/3/4/0/134047581/760748004.png)
- User-specific data that isn’t persisted in another database, such as a user’s name, address, phone number or email address
- App-specific configuration values, like feature flags and a user’s in-app preferences
- The state of an app, for state restoration and synchronization between app installs, i.e. “start again on device B where you left on device A”
Just like
UserDefaults
, you don’t have to actively synchronize the key-value store on the device. Here’s how that works:- You write a key-value pair to
NSUbiquitousKeyValueStore
- This key-value pair is stored in-memory as the app runs
- At some point the data is persisted to disk, or you can call
synchronize()
manually - At some point the data is sent to iCloud, where it is stored and pushed to other devices
Pushing to iCloud goes automatically. You can call
synchronize()
explicitly to sync the in-memory key-value store with the data on the disk. Like this:You can also observe changes to the iCloud data store in your app. Whenever data changes, a notification is sent to a function you specify, which includes the keys that have been changed. This mechanism uses NotificationCenter.
Here’s how you register for those changes:
It’s a bit of a beast. Here’s what it does:
- You add
self
as the observer of the.didChangeExternallyNotification
notification - The last parameter of
addObserver(...)
indicates that you only want to receive notifications from the.default
key-value store - When a notification is observed, the function
onUbiquitousKeyValueStoreDidChangeExternally(notification:)
is called
And here’s that function:
Inside that function you can use the
notification
parameter to get information about the pushed key-value data, via its userInfo
property. Such as:NSUbiquitousKeyValueStoreChangeReasonKey
to get the reason of the data changeNSUbiquitousKeyValueStoreChangedKeysKey
to get the keys of data that’s changed
You can also get the key-value data store object that’s changed, via
notification.object
. This is especially helpful if you’re working with multiple NSUbiquitousKeyValueStore
objects.Learn how to build iOS apps
Get started with iOS 14 and Swift 5
Sign up for my iOS development course, and learn how to build great iOS 14 apps with Swift 5 and Xcode 12.
App Store
Further Reading
Icloud Download Mac
That’s all there is to it! Reading and writing data with the iCloud data store is surprisingly simple. The
NSUbiquitousKeyValueStore
is ideal for syncing key-value store data between iOS devices – and now you know exactly how it works.Icloud Mac App Store Download Free
Want to learn more? Check out these resources: