Editor’s note: This week, we work with Ziad Tamim again to give you an introduction of iCloud programming. You’ll learn how to save and retrieve data from iCloud.
In this tutorial we are going to talk about iCloud, one of the new features introduced in iOS 5. From user’s perspective, iCloud is a simple feature that allows the access of personal information from all devices wirelessly and automatically via an Apple ID. As an developer, you can use the iCloud API in the iOS SDK to integrate iCloud service in your app.
What is iCloud
Basically, iCloud is a service that helps user synchronize the data across devices. The main purpose is to let users easily put their data, whether it’s a file or document, so that they can access the data on any of their iOS devices. While you can use other cloud services for saving file or data, the core idea behind iCloud is to eliminate explicit synchronization between devices. Apple do not want users to think of the cloud servers and the syncing. Everything simply works seamlessly.
The same design philosophy also applies to developers. When you adopt iCloud, you do not need to know how to interact with the cloud server or upload data to iCloud. The iOS handles all the heavy lifting. Your focus is on the content such as managing the change of data.
Kinds Of Storage
iCloud offers three kinds of iCloud storage:
- Key-value storage for discrete values, such as preferences, settings, and simple app state.
- Document storage for user-visible file-based information such as word processing documents, drawings, and complex app state.
- Core Data storage for multi-device database solutions for structured content. iCloud Core Data storage is built on document storage and employs the same iCloud APIs.
Depending on the type of data you’re working with, you should pick the right kind of storage. In this tutorial we’ll just cover the key-value storage and show you how to build a simple app that saves notes using iCloud. (We will talk about the other kinds of storage in later tutorials).
Enabling iCloud
To use iCloud, you first needs to join the Apple Developer Program. If you don’t have one, go and purchase it as it is a prerequisite for using iCloud.
Assuming you have an iOS developer account, we’ll first create the App ID with iCloud feature enabled. Log onto the iOS Provisioning Portal and select the App IDs in the sidebar. Create a New App ID by clicking on the button in the top-right of the page like below:
In the next screen, fill in TamNote for the description and com.appcoda.TamNote for the unique identifier.
Then, go to the App ID you’ve just created and click on the link configure to enable the iCloud service:
To enable the iCloud feature, simply check the option Enable for iCloud and click the Done button.
Creating a Simple Note App with iCloud Integration
After preparing the App ID, let’s move onto create the Xcode project. Open Xcode and create a new Project, choose the template Empty Application as shown below.
In the next screen, enter TamNote as the project and set com.appcoda in the company identifier field. Don’t forget to select the “Use Automatic Reference Counting” option. Press next and create.
Enabling Entitlements for key-value Storage
Entitlements are key-value pairs that request specific capabilities or security permissions for your app. When the system grants your app entitlements, your app gain access to its iCloud key-value storage.
To use the iCloud key-value storage, first enable entitlements in the Xcode target editor, then select the use store with identifier, it will set automatically the bundle identifier of the app in the input box.
Designing the User Interface
Editor’s note: If you follow our iOS programming course from the very beginning, you should be very familiar with Storyboard. We encourage you to build the user interface from scratch. However, if you just want to focus on the integration with iCloud, you can skip this section by downloading the Xcode project template and go directly to the “Working with iCloud” section. The template already creates the user interface and view controller classes for you.
The next thing we need to do is to create the Storyboard that defines the views of our app. Navigate to File > New > New File and choose Storyboard in the User Interface template. Click next and select the iPhone device family, click create.
Also don’t forget to delete all the generated code in the method -(BOOL)application:application didFinishLaunchingWithOptions:launchOptions inside the AppDelegate file. The method should be as simple as this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
Go to Storyboard and create the user interface like below:
First, drag a Table View Controller and embed it in a Navigation Controller. Drag a button to the top-right part of navigation bar and set the identifier as “Add”. This will automatically change the button to a “+” button. Next, select the prototype cell and change its style to “Basic”.
Drag another Table View Controller and embed it in a Navigation Controller. Next, drag the Bar Button into the navigation bar. Name one as Cancel and the other as Save. Then, select the Table View and set the content to Static Cells. Finally, add one static row to the Table View and add a Text Field as below:
This “Add Note View Controller” will be shown when user taps on the “+” button. So press and hold the Control key, click the “+” button and drag towards the “Add Note View Controller”. Select “Modal” as the Segue action.
Creating View Controller Classes
Next, create two new classes to associate with the table view controller. Name one of the class as NoteListViewController and set it as a subclass of UITableViewController. Navigate to the Storyboard, select the Table View Controller of the Notes and associate it with the NoteListViewController class.
Once done, do the same steps to create another Table View Controller and name it as AddNoteViewController. Lastly, wire up the textfield to AddNoteViewController and create two actions method, so we can Cancel and Save.
Your code in AddNoteViewController.h should like this:
@interface AddNoteViewController : UITableViewController
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
@property (weak, nonatomic) IBOutlet UITextField *noteTextField;
@end
Working With iCloud
We’ve been creating on the previous section the user interface. Now, we are going to implement the main functions of the app, which are:
- Fetch all notes available on the cloud, and show them into Table View.
- Delete note from the cloud
- Add new note and save it to the cloud.
Fetching Notes
The First step that we need to do is to implement the class NoteListViewController, so we can display notes available on the cloud in the table view. Open the NoteListViewController.h and add this property to it:
@property (strong, nonatomic) NSMutableArray *notes;
The purpose of the property is to save notes locally. We’ll display the notes in the table view when needed. Next, we need to make a lazy instantiation of this property. Put the following code in NoteListViewController.m:
- (NSArray *)notes
{
if (_notes) {
return _notes;
}
_notes = [[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:@"AVAILABLE_NOTES"] mutableCopy];
if (!_notes) _notes = [NSMutableArray array];
return _notes;
}
This lazy instantiation fetches all the existing notes from iCloud by using the NSUbiquitousKeyValueStore class. The key “AVAILABLE_NOTE” is the key for storing the note array in the cloud. The key-value storage of iCloud is very similar to the local user defaults store. We use NSUserDefault class to deal with the local user defaults store, while for the iCloud key-value store, we use the NSUbiquitousKeyValueStore class for reading and writing property data.
Next, edit the viewDidLoad: method with the code below:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = self.editButtonItem;
// Observer to catch changes from iCloud
NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(storeDidChange:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:store];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
// Observer to catch the local changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didAddNewNote:)
name:@"New Note"
object:nil];
}
What we are doing here is to add an Edit button in the navigator bar for users to delete certain note. We also add an observer to the controller so we can get notification when the data on the cloud was changed. Any device running the app can upload the key-value changes to the user’s iCloud account. Upon a change received from iCloud, this NSUbiquitousKeyValueStoreDidChangeExternallyNotification will be sent. So to keep track with such key-value changes, we register for the NSUbiquitousKeyValueStoreDidChangeExternallyNotification notification.
To ensure we start off with the latest available notes, we also call up the synchronize method to obtain a fresh copy of key-value pair from the iCloud.
Lastly, we register another observer to handle the “New Note” notification. This notification is a local notification sent when user adds a new note using the “AddNoteViewController”.
Okay, let’s move on to implement the methods that are executed when we get the above notifications:
#pragma mark - Observer New Note
- (void)didAddNewNote:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
NSString *noteStr = [userInfo valueForKey:@"Note"];
[self.notes addObject:noteStr];
// Update data on the iCloud
[[NSUbiquitousKeyValueStore defaultStore] setArray:self.notes forKey:@"AVAILABLE_NOTES"];
// Reload the table view to show changes
[self.tableView reloadData];
}
#pragma mark - Observer
- (void)storeDidChange:(NSNotification *)notification
{
// Retrieve the changes from iCloud
_notes = [[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:@"AVAILABLE_NOTES"] mutableCopy];
// Reload the table view to show changes
[self.tableView reloadData];
}
The didAddNote method will be invoked when user saves a new note. We first retrieve the note newly created by user and add it to the local array, then upload the array to the iCloud. Lastly, we reload the table view to display the new note.
The storeDidChange method will be called when the data is changed in the cloud. Say, when user adds a new note using iPhone, the app in other devices (e.g. iPad) will be notified and call up this method. When there is any update, we simply retrieve latest available note from iCloud again, save it locally and reload the table view to display the notes.
Displaying Notes in Table View
We’ve pretty much completed the iCloud integration. You should be very familiar with this section if you have read through the UITableView tutorial. As we have already retrieved the note saved in iCloud, the rest of the implementation is to display the notes in the table view. In the NoteListViewController.m, add the following code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.notes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *note = [self.notes objectAtIndex:indexPath.row];
[cell.textLabel setText:note];
return cell;
}
The code is pretty straightforward. We just display the notes store in the array in the table view.
Deleting Notes From iCloud
For deleting notes from iCloud, we need to implement this code in the NoteListViewController.m:
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.notes removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[[NSUbiquitousKeyValueStore defaultStore] setArray:self.notes forKey:@"AVAILABLE_NOTES"];
}
}
When user delete a note from the table view, we remove the selected note from the local array and the selected row from table view. Lastly, we submitted the updated notes array to iCloud.
Adding Notes
Now we come to the last part of the implementation – implementing the AddNoteViewController for adding notes to the cloud. Go to the implementation file of AddNoteViewController and add the code below:
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
// Notify the previouse view to save the changes locally
[[NSNotificationCenter defaultCenter] postNotificationName:@"New Note" object:self userInfo:[NSDictionary dictionaryWithObject:self.noteTextField.text forKey:@"Note"]];
[self dismissViewControllerAnimated:YES completion:nil];
}
Recalled that we’ve registered the “New Note” notification in the NoteListViewController, here the save method is the sender of the notification. When user taps the save button to save the note, the app posts the “New Note” notification with the newly created note attached. This notification will be picked up by the NoteListViewController and saves the note to the cloud.
Testing the Note App
To test the note app, you have to compile and deploy it onto an actual device. You can’t use the Simulator to test the app as it doesn’t support iCloud. To test the iCloud sync, you can deploy to two devices. Make sure you enable iCloud on both devices. Launch the app, add a note on one device and you’ll see the note appeared on the other device. Here is a demo video to see the app in action:
What’s Coming Next
In this tutorial, we covered the basics of iCloud integration and cover how to save/retrieve data using key-value storage. However, there is a limitation for this type of storage. The total space available in your app’s iCloud key-value storage is 1 MB per user. The maximum number of keys is 1024, and the size limit for each value associated with a key is 1 MB. So if the note contains a very large amount of data, it’ll exceed the iCloud limit.
Therefore, the key-value storage is not suitable for every types of data. Normally, it is best suitable for saving app preferences and app configuration. In the upcoming tutorial, we are going to show you how to save documents using the document storage. Of course, we’ll creating an app to demonstrate the usage.
For your complete reference, you can download the Xcode project from here.
Lastly, let me end the tutorial with an exercise. Try to modify the app by adding a new feature to let user update an existing note. As always, leave me comment and share your thought about the tutorial.