Você está na página 1de 26

1

Table of Contents

Introduction ........................................................................................................... 3
How iCloud Works ................................................................................................. 4
Configuring iCloud in iOS ....................................................................................... 8
Enabling iCloud in your App ................................................................................... 9
Checking for iCloud Availability ............................................................................ 15
iCloud API Overview............................................................................................. 17
Opening an iCloud File ......................................................................................... 18
Conclusion ........................................................................................................... 26

www.andolasoft.com

Introduction

iCloud is a service that helps users synchronize their data across devices. It is a set
of central servers which stores documents, and makes the latest version available
to every device/app compatible with iCloud (iPhone, iPod, iPad, Mac, or even
Windows PC).
Here, well look into iCloud by implementing a set of simple applications which
interact with cloud servers to read, write & edit documents. In the process, well
learn about the new UI Document class, querying iCloud for files, auto-saving and
so much more!
To get the most out of this session, we recommend you to have two iOS devices
(iPhone/iPad) ready with you.

How iCloud Works

Before we get down, lets talk about how iCloud works.


In iOS, every application has its data stored in a local directory, and each app can
access data only in its own directory. This prevents apps from reading or
modifying data from other apps.
iCloud allows you to upload your local data to central servers on the net, and
receive updates from other devices. The replication of content across devices is
achieved by means of a continuous background process (daemon) which detects
changes to a resource (document) & uploads them to the central storage.
This works in real-time and enables another interesting feature: notifications.

For example, whenever there is a conflict about a document, the application is


aware of that.
If you ever tried to create something like this with your own apps, you know there
are several major challenges implementing this:
1. Conflict Resolution: What happens if you modify a document on your iPhone &
do the same on your iPad at the same time? You somehow have to reconcile
these changes. iCloud allows you to break your documents into chunks to prevent
any merge conflicts being a problem.

For cases when it truly is a problem, it allows you fine-grained control over how to
handle the problem as a developer (also you can always ask users what they
would like to do).
2. Background Management: iOS apps only have limited access to running tasks
in the background, but keeping your documents up-to-date is something you
always want to do.

Since iCloud sync runs on a background daemon, its always active.


3. Network Bandwidth Costs: Continuously pushing documents between devices
can take a lot of network bandwidth. And we know that iCloud helps reduce the
costs by breaking each document into chunks. So when you first create a
document, every chunk is copied to the cloud. When subsequent changes are
detected only the chunks affected are uploaded to the cloud, to minimize usage
of bandwidth and processing. A further optimization is based on a peer-to-peer
solution. That happens when two devices are connected to the same iCloud
account and the same wireless network. In this case data takes a shortcut and
moves directly between devices.

The mechanisms described so far are enabled by a smart management of


metadata like file name, size, modification date & version etc. This metadata is

pushed to the cloud, and iCloud uses this info to determine what needs to be
pulled to each device.
Note that the devices pull data from the cloud only when it's appropriate; Which
means it depends on the OS and platform. For example if an iPhone has much less
power and battery dependency than an iMac, the iOS might decide to notify
just the presence of a new file, without downloading it. Whereas Mac OS X might
start the download immediately after the notification.

The important aspect is that an app is always aware of the existence of a new file,
or changes to an already existing file. Also, through an API the developer is free to
implement the synchronization policy. The API allows an app to know the
situation on iCloud even if the files are not yet local, leaving the developer free
to choose whether (and when) to download an updated version.

Continued.

Configuring iCloud in iOS


The first time you install iOS, youll be asked to configure an iCloud account by
providing or creating an Apple ID. Configuration steps will also allow you to set
the services you want to sync (calendar, contacts, photos etc.). Those
configurations are also available under Settings\iCloud on your device.
Before you proceed any further with this tutorial, make sure that you have iOS
installed on two test devices, and that iCloud is working properly on both of
those.
One easy way to test this is to add a test entry into your calendar, and verify if it
synchronizes properly between your various devices. You can also use
http://www.icloud.com to see whats in your calendar.

Once youre sure iCloud is working fine, lets try it out in an app developed by
you.

Enabling iCloud in your App

In this session, well be creating a simple app that manages a shared iCloud
document called dox. The app will be universal and will be able to run on both
iPhone and iPad, so we can see changes made on one device propagated to the
other.
There are three steps in this, so lets try them out.
1. Create an iCloud-enabled App ID
To do this, visit the iOS Developer Center and log onto the iOS Provisioning Portal.
Create a new App ID for your app similar to the following screenshot:

10

Note: Be sure to end your App ID with dox in this session, because that is what
we are naming the project. For example, you could enter com.yourname.dox.
After you create the App ID, you will see that Push Notifications and Game Center
are automatically enabled, but iCloud requires you to manually enable it. Click the
Configure button to continue.

On the next screen, click the checkbox next to Enable for iCloud and click OK
when the popup appears. If all works well, you will see a green icon next to the
word Enabled. Then just click Done to finish.
11

2) Create a Provisioning Profile for that App ID


In the iOS Provisioning Portal, switch to the Provisioning tab and click New Profile.
Select the App ID you just created from the dropdown, and fill out the rest of the
information, similar to the below screenshot:

After creating a profile, refresh the page until it is ready for download and then
download it to your machine. Once its downloaded, double click on it to bring it
into Xcode, and verify if it's visible in Xcodes Organizer.

12

3) Configure your Xcode Project for iCloud


Start up Xcode and create a new project with the iOS\Application\Single View
Application template. Enter dox for the product name, enter the company
identifier you used when creating your App ID, set the device family to Universal,
and make sure you've checked the 'Use Automatic Reference Counting' (but leave
the other checkboxes unchecked):

After youve finished creating the project, select your project in the Project
Navigator and select the dox target. Select the Summary tab, and scroll all the
way down to the Entitlements section.
Once youre there, click the Enable Entitlements checkbox, and it will autopopulate the other fields based on your App ID, as shown below:
13

This is what the fields mean:


Entitlements File points to a property list file which much like the info.plist file,
includes specifications about application entitlements.
iCloud Key-Value Store represents the unique identifier which points to the keyvalue store in iCloud.
iCloud Containers section represents directories in the cloud in which your
application can read/write documents. Applications, for a users container can be
managed by more than one application. Only requirement is that the applications
have to be created by the same team (as in the iTunes Developer Center).
iCloud Access Groups include keys needed by applications, sharing keychain data.
You dont have to change anything from the defaults for this session! If you wish,
you can edit the same by editing the file dox.entititlements included in your
project.
14

Checking for iCloud Availability


While developing an app which makes use of the iCloud, best thing to do is to
check the availability of iCloud as soon as the application starts. Although iCloud is
available on all iOS devices, not everyone configures it.

To avoid possible unattended behaviors or crashes, you should check if iCloud is


available before using it.
Now, lets see how this works.
Open up AppDelegate.m & add the following code at the bottom of application:
didFinishLaunchingWithOptions (before the return YES):
NSURL *ubiq = [[NSFileManager
defaultManager]
URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSLog(@"iCloud access at %@", ubiq);
// TODO: Load document...
} else {
NSLog(@"No iCloud access");
15

}
Here we use a new method called URLForUbiquityContainerIdentifier. This
method allows you to pass in a container identifier (like we had set up earlier in
the iCloud Containers section) and it'll return you a URL to access the files in
iCloud storage.
You need to call this on startup for every container you want to access, to give
your app the permission to access the URL. If you pass nil to the method (like we
do here), it automatically returns the first iCloud Container set up for the project.
Since we only have one container, this makes it nice and easy.
Compile and run your project (on a device, because iCloud does not work on the
simulator) and if everything goes well, you should see a message in your console
like this:
iCloud access at file://localhost/private/var/mobile/Library/Mobile%20
Documents/KFCNEC27GU~com~razeware~dox/
Note that the URL it returns is actually local on the system! This is because the
iCloud daemon transfers files from the central servers to a local directory in the
device, on your behalf. Your application can then retrieve files from this directory
or send updated versions and the iCloud daemon will synchronize everything for
you.
This directory is outside of your app, but as mentioned above the act of calling
URLForUbiquityContainerIdentifier gives your app the permission to access this
directory.

16

iCloud API Overview


Before we proceed further with the code, lets give an overview of the APIs well
be using to work with iCloud documents.

To store documents in iCloud you can do things manually if youd wish, by moving
files to/from the iCloud directory with new methods in NSFileManager and the
new classes NSFilePresenter, NSFileCoordinator.
However doing this is fairly complex and unnecessary in most cases, because iOS
has introduced a new class to make working with iCloud documents a lot easier.

Continued.

17

Opening an iCloud File


For this tutorial, well start by creating a single filename. Add the following
#define at the top of AppDelegate.m:

#define kFILENAME
@"mydocument.dox"
Next, lets extend the application delegate to keep track of our document, and a
metadata query to look up the document in iCloud. Modify AppDelegate.h to look
like the following:
#import <UIKit/UIKit.h>
#import "Note.h"
@class ViewController;
@interface AppDelegate : UIResponder
<UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController
*viewController;
@property (strong) Note * doc;
@property (strong) NSMetadataQuery *query;
- (void)loadDocument;
@end
Then switch to AppDelegate.m and synthesize the new properties:
@synthesize doc = _doc;
@synthesize query =
_query;
18

Weve already added code into the application: didFinishLaunchingWithOptions


to check for the availability of iCloud. If iCloud is available, we would want to call
the new method were about to write to load our document from iCloud. So let's
add the following line of code right after where it says TODO: Load document:
[self
loadDocument];
Now, well write the loadDocument method. Lets put it together bit by bit so we
can discuss all the code as we go forward.

- (void)loadDocument {
NSMetadataQuery *query = [[NSMetadataQuery alloc]
init];
_query = query;
}
Note that before we can load a document from iCloud, we first have to check
whats in it. We cant simply enumerate the local directory returned to us by
URLForUbiquityContainerIdentifier, because there may be files in iCloud not yet
pulled down locally.

If you ever worked with Spotlight on the Mac, youll be familiar with the class
NSMetadataQuery. It is a class to represent results of a query related to the
properties of an object, such as a file.
19

In building such a query you have the possibility to specify parameters and scope,
i.e. what you are looking for and where. In the case of iCloud files the scope is
always NSMetadataQueryUbiquitousDocumentsScope. You can have multiple
scopes, so we have to build an array containing just one item.
So continue loadDocument as follows:
- (void)loadDocument {
NSMetadataQuery *query = [[NSMetadataQuery alloc]
init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
}
Now you can provide the parameters for the query. If you ever worked with
CoreData or even arrays you probably are familiar with the approach. Basically,
you build a predicate and set it as parameter of a query/search.
In our case we are looking for a file with a particular name, so the keyword should
be NSMetadataItemFSNameKey, where FS stands for file system. Add the code
to create and set the predicate next:
- (void)loadDocument {
NSMetadataQuery *query = [[NSMetadataQuery alloc]
init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate
predicateWithFormat:
@"%K == %@", NSMetadataItemFSNameKey,
kFILENAME];
[query setPredicate:pred];
20

}
You might not have seen the %K substitution before. It turns out predicates treat
formatting characters a bit differently than you might be used to, with NSStrings
stringWithFormat. When you use %@ in predicates, it wraps the value you
provide in quotes. You dont want this for keypaths, so you use %K instead to
avoid wrapping it in quotes. For more information, see the Predicate Format
String Syntax in Apples documentation.
Now the query is ready to be run, but since it is an asynchronous process we need
to set up an observer to catch a notification when it's completed.
The specific notification we are interested in has a pretty long (but descriptive)
name: NSMetadataQueryDidFinishGatheringNotification. It's posted when the
query has finished gathering info from iCloud.
So here is the final implementation of our method:
- (void)loadDocument {
NSMetadataQuery *query = [[NSMetadataQuery alloc]
init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate
predicateWithFormat:
@"%K == %@", NSMetadataItemFSNameKey,
kFILENAME];
[query setPredicate:pred];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(queryDidFinishGathering:)
21

name:NSMetadataQueryDidFinishGatheringNotification
object:query];
[query startQuery];
}
Now that this is in place, add code for the method that will be called when query
is completed:
- (void)queryDidFinishGathering:(NSNotification
*)notification {
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
_query = nil;
[self loadData:query];
}
Add the starter implementation of this method next (above
queryDidFinishGathering):
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query
resultAtIndex:0];
}
}
22

As you can see, a NSMetadataQuery wraps an array of NSMetadataItems which


contain the results. In our case, we are working with just one file so we're just
interested in the first element.
An NSMetadataItem is like a dictionary, storing keys and values. It has a set of
predefined keys that you can use to look up information about each file:

NSMetadataItemURLKey

NSMetadataItemFSNameKey

NSMetadataItemDisplayNameKey

NSMetadataItemIsUbiquitousKey

NSMetadataUbiquitousItemHasUnresolvedConflictsKey

NSMetadataUbiquitousItemIsDownloadedKey

NSMetadataUbiquitousItemIsDownloadingKey

NSMetadataUbiquitousItemIsUploadedKey

NSMetadataUbiquitousItemIsUploadingKey

NSMetadataUbiquitousItemPercentDownloadedKey

NSMetadataUbiquitousItemPercentUploadedKey

In our case, we are interested in NSMetadataItemURLKey, which points to the


URL that we need to build our Note instance on. So, continue the loadData
method as follows:
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item
valueForAttribute:NSMetadataItemURLKey];
Note *doc = [[Note alloc] initWithFileURL:url];
self.doc = doc;
23

}
}
When you create a UIDocument (or a subclass of UIDocument like Note), you
always have to use the initWithFileURL initializer and give it the URL of the
document to open. We call it here, by passing the URL of the located file and
store it away in an instance variable.
Now we are ready to open the note. As explained previously you can open a
document with the openWithCompletionHandler method. So continue loadData
as follows:
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
Note *doc = [[Note alloc] initWithFileURL:url];
self.doc = doc;
[self.doc openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(@"iCloud document opened");
} else {
NSLog(@"failed opening document from iCloud");
}
}];
}
}
When you run the app now, it'll work except it'll never print out either of the
above messages! This is because there is currently no document in our container
in iCloud, so the search isnt finding anything (and the result count is 0).
24

Since the only way to add a document on the iCloud is via an app, we need to
write some codes to create a doc. We will append this to the loadData method
that we defined earlier. When the query returns zero results, we should:

Retrieve the local iCloud directory

Initialize an instance of document in that directory

Call the saveToURL method

When the save is successful, we can call openWithCompletionHandler.

So add an else case to the if statement in loadData as follows:


else {
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:
@"Documents"] URLByAppendingPathComponent:kFILENAME];
Note *doc = [[Note alloc] initWithFileURL:ubiquitousPackage];
self.doc = doc;
[doc saveToURL:[doc fileURL]
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
[doc openWithCompletionHandler:^(BOOL success) {
NSLog(@"new document opened from iCloud");
}];
}
}];
}
Now, compile and run your app. You should see the new document message
the first time you run it and iCloud document opened in subsequent runs.
25

You can even try this on another device and you should see the iCompileloud
document opened message show up on the second device (because the
document already exists on iCloud now!)
Now iCloud integration in your application is complete.

Source-raywenderlich.com

Conclusion
Cloud is the way forward. Hence, integrating Cloud is no more a choice for an App
Developer. And, what better than Apple's highly popular iCloud?
We would be more than happy to hear your feedback/suggestions on
info@andolasoft.com.

------------------------------------------------------------------------------------------------------------------------------------------

Andolasoft builds high performance iOS Apps assuring seamless integration (With More than
300+ Apps deployed to the App Store). If you have anything to discuss, get in touch with
Andolasoft's iOS Experts.
www.andolasoft.com
26