Keeping application data synchronized across devices is a complex and daunting task. Fortunately, that is exactly why Apple built iCloud. In this Tuts+ Premium series, you will learn how iCloud works and how your applications can seamlessly share data across multiple devices.
In the second installment of this series, I showed you how to leverage iCloud’s Key-Value Storage to keep small amounts of user data synchronized across multiple devices. Even though Key-Value Storage is easy to use and adopt, one of the downsides is the limitation it poses on the amount of data that can be stored. Remember that each application can only store 1MB of data and the number of key-value pairs is limited to 1024. As I mentioned at the end of the previous tutorial, our bookmarks manager might run into this limitation if any of our users wants to store a lot of bookmarks.
The solution to this problem is switching from iCloud Key-Value Storage to iCloud Document Storage for storing bookmarks. In terms of disk space, iCloud Document Storage is only limited by the user’s iCloud storage. Knowing that a free account comes with 5GB of data storage, iCloud Document Storage is the ideal solution for our bookmark manager. In this tutorial, we will refactor the bookmarks manager from using iCloud Key-Value Storage to adopting iCloud Document Storage.
Before We Start
I would like to emphasize that it is important that you have read the first and second installment in this series before reading this piece. In this tutorial, we will refactor our bookmarks manager by building on the foundations we laid in part 2 of the series.
A Few Words About
With the introduction of iCloud, Apple also made
UIDocument available to developers. The engineers at Apple created
UIDocument with iCloud in mind.
UIDocument makes iCloud integration for document based application much easier. However, it is important to note that
UIDocument does much more than providing an easy to use API for iCloud integration.
Document based applications have to deal with a number of challenges, such as (1) reading and writing data from and to disk without blocking the user interface, (2) saving data to disk at appropriate intervals, and (3) optionally integrating with iCloud.
UIDocument provides built-in solutions for these challenges.
Before we start working with
UIDocument, I want to make clear what
UIDocument is and what it isn’t.
UIDocument is a controller object that manages one or more models just like
UIViewController controls and manages one or more views.
UIDocument doesn’t store any data, but manages the model objects that hold the user’s data. This is an important concept to understand, which will become clearer when we start refactoring our application to use
Another important concept to understand is how read and write operations work when using
UIDocument. If you decide to use
UIDocument in your application, you don’t need to worry about blocking the main thread when reading or writing data to disk. When using
UIDocument, the operating system will automatically handle a number of tasks for you on a background queue and ensure that the main thread remains responsive. I’d like to take a moment and explain each operation in more detail to give you a good understanding of the different moving parts that are involved.
Let’s start with reading data from disk. The read operation starts by an open operation initiated on the calling queue. The open operation is initiated when a document is opened by sending it a message of openWithCompletionHandler:. We pass a completion handler that is invoked when the entire read operation is finished. This is an important aspect of the read and write operations. It can take a non-trivial amount of time to read or write the data from or to disk, and we don’t want to do this on the main thread and block the user interface. The actual read operation takes place in a background queue managed by the operating system. When the read operation finishes, the loadFromContents:ofType:error: method is called on the document. This method sends
UIDocument the data necessary to initialize the model(s) it manages. The completion handler is invoked when this process finishes, which means that we can respond to the loading of the document by, for example, updating the user interface with the contents of the document.
The write operation is similar. It starts with a save operation initiated in the calling queue by sending saveToURL:forSaveOperation:completionHandler: to the document object. As with the read operation, we pass a completion handler that is invoked when the write operation finishes. Writing data to disk takes place in a background queue. The operating system asks
UIDocument for a snapshot of its model data by sending it a message of contentsForType:error:. The completion handler is invoked when the write operation finishes, which gives us the opportunity to update the user interface.
UIDocument is a base class and is not meant to be used directly. We need to subclass
UIDocument and adapt it to our needs. In other words, we subclass
UIDocument so that it knows about our model and how to manage it. In its most basic form, subclassing
UIDocument only requires us to override loadFromContents:ofType:error: for reading and contentsForType:error: for writing.
Confused? You should be. Even though
UIDocument makes life much easier, it is an advanced class and we are dealing with a complex topic. However, I am convinced that you will get a good grasp of document based applications once we have refactored our application.
Before we continue, I want to make clear what our goals for this tutorial are. The primary goal is to refactor our application to make use of iCloud Document Storage instead of iCloud Key-Value Storage. This means that we will make use of
UIDocument and subclass it to fit our needs. In addition, we will create a custom model class for our bookmark that will be used and managed by the
Step 1: Configuring Entitlements
Currently, our application is configured to only use Key-Value Storage. To enable Document Storage, we first need to configure our application’s entitlements. Open the Target Editor by selecting our application in the Project Navigator and select the only target from the targets list. In the Entitlements section, you should see iCloud Containers below iCloud Key-Value Store. The list next to iCloud Containers is empty at the moment. Click the plus button at the bottom of the list and you’ll see that Xcode creates an iCloud container identifer for you that matches your appication’s bundle identifier.
What is an iCloud container? As the name implies, it is a container in the user’s iCloud data storage. By specifying one (or more) iCloud containers in our application’s entitlements file, we tell the operating system what containers our application has access to.
Step 2: Creating the Bookmark Class
In the previous tutorial, we stored each bookmark as an instance of
NSDictionary, but this isn’t a good solution for a document based application. Instead, we will create a custom bookmark class that will allow us to easily archive its data.
Get the Full Series!
Joining Tuts+ Premium. . .
For those unfamiliar, the family of Tuts+ sites runs a premium membership service called Tuts+ Premium. For $19 per month, you gain access to exclusive premium tutorials, screencasts, and freebies from Mobiletuts+, Nettuts+, Aetuts+, Audiotuts+, Vectortuts+, and CgTuts+. You’ll learn from some of the best minds in the business. Become a premium member to access this tutorial, as well as hundreds of other advanced tutorials and screencasts.