navigate site menu

Start learning with our library of video tutorials taught by experts. Get started

Developing for the Apple iCloud API with iOS

Developing for the Apple iCloud API with iOS

with Todd Perkins

 


Learn to build iOS apps that can access data, documents, and more from the cloud. In this course, Todd Perkins shows you how to build a note-taking app that leverages the storage and remote data access capabilities of iCloud. Discover how to build the data model, manipulate it with helper methods, work with iCloud key-value pairs, and manipulate online documents.
Topics include:
  • Understanding the class structure of an app
  • Building the data model and constants
  • Making your app compatible with the Apple Developer portal
  • Connecting to iCloud
  • Understanding the UIDocument class
  • Handling document metadata class
  • Opening, closing, saving, and deleting documents from iCloud

show more

author
Todd Perkins
subject
Developer, Mobile Apps, Cloud Computing, Databases
software
iOS , iCloud
level
Intermediate
duration
1h 33m
released
Apr 25, 2013

Share this course

Ready to join? get started


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:00(music playing)
00:04Hi, I'm Todd Perkins.
00:06Welcome to Developing for the Apple iCloud API with iOS.
00:11This course is designed to demonstrate the key features provided by Apple in the iCloud API.
00:17iCloud enables data syncing between multiple devices using the same app, as well
00:22as multiple devices using different apps created by the same developer.
00:27The topics we'll discuss include building a note-taking app, setting the app
00:31up for iCloud integration, and multiple ways to integrate iCloud functionality into your apps.
00:37I'll also show how to manage conflicts that can arise when devices sync through iCloud,
00:43so that changes made on one device will appear on another device in a way that
00:47a user would expect.
00:49This course covers a topic that is exciting to me personally.
00:52So I hope you can share in that excitement as we go through Developing for the
00:56Apple iCloud API with iOS.
Collapse this transcript
What you should know before taking this course
00:01While you will need some Basic iOS and Objective-C experience coming to this
00:06course, you won't have to be too advanced.
00:09So if you've already watched an iOS Essential Training Course, you should be
00:13good to go for this course.
00:15But definitely having any extra programming background helps.
00:19So the more experience you have with Objective-C, the better.
00:23But again, coming into this course, I'm really expecting you to just have some
00:27basic familiarity with developing an iOS application.
00:32So with that knowledge, to follow along testing all of the iCloud functionality
00:37in the course, you also need an Apple developer account and a device to test
00:41your app on, because iCloud does not fully work in a simulator.
00:46Ideally you would have two devices. So again, knowledge of the programming, an
00:52Apple developer account, and one or more devices to test iCloud on.
Collapse this transcript
Using the exercise files
00:01If you are a premium subscriber to lynda.com then you have access to the Exercise
00:05Files for this course.
00:07Exercise Files are organized by Chapters.
00:10You'll also see a folder called assets. The assets folder has the graphics that
00:15are used, which are simply app icon images.
00:19Each chapter contains the Exercise Files used for that chapter.
00:22The individual exercise files for each movie are organized into folders, with
00:27the final and start states being saved in separate folders.
00:31That way you could follow along and if you get stuck somewhere, you can open up
00:36the final version of the files and see what's different.
00:39If you don't have access to the Exercise Files, there is no need to worry.
00:42Throughout this course I'll show you how you can build the exact same
00:46application without having the Exercise Files to start with.
Collapse this transcript
Demoing the finished app
00:00Here is the finished version of this application.
00:03As you can tell, it's pretty basic, you tap the plus button to create a note, and
00:09I'll call this note Example note.
00:12Then I can save the note by hitting the Notes button to go back.
00:17Of course I can edit the note if I want to by tapping the note again, call this
00:21Example note changed, and then go back to save it.
00:26Now the great thing about this is with the iCloud integration, I
00:29automatically see this update appear on any devices that are logged into the
00:34same iCloud account.
00:35Typically this update is going to happen within 10 to 15 seconds.
00:40And right now you can see the update happened on the other device.
00:44So I have my second device here with that exact note updated on it.
00:49Let's say I wanted to delete this note from my second device.
00:52When I delete that note, 10 to 15 seconds later, depending on your connection
00:57speed, it might take longer, or shorter, you're going to see that note disappear
01:02from my original device.
01:04That's really the gist of iCloud, is that we have this automatic syncing
01:07that happens semi-magically in the background, and we create this as
01:12developers so that users have the easiest experience integrating all their
01:17data between multiple devices.
01:20So that's the finished version of our application, and throughout this course
01:23we're going to look at how to build all the different parts of it and how to
01:26make sure that they sync together in the appropriate way.
01:29So let's move forward in building our application to integrate with iCloud.
Collapse this transcript
1. Building the Note-Taking App
Understanding class structure for this app
00:00Before we start running the code for our app,
00:03I wanted to go over the basic classes and files that we're going to be working with,
00:08so you have an idea about how the app is organized before you create it.
00:12This is actually the finished version of the app, and for those of you who have access
00:16to the Exercise Files, I didn't provide files for this movie, because I just want
00:21you to watch and just look on the screen here.
00:24And I'm at MainStoryboard.storyboard, and this application is a storyboard
00:29application that's based on the master detail template.
00:33And so it's going to be a note-taking app, and in a storyboard you will see that
00:39there is a Master view with a table view in it called Notes, and then the
00:44detail view has the text in it.
00:46So you create a note by clicking the plus button, the View with the Table view,
00:52and then you click on the note, and you see the note in a single Note view page.
00:59All the data is managed in the data class, and there are constant values in
01:04Constants.h. The data class has a number of methods that allow you to manipulate
01:11the data that's stored inside of an NSMutableDictionary.
01:15Again the MasterViewController controls the Main View, which has the table
01:21view, and the DetailViewController controls the view when you're looking at a single Note.
01:28The finished application will allow you to create notes by clicking the plus button.
01:34After you create a note you can go back and see that the note is saved.
01:40You can click on the note to see and edit it.
01:47Notes are then saved to user defaults, which means they are saved on your device
01:52until you delete the application.
01:53So if I click the Home button and the Stop button and run the application again,
01:58we'll see that that same note that I added is there along with the other notes
02:03that were there from before.
02:04So that's an overview of the classes we'll be working with in this chapter.
02:11This organization is called the ModelViewController design pattern.
02:16The Model represents the data for your app, which is stored in our data class.
02:21The View is the visual part of your app, which is our storyboard. And Controller
02:26is the part of the app that contains the logic for manipulating the model to update
02:31the view. And that's split up into the MasterViewController and
02:35DetailViewController classes.
02:36So now that you've seen an overview of how this app is set up, let's
02:41start building it.
Collapse this transcript
Creating the project
00:00Now let's create the project for the app.
00:03In Xcode, click Create a new project or go to File > new project, and choose under
00:09iOS > Application > Master- Detail Application template.
00:14This template isn't far away from what we want the end result of our notes app
00:18to be, so it's going to help us a lot.
00:21I'll click Next, for Product Name I'll type Plain Ol' Notes, for Organization
00:26Name I am going to leave that blank, for Company Identifier I am going to put my
00:30reverse domain structure which is com.toddperkins.
00:33Of course you could use your own here if you want.
00:36For Devices choose iPhone and then check Use Storyboards and Use Automatic
00:39Reference Counting. And click Next.
00:42I am going to save this in the Chapter_01 folder in the creating_project
00:47folder and click Create.
00:50In the project I am going to go to MainStoryboard.storyboard, and I am going to
00:56scroll over to find the MasterViewController and change Masters to Notes, and
01:03then I'll go to the DetailViewController and change Detail to Note.
01:07This is going to the single note view.
01:09Then I am going to replace this UI label with a UI Text view, so I'm going to
01:15scroll down in the Object area until I find text view, and just drag and drop
01:21that into place, making sure it's aligned properly, and now I'm going to go to the
01:27DetailViewController.h and make a small tweak to the code.
01:30I'm just going to change UILabel to UITextView, and I'm going to change
01:39detailDescriptionLabel to tView, and I'll save that file and go to
01:45DetailViewControl.m and make sure I change where the detailDescriptionLabel
01:50is mentioned to tView. And then we'll need to go back to that storyboard and
01:56make the connections.
01:57So I'll save this file and go to MainStoryboard.storyboard, click the
02:03Connection inspector button, click Detail View Controller on the left side of
02:08the screen, and then just click and drag from tView onto that text view so
02:14the connection is made.
02:15So now you should be able to save and test the app in the simulator. And
02:20simulator you see Notes, I can click plus to add a new item, I can add as many
02:30as I want, and it shows the timecode of the item, and if I click it, and I see the value
02:35in that UITextView.
02:37So now I've successfully created our project and set up the storyboards.
Collapse this transcript
Building the data model and constants
00:00Now, as we have discussed earlier, this app is going to use the
00:05ModelViewController design pattern.
00:07So as part of the model, we're going to create a class to hold all the data and
00:12we'll access the data through static methods.
00:14We'll also create a file to hold our constant values.
00:18So I am going to press Command+N to create a new file, and I'll choose
00:23Other, Empty, and click Next, we'll call this file Constants.h, and then I'll click Create.
00:31In Constants.h I'm going to define three values.
00:35So, we'll define one called kDefaultText, we'll set it equal to New Note, this is
00:44going to be the default text when you create a New Note, and then we'll define
00:48another value called kAllNotes, this is going to represent the key that holds
00:54all the notes in user defaults. Call this notes. And then finally, we'll
01:01define kDetailView, that will represent the DetailViewController, and we'll call this showDetail.
01:06Save the file, and press Command+N to create a new file.
01:12Under Cocoa Touch, choose Objective-C class and click Next. The Class name is
01:16going to be Data, which it will be a subclass of NSObject, and I'll click Next. And
01:23then, press return to create the file.
01:25So what we'll do here is just define NSMutableArray that's going to store all of our data.
01:32So in the header file, let's declare a static method that returns an
01:36NSMutableDictionary, and we'll call this getAllNotes.
01:43And then, I am going to copy this line of code and then paste it in Data.m. Now
01:59in Data.m, above that method I just pasted in, I am going to declare a static
02:05NSMutableDictionary called allNotes, and I am going to return that at the
02:17bottom of getAllNotes.
02:22Above the return value, I'm going to instantiate all notes if it's not
02:27already instantiated.
02:29So, we'll check to see if allNotes is equal to nil. If that's the case, we'll set the
02:36value of allNotes by creating an NSMutableDictionary. So allocate it, and we'll
02:48call initWithDictionary.
02:49And then in double brackets I am going to call NSUserDefaults,
02:58standardUserDefaults, and in the outer brackets, dictionaryForKey, and the key is
03:06going to be kAllNote.
03:09And since that's going to come from our constants file we'll need to import that
03:13constants file at the top of our code.
03:15So, import Constants.h, and for that string type in kAllNotes.
03:22So you can save the files now.
03:26Now we've created the classes that we need for our data model.
Collapse this transcript
Creating helper methods for manipulating data
00:01Let's now create some static helper methods so that we can easily manipulate the
00:05data in our data class from anywhere in our application.
00:11Under getAllNotes I am going to declare a few methods.
00:13Again, they are all going to be static and they're going to revolve around
00:17manipulating our NSMutableDictionary that holds all the notes.
00:22So the first one is going to return void, and we will call it setCurrentKey. I'm
00:27going to receive an NSString called key.
00:35The next one, we'll go with that current key method, and it will return an
00:39NSString, we will call it getCurrentKey.
00:42The current key is going to correspond to the note that's being edited currently.
00:48So this will represent the key for the currently edited note and the value will
00:54be the text inside of the note.
00:55The keys will actually just be timestamps as well.
01:00So on the next line, another static method, returning void, called
01:05setNoteForCurrentKey.
01:12So this will receive an NSString called note, and then on the next line, we'll call
01:20this setNote and then a colon,
01:23and we'll receive an NSString right there called note forKey, and then we'll
01:31pass in the key, that's an NSString as well, we'll call that key.
01:37On the next line we are going to declare a method called saveNotes, it will return void.
01:42On the next line, removeObjectForKey, and we'll pass in the key so, that's going
01:55to be an NSString. Again, we'll call it key.
02:01And then what I am going to do is actually just copy and paste all of this code
02:06that we just wrote, and save the file, and go into Data.m, and I am going to
02:11paste the code right under getAllNotes.
02:14Now for each of these methods I am going to delete the semicolon, go to
02:19next line, and add some curly braces.
02:24And once that's done I am going to scroll to the top of my code and declare a
02:30static NSString called CurrentKey.
02:33So I will do that right under the NSMutableDictionary allNotes.
02:38Again that's an NSString called currentKey.
02:43So in setCurrentKey method, I'll set it to currentKey = key, and in the get
02:50method I'll return it.
02:51So return currentKey, and then setNote for current key, I'm going to call the method self.
03:01Since this is a static method, self means the class. setNote, passing in the note
03:10received forKey, currentKey.
03:14And then in setNote for key, I'm going to manipulate the NSMutableDictionary.
03:20So allNotes setObject:note forKey:key. In saveNotes are going to save the
03:29values to NSUserDefaults, so double brackets, and the inner brackets,
03:35NSUserDefaults standardUserDefaults, setObject, and object is going to be
03:41AllNotes, and the key is going to KAallNotes, remember we declared that in
03:47the Constants file.
03:49So I'll go to removeObjectForKey, and again we are going to manipulate our
03:52NSMutableDictionary AllNotes by calling removeObjectForKey, passing in the key received.
03:59So a very simple, straightforward manipulation of our NSMutableDdictionary,
04:03but we can easily access all of that information straight through our data
04:08class static methods.
Collapse this transcript
Adding notes
00:00Now we are going to look at adding the notes to our app.
00:04This is where you're going to see the Master Detail template app really help a
00:10lot to creating notes taking app.
00:12So you go to MasterViewController.m, and in the Import statement area we are going to
00:19import both the Constants file and the Data Class.
00:26So Constants.h and Data.h. If you scroll down a little bit you will see an
00:31NSMutableArray declared called objects, and objects is going to hold all of the
00:36data that's going to appear in the UITableView.
00:40So by manipulating that objects array we can control what appears in the table in the app.
00:47So let's do that by scrolling down below insertNewObject. We're going to declare
00:53our own method called makeObjects. And makeObjects is simply going to instantiate
01:02objects to an NSMutableArray, so we will call it NSMutabelArray, arrayWithArray, and
01:09the array is going to come from the keys in our data class.
01:13So first we will need to access that dictionary.
01:17So Data, getAllNotes, and then allKeys.
01:23So that's going to return an NSArray, which we're wrapping within an NSMutableArray.
01:30That way we can manipulate it.
01:31So let's scroll up to viewDidLoad, and right below that comment, it says Do any
01:39additional setup after loading the view,
01:41we're going to call self makeObjects.
01:45Now if you look at this ad button declaration, again this is part of the
01:48template that already exists for us.
01:51The add button calls insertNewObject when clicked.
01:55So let's scroll down to insertNewObject and we'll delete the code that
02:00instantiates the objects array, so that's that if statement there,
02:05and we'll replace that by creating a string key based on the NSDate, which is
02:11basically just the timestamp.
02:13So NSString, call it key, set it equal to in [[NSDate date]] so that's the
02:27current date, down to the second, and then description.
02:31So its going to give us a string for the date.
02:35So on the next line we are going to set the current note, so Data setNote, and
02:42the note is going to be the default text, so that's just kDefaultText coming
02:46from Constants.h, and for key its going to be Key, on the next line Data setCurrentKey key.
02:58And now it says _objects insertObject we're going to replace NSDate date just with key.
03:03And at the bottom of this method we are going to segue to the detail view controller.
03:12Now we want to do that because whenever you add a new note, we don't want to see
03:17that there's a blank new note added to the table view. We want to create that new
03:22note in the table view and then transition right into that detail view so the
03:25user can just start writing a note right away.
03:28So to do that, call self performSegueWithIdentifier, the identifier is going to
03:36come from Constants.h, and that is kDetailView, and the sender will be self.
03:45So at this point we should be able to save and test and just click on the plus
03:50button and see the transition to the detail view.
03:53So now we still see that timestamp in there, so let's make sure that when you
03:57create a new note, that there's actually nothing written in that note area.
04:01So let's stop the app and go into DetailViewController.m, scroll down, and notice
04:11that in viewDidLoad we are calling self configureView.
04:15So again, that comes from the template.
04:16And so what I'm going to do is I am going to set the text field to blank.
04:23So I am going to delete everything in configure view except for that comment, and
04:27just type self.tView.text equals a blank NSString.
04:34So let's save and test that again.
04:36And we should see that when you click the plus button, we transition to the
04:40detail view and its blank, and then if we want to we can click in there
04:45and start typing text.
04:46Now of course that would be better if the keyboard showed automatically.
04:50To do that, all we have to do is make that text view become first responder.
04:55So in brackets, self.tView becomeFirstResponder.
05:00So with that line of code, we can test, we will see the transition to the
05:04detail view, that the text field is blank, and that the keyboard automatically
05:11comes up. And there we go.
05:14So we have some test text in there.
05:18So now we have successfully created a note in the table view and transitioned
05:24over to the detail view with the keyboard ready to type out a note.
Collapse this transcript
Displaying and saving notes
00:00Now that we have a system for adding notes, let's add the functionality to save
00:06the notes that we write and display notes that we've saved.
00:10Go to MasterViewController.m and scroll down to the bottom and find the
00:14prepareForSegue method.
00:15In this method, I am going to change NSDate to NSString. Let's go to the next
00:23line, and we are going to set the current key for data.
00:31So Data setCurrentKey, and just pass an object.
00:35So now save this file and go to DetailViewController.m.
00:38Now let's scroll down to configureView.
00:42Now the first thing we want to do here is import the data class.
00:45So scroll to the top and import Data.h and Constants.h. And then, scroll down
00:55into configure view, and what we are going to do is check to see if the
01:00current note is blank.
01:02So let's delete self.tView.text equals the blank string, and what we are going to
01:08do is create a string, let's create an NSString called currentNote, and we will
01:17set it equal to Data getAllNotes, objectForKey, and then some brackets,
01:26Data getCurrentkey.
01:29Now you may have noticed that the detail item was set in MasterViewController.m
01:35before the Segue, and it actually has a value the same as the current key.
01:39So we could put detail item right here in objectForKey.
01:43But since we want to manage all of our data in the data class I decided not to do that.
01:47So now let's check if currentNote is EqualToString:kDefaultText, remember that's
01:58the default text for a new note as set in Constants.h, and it just says new note.
02:04So if it's the default text and we haven't changed or saved the note, then we're
02:09going to set self.TView.txt equal to a blank NSString.
02:14Because we don't want a user clicking the button to create a new note and
02:18having to delete the words new note every single time that happens.
02:22So to save us from that we are just going to set the string to blank, and then we
02:27will create an else statement below the if statement, and here we will just set
02:32the TView's text = currentNote.
02:36So it's whatever note is saved.
02:37Now let's look at saving the notes.
02:40Scroll down right below viewDidLoad.
02:43And we are going to implement viewWilldisappear, so returns void,
02:49viewWillDisappear, and here we want to make sure that the note's not blank
02:57before anything else happens.
02:58So create an if statement and check to see if not self.TView.txt is equal to
03:06string and then just a blank NSString, so if it's not blank we're going to set the
03:14note for current key.
03:15So Data, setNoteForCurrentKey, and the note is going to be self.TView.txt.
03:23But if the note is blank, in other words, else, we are going to remove the object
03:32for the current key.
03:33So Data, removeObjectForKey, and then Data, getCurrentKey.
03:39Now after we take care of this we want to make sure that we save the note.
03:43So below the else statement, Data saveNotes.
03:47Now what we want to do is when we return back to the MainViewController, we want
03:52to reset all of the data to make sure that all of the table view items are
03:58reloaded from our data model.
04:00So save this file, and go to MasterViewController.m. In here, scroll up right
04:09below viewDidLoad, and then we're going to implement viewWillAppear.
04:14Then we are going to override viewWillAppear. So just start typing it out, you
04:22should see that code hinting, and of course we're going to call super
04:26viewWillAppear, animated.
04:31And on the next line we are just going to call makeObjects, so self makeObjects,
04:35and the next line after that we are going to reload the table view, so
04:39self.tableView, reloadData.
04:43So that will re-populate the data in table view, making sure everything is
04:48re-created based on the new data.
04:51So we will save and test the app in the simulator, and we can create a new note,
04:59and just type Example note, click Notes to return, I can click on that note
05:06and I see Example note.
05:07And now you'll notice that we still have that timestamp displaying instead of
05:12the name of the note.
05:13So let's fix that by stopping the simulator, and then finding the method called
05:21cellForRowAtIndexPath.
05:25In here change NSDate to NSString, and then for cell.textLabel.text we are just
05:34going to use that object as the key.
05:36So Data getAllNotes, and then we are going to type objectForKey, passing in the object.
05:42So it's going to display the actual text of the note instead of displaying the
05:47key, which is the timestamp.
05:50So save and test this in the simulator, you create the new note, you can
05:55type out example or anything you want for the note, and then I see the name
05:59of note here, I can create another note in here, return back to the master
06:06view, and I can view my notes.
06:09You will notice also that I can edit them, so I can click on example, change that
06:14to examples, and it's updated when I return to the master view.
06:18So now we can create, save, and edit notes.
Collapse this transcript
Sorting and deleting notes
00:01The last thing we'll do for our application is sort all of the notes so that
00:05the newest notes appear on the top.
00:08In addition to that, we're going to make it so you can delete notes if you want
00:12to when you click the Edit button.
00:14So, first we will sort the notes. Go to makeObjects in MasterViewController, and
00:19right under where we instantiate objects, call _objects sortUsingComparator.
00:24I am going to press return so Xcode writes out the block of code.
00:31And in here I'm going to return in brackets [obj2 compare:obj1].
00:42Now I want to typecast these two NSDates so that they are treated in that way.
00:47So typecast both of these values to NSDate.
00:52And those will be sorted again with the newest values first.
00:56Now the reason why that typecast is going to work is because all of the keys are
01:03actually strings of dates.
01:04So if you go to insertNewObject, remember we declared that key, it's a string
01:10representation of the date.
01:12So this is actually going to work when we typecast it back when we are
01:16organizing them later.
01:17So now scroll down and find a TableViewCommitEditingStyle.
01:26So right above _objects removeObjectAtIndex, we are going to call Data
01:32removeObjectForKey, and the object is going to be
01:38[_objects objectAtIndex:indexPath.row].
01:44So its going to remove that key, and then what we want to do is save the notes, so we'll
01:50call Data saveNotes, and remember that's going to actually save the value to
01:56user defaults, so the value will be saved on the device until the application is uninstalled.
02:02So let's save and test this in the simulator, and see we have the notes that are
02:09still saved in the simulator from earlier, and if you didn't happen to see those
02:14notes saved now, don't worry about it, sometimes changes are not saved until you
02:21click the Home button of your application.
02:23When you exit by clicking the stop button in Xcode, sometimes your application is
02:28killed before values are saved to user defaults.
02:31So if you are not seeing values saved, just click the Home button first before
02:35clicking stop in Xcode, and the value should be saved then.
02:39So I will create a new note, and we should see that it's at the top, and I will
02:47click notes again, so I have a top note at the top, and I will click the plus
02:52button again, and I'll call this Real top note, and now this one will be at the top.
02:58Now I can edit, delete any notes I want, click Done, they are deleted.
03:04Now I have Real top note and another note, lets see that they're displayin
03:08the appropriate data.
03:10And if I hit the Home button, stop the app, remember I hit the Home button
03:16first to make sure the values are saved to user defaults, and then I run the app again,
03:21you'll see that the same notes that I had when I closed the app before are still there.
03:29So that completes our app.
03:31So now we've created an app where you can make notes, edit them, and save them.
03:37And with this all done, we're ready to start integrating iCloud.
Collapse this transcript
2. Preparing the App to Use iCloud
Understanding what iCloud does
00:01Before we add iCloud integration in our app, let's take a minute to discuss what
00:07iCloud actually does.
00:08I am going to get my information from Apple's iCloud Design Guide, so you can
00:14either find it on Apple's website or just follow along on my screen.
00:19iCloud was made to sync data between multiple devices with minimal user interaction.
00:28So, as this picture shows, you have one document in the Cloud, and you'd maybe
00:33edit it on your iPhone, and the data would be beamed up to the Cloud, and
00:39the file would be updated, and automatically your iPad and your Mac book would
00:45be updated as well.
00:47I'm going to click Next on this page to go into Apple's Design Guide, and if you
00:53scroll down we can see how the data is actually stored.
00:58Your Application has a Sandbox Container. That's where you store the documents
01:04that are used in your app.
01:05There is also something called a Ubiquity Container that's outside of your
01:09application Sandbox, and that's where you store the iCloud data.
01:14Your Ubiquity Container is a local representation of your Cloud documents.
01:21I'll scroll down just a little bit more,
01:24and we'll look at a Ubiquity Container.
01:25There is a folder that has a subdirectory called Documents, and you can create
01:32your own subdirectories in the Documents folder if you want, or you can create
01:36them outside the Documents folder.
01:38Files and folders created within the Ubiquity Container's Documents folder are
01:44publicly viewable by users.
01:46Files outside the Documents folder are private.
01:51The Design Guide explains more important things if you'd like to look over it.
01:56This section explains how a user's iCloud storage is limited.
02:02So you don't want to store massive amounts of files in there that you can easily
02:06replace within your app.
02:08You'll also see that there are two different types of iCloud storage.
02:12There is key-value storage, for preferences and small data values like you would
02:20store in user defaults, and there's document storage for iCloud for larger
02:24document files that could be publicly available and viewable by our user.
02:30So, if you do have more questions about how iCloud works, there is the Design
02:34Guide available to you, so you can feel free to read that over.
02:37But for the purposes of this course, just understand that iCloud is made to sync
02:42files between devices in a way that doesn't require a lot of user interaction,
02:48that's kind of magically done behind the scenes.
02:52So, throughout this chapter we'll look at how to set up our application
02:55to support iCloud.
Collapse this transcript
Making your app compatible with iCloud in the developer portal
00:01To make your app compatible with iCloud, you'll need to go to the Provisioning
00:05Profile and click on App IDs, so scroll down and I'm going to find my App, which
00:11is called Notes, and if you don't have an app that's here already, then you're going
00:15to need to create the app and the Provisioning Profile, and all of the standard
00:20things you do when you create an app, liked you learn in iOS Essential Training.
00:24And here I'm going to click the Configure button, and open up the Configure App
00:29ID menu, and you can check Enable for iCloud there, click Done, and when you
00:36return back to App ID's page you should see that your app has iCloud set to
00:41be Enabled, and then make sure to re-download your Provisioning Profile and install it.
00:48Also note that iCloud is not going to work on the simulator.
00:53You are going to need to actually use your device for testing iCloud.
00:57So, once you've done that then you're ready to go back into Xcode and start
01:02enabling iCloud there.
Collapse this transcript
Modifying your project's properties for iCloud connectivity
00:00Now let's set up or Xcode project to support iCloud.
00:04I am on the Summary tab with my target selected, and I've changed my Bundle
00:09Identifier to com.toddperkins.notes, and that corresponds to my Development
00:14Provisioning Profile that we setup in the previous movie.
00:18As I scroll down you'll also notice my app icons are set, and these each come from
00:23the assets folder so you can set these if you'd like, by right-clicking the
00:28images and choosing Select File.
00:31And I am going to scroll down to the Entitlement section and I am going to check
00:35to use and entitlements file.
00:36When I check that box, on the left side of the screen you'll notice that Plain Ol' Notes.entitlements is created.
00:42So this is going to hold our entitlements, and then I checked to Enable iCloud,
00:47and then I check the box use Key-Value Store with identifier, and then I am
00:52going click the plus button to create a Ubiquity Container.
00:55Now this Ubiquity Container is for when we use the Document model for storing
01:00iCloud data, and the Key-Value Store is of course for Key-Value pairs when you're
01:05storing iCloud data.
01:06So, we're going to look at using both in the rest of this course.
01:10So once you have these boxes checked, your app is then iCloud-enabled.
Collapse this transcript
3. Working with iCloud Key-Value Pairs
Understanding key-value pairs in iCloud
00:01Perhaps the easiest way to add iCloud integration to your app is to store key value pairs.
00:08Note that with key value pairs the max amount of total data that you're allowed
00:12to store in iCloud is 1 MB.
00:16If you're making a more robust app than this, then maybe you don't want to save all
00:21of your data into the key value pairs.
00:23We will look at ways in another chapter for how to store more data to the cloud.
00:29But for the purposes of teaching you how to save key value pairs, we're going to
00:33save all of our note data to iCloud.
00:35In Data.m scroll down to where we save the notes. So here we save to
00:41NSUserDefaults. Saving to iCloud is almost exactly the same. In fact I can
00:47actually copy and paste this line of code to the next line and simply
00:51change NSUserDefault standardUserDefaults to and NSUbiquitousKeyValueStore defaultStore.
01:00And that value is then saved to iCloud.
01:03The NSUbiquitousKeyValueStore works almost exactly like NSUserDefaults, and it
01:09has the same methods like set object for keys, set dictionary for key, and in
01:14almost every situation it works just like NSUserDefault.
01:18Apple, however, does not recommend replacing NSUserDefaults with
01:23NSUbiquitousKeyValueStore. It recommends using them together so that you always
01:28have a backup of your data locally
01:31So now with our data saved to the cloud every time it's saved normally,
01:36let's create a new method that returns void, and this is going to handle
01:42change events in iCloud.
01:44Whenever your app notices data from the cloud that's been updated, this
01:49notification will be sent to your app.
01:51So we will call this dataUpdatedFromCloud, and it's going to receive an
01:58NSNotification, we'll call it notification.
02:06For now we are just going to copy and paste this into data.h and leave it blank,
02:15and we'll go to AppDelegate.m, ImportData.h, and then we are going to listen
02:22for the event that occurs when data comes from iCloud.
02:26So type double brackets, NSNotificationCenter defaultCenter, addobserver, selector,
02:35name, object. So make sure you grab the right method their.
02:39The observer is going to be Data class, because it's a class method, selector is
02:45going to be dataUpdatedFromCloud.
02:48The name is going to be
02:49 NSUbiquitousKeyValueStoreDidChangeExternallyNotification.
02:54Notice the other notifications here. If you want to learn more about that I
02:58recommend looking that up in the documentation.
03:01But just note now that you can listen for other events if you want to.
03:07And the object is going to be NSUbiquitousKeyValueStore defaultStore.
03:14And then after that in application didFinishLaunching, we're going to call
03:20NSUbiquitousKeyValueStore defaultStore synchronize.
03:25Now what this is going to do it is its going to search for data from the cloud.
03:29So we will save the file, and now our application is set up to save our notes to
03:35iCloud using key value pairs, and to listen for changes on iCloud, so we can update our
03:42app when that happens.
03:43We'll look at how to do that throughout the rest of this chapter.
Collapse this transcript
Using iCloud as your main data source
00:00So let's say you wanted iCloud to be your main source of data for your app, so
00:04you back everything up to iCloud and as soon as you get an update from iCloud you
00:08just overwrite all the local of data with your cloud data.
00:11Now you may be thinking off the bat that their might be some problems with that,
00:15but I want to show you how to do it anyway, in case there are those of you out
00:20there who would like to see how to do it. And we are going to spend the rest of
00:25this chapter after this
00:26improving the app step by step to give it better support with cloud data, and
00:29better integrate that with the local data.
00:31So we always have the most accurate, up-to-date information in our app when we are running it.
00:36So what we are going to do is scroll down to data updated from cloud, and I'm in the data class.
00:42And in here, I am going to create an NSDictionary called dictionary called cloudData.
00:49Now remember we can grab a dictionary as if we're using NSUserDefaults, except
00:53for use NSUbiquitousKeyValueStore defaultStore, and then we just call
00:59dictionaryForKey and we pass in kAllNotes.
01:02So that gives us a dictionary of all the cloud notes.
01:07So right here what we could do is say allNotes equals, then we just create a new
01:13NSMutableDictionary.
01:15So NSMutableDictionary alloc initWithDictionary, and we pass in cloudData.
01:21Once we've done that we are going to want to update the table view on the screen.
01:26Now to do that we will need to be able to access the table view from here, and a
01:31simple way to do that is to go to MasterViewController.h and add a few methods.
01:35First, a static method that returns a MasterViewController that we will call a masterView.
01:41And now we will define a method called reload, and what reload is going to do is
01:48it's going to reload our table view.
01:50So we will go MasterViewController.m, and the first thing we will do is define
01:54the master view method.
01:56So right under Implementation, I'm going to declare a static property that is
02:01of type MasterViewController, and we will call this mView, and we'll just
02:08define masterView to return mView, and in viewDidLoad we are going to
02:17instantiate mView as self. So mView = self.
02:21So that way we have an anywhere accessible through this class way to grab
02:27the current table view.
02:29And then I'm going to scroll down, and I'm going to define reload right
02:34below view will appear.
02:35And all I am going to do is just cut and paste self makeObject and
02:41self.tableView reloadData.
02:42And then I am going to call reload and view will appear.
02:50So whenever we want to reload the table view we just call this reload, and we can
02:55access the MasterViewController through its static method. So lets save this and
03:00head on back to Data.m, scroll to the bottom, we want to call
03:05MasterViewController to reload the data to the table view.
03:08Of course first, we are going to need to import MasterViewController.h. So we'll
03:14do that and scroll back down, and then in double brackets we will call
03:19MasterViewController masterView reload.
03:23So you reload that table view with the cloud data.
03:26So I will save the file, and now what I'm going to do is plug in my phone and
03:31test this on my phone instead of in the simulator, that's because these iCloud
03:36features do not work in the simulator.
03:39So with my phone plugged in, I'm going to run the app.
03:48So in the Notes app on my phone, I can tap the plus button to create a new note. I am
03:53going to going to call this Example note.
03:57Remember that when we call saveNotes after I go back to the Main view that the
04:02data is then sent to iCloud in the saveNotes method.
04:07At this point I should be able to press the Home button on my phone and exit out of the app.
04:15Remember you don't want to hit stop, we want to make sure this data saves.
04:18So I am exiting out of the app by pressing the Home button on my phone, and
04:23then I'm going to press stop in Xcode, so the app is now stopped.
04:29And back on my phone I'm going to delete the app.
04:35Now you may see a message that says this app stores data on the cloud, so
04:41deleting it will not delete that cloud data.
04:44Now what I am going to do is go back and reinstall it. Remember, all of the
04:49NSUserDefaults data is deleted off my phone when I delete the app from my phone,
04:56but the cloud data is saved.
04:58So if this is working right I should be able to run the app again. It reinstalled
05:03the app onto my phone, and see that note that was deleted from NSUserDefaults
05:08when I deleted my app.
05:12And there it is on my phone.
05:14Now of course this will work with any version of this application that's on
05:18a different device.
05:19So when I update my notes on here, if I have installed the same app, those
05:24updates will be sent to I cloud and then downloaded to that other device.
05:28So in this way we are overriding all of the data that's in the app with the
05:34iCloud data, which in theory should be completely fresh, but in practice I've
05:40noticed some glitches with this and throughout the rest of this chapter, I said
05:44earlier, we're going to look through handling those problems and conflicts and
05:49some ways that I found to resolve them.
Collapse this transcript
Adding notes when cloud data is found
00:00Now we've already looked at overwriting all of your local data with cloud data.
00:05But let's say you don't want to do that, you just want to detect whenever
00:09new cloud data is added and then you want to add new notes that weren't there before.
00:15So let's look at how to do that.
00:17In dataUpdatedFromCloud, in Data.m, I'm going to delete the line of code that sets allNotes.
00:23Here I'm going to create a loop that's a for in loop, so I'm going to loop
00:29through all of the keys, so those are NSStrings we're calling key in cloudData.
00:34And what I want to do is check to see if the key in cloudData is not already
00:41a locally saved note.
00:43And if not I'll add it to the local notes.
00:46So all I need to do is check to see if the value for that key is nil in all notes.
00:52So in allNotes objectForKey, key is equal to nil, and we are going to add it.
01:00So in brackets allNotes setObject, and the object is going to be cloudData
01:05objectForKey, key, and the key is going to be key as well.
01:12So now what should happen is that when we actually update a note on another
01:17device, that note should then come and be added to our current notes and not
01:23overwrite anything that's existing.
01:26So let's save the file, and this time I am actually going to test it on two devices.
01:33So I am starting it on my iPhone 5 now, and I'll start it on my other iPhone as well.
01:42So now what I'm going to do is I'm going to add another note from my other
01:47iPhone, so not my iPhone 5, I am adding another note here.
01:51We will call this Remote note, and we are going to go back.
01:58And typically this process takes, from my experience, about 10 to 15 seconds
02:04for the data to pass, and this is of course on the same Wi-Fi network for these devices.
02:09So it might take longer or shorter for you.
02:13But if you look at my iPhone 5 now, you'll see that I have the Remote note showing up.
02:19And so here, if I add another note, and I called this Bonus note, and go back to
02:28notes on my iPhone 5, I should then, 10 to 15 seconds later, see Bonus note appear
02:34on my other iPhone as well.
02:36So there it is, and you can see that it's working whenever I add a note, but when
02:41I say, delete a note, you'll see that it does not get updated, so if I delete one
02:47on my other iPhone, you'll see that if I only have Remote note on the other iPhone
02:52that Bonus note will not be deleted all my iPhone 5.
02:57So we will have to create away for handling that, if that's what you want.
03:01So you can add every single new added note to iCloud.
03:05But as we move on, we'll handle deleting and editing notes in iCloud to make
03:11sure that all of the data, both local and remote, is as accurate as possible.
Collapse this transcript
Handling deleted notes
00:00So we have already looked at adding notes and having them transfer to other
00:04devices through iCloud.
00:06But what if you wanted to transfer your deleted notes without having the clunky
00:11system of just using all of iCloud's data and dumping it on all of your notes?
00:17So let's go to Constants and look at how to do that by adding a new constant
00:22value called kDeletedNotes, and the value will be the NSString deletedNotes.
00:28Save and head over to Data.m.
00:32And now scroll up to the top where allNotes is instantiated.
00:36What we're going to do here is we are going to create another static property,
00:41and we will call it deletedNotes and we are going to save this to
00:45NSUserDefaults and the cloud, so that way when a note is deleted we can check
00:49against the cloud's deleted notes and see what needs to be deleted from our local data source.
00:57So where it says static NSMutableDictionary allNotes, separate comma and add a new
01:02one, and we'll call this deletedNotes, and what we'll do is copy allNotes, and
01:10if allNotes is equal to nil, and change allNotes to deleted notes, and change the
01:16key value to KDeletedNotes.
01:19Remember, we already have a method for handling deleted notes here in our data class.
01:25So if we scroll down and find that method, which is removeObjectForKey,
01:30now here below where we removed that object, what we want to do is set the
01:35value in deletedNotes.
01:38So type deletedNotes setObject, and the object is going to be the string deleted.
01:43Now this is just something that we have to put in there, we can't put nil as the object.
01:48So I'm putting an NSString.
01:50And since we're never going to recall this value ever again, I'm not creating a
01:55constant for it. Otherwise I would, but this is just a random junk value that we
02:00are really just using the key for.
02:03So we are setting the key as key, and then we are going to save the notes to
02:08NSUserDefaults, so NSUserDefaults StandardUserDefaults, set object, the object is
02:16going to deletedNotes for key, and the key is going to kDeletedNotes.
02:22And on the next line we are going to do the same thing, so I am just going to
02:27copy and paste that last line.
02:29And we are just going to change NSUserDefault, StandardUserDefault to
02:32NSUbiquitousKeyValue, defaultStore.
02:37And now we have successfully updated iCloud when a note is deleted, but what we
02:44want to do is make sure that when we receive our data that's updated from iCloud
02:49that we make sure to delete the right notes.
02:52To do that scroll down to data updated from cloud.
02:54Here I am just going to copy and paste the line of code that grabs cloud data.
02:58I am going to change this to cloudDeletedNotes.
03:04And of course the key is going to be KDeletedNotes.
03:08And I am going to make this easy by copying and pasting this for in loop right
03:13here, and we are going to loop through the keys in cloudDeletedNotes.
03:17And we want to see if the key is not equal to nil in all notes.
03:23So if it's not equal the nil, we are going to remove the object.
03:27So I am going to delete the code after all notes in these brackets, and type
03:31removeObjectForKey, and the key is going to be key.
03:36So if it's inside of all notes and it needs to be deleted because the cloud
03:40says so, we are going to remove that from all notes, then we are going to reload our table view.
03:45So let's save this file, and we are going to test it, and again I am going to
03:49test on two devices.
04:00So now I have my notes loaded up from the cloud on both devices.
04:05Now let's say on my other iPhone that I delete this Remote note that I saved.
04:10So I am going to choose to delete it, and I'm going to click done.
04:14We should be able to wait 10 to 15 seconds and see that on the iPhone 5 the
04:19Remote note is then deleted.
04:22And I can confirm that now.
04:24So by using this method we can control both added notes and deleted notes
04:29on multiple devices.
Collapse this transcript
Handling updated notes
00:00Now that we've looked at adding and deleting notes and syncing them
00:04through iCloud, let's look at how to update notes and make sure that that data is accurate.
00:09Go to Constants.h and define one new constant that we'll call kUpdatedNotes.
00:17And we'll call this updatedNotes, save the file, and head over to Data.m, and in
00:25here scroll up to the top, and we're going to do the same thing that we did for the deleted notes.
00:30So we'll add a new static value called updatedNotes, and then we are going to
00:37copy or re-instantiate deletedNotes in the getAllNotes method, change the name
00:42to updatedNotes, and change the key to kUpdatedNotes.
00:46Now what updatedNotes is actually going to do is it's going to take timestamps
00:52and save them as the objects for the keys that correspond to the notes.
00:57So the keys for updatedNotes and allNotes will be the same, but updatedNotes
01:02will store, instead of the note value as the value, it will store the timestamp of
01:08when the note was last updated.
01:10So we actually already have a method that controls when notes were updated and
01:14that's setNoteForKey.
01:16So below the existing code in that method, what I am going to do is call
01:21updatedNotes, setObject, and the object is going to be the current date.
01:28Remember you can get that with NSDate date, and the key is going to be the same
01:35as the allNotes key.
01:37So we're saving the note each time it's updated.
01:40And then just like before with the deleted notes, we're going to call
01:45NSUserDefaults standardUserDefaults, setObject, and the object is going to be
01:51updatedNotes, forKey, kUpdatedNotes, and then we'll just copy and paste this line
01:57of code to the next line, and change NSUserDefaults standardUserDefaults to
02:03NSUbiquitousKeyValueStore defaultStore, and that way we'll save it to iCloud.
02:09Now we'll scroll down to dataUpdatedFromCloud and copy that NSDictionary, and
02:16paste it on the next line, and we'll change cloudDeletedNotes to
02:19cloudUpdatedNotes. Make sure you change the key value as well.
02:24So make the key kUpdatedNotes. And right below where we do the for N loop
02:30for cloudData, I am going to copy and paste that, and we're going to loop
02:35through cloudUpdatedNotes.
02:37And what we are looking for this time in the if statement is we're checking to
02:42see if the notes are different.
02:47So we have allNotes objectForKey, I am going to delete is equal to nil, and I'm
02:51going to write an extra close bracket which will have Xcode automatically write
02:56the opening bracket, and then isEqualToString, and the string is going to be
03:03cloudData objectForKey:key.
03:08Now member we're not referencing cloudUpdatedNotes, because that doesn't have
03:12the notes, that just has the keys and the date that they were updated.
03:16And then, I want to put an exclamation point
03:19right at the beginning of this if statement.
03:20So what we want to do is check to see if the note that's saved locally is
03:25different from the note that's saved remotely.
03:28If so, then we want to check to see which note is newer.
03:32Now to do that, we're going to have to create two NSDates.
03:36So one NSDate that will represent the date stored in the Cloud, and that will be
03:41dCloud, and that's going to be cloudUpdatedNotes objectForKey:key.
03:47Remember that's the NSDate that we saved earlier when the note was updated.
03:52So that keys are the timestamps, and the object for the keys are the timestamps
03:57of when that note was last updated.
04:00So now we'll go to the next line, and now we need to create another NSDate that
04:04represents the date of the local note.
04:08So dLocal is what we'll call it, and we'll set it equal to
04:11updatedNotes objectForKey:key.
04:16So this is when the note was last updated locally, and this is when note was
04:21last updated in iCloud.
04:23So when that happens we want to compare the two dates and see which one is more
04:27recent, and then update the note based on which is more recent.
04:32So go to the next line, and we want to make sure that neither of these are nil.
04:37So if dCloud == nil or dLocal == nil, we're just going to call a continue and
04:47go to the next key.
04:48Now on the next line, we're going to do an if statement that checks to see which
04:55date is more recent.
04:57So create an if statement, and inside of the if statement we're going to
05:01call dLocal compare:dCloud, and we want check to see if that's equal to
05:07NSOrderedAscending.
05:10And then we want to do one more check, we want to make sure that the cloudData's
05:15note is not equal to nil.
05:22So && cloudData, objectForKey, key is not equal to nil.
05:25So if that's the case and we have more recent data in the cloud, then we're
05:30going to overwrite the local note with the cloud note's data.
05:34So in here some brackets, allNotes setObject, and the object is going to be
05:40cloudData objectForKey:key, and the key is going to be key.
05:48So again, all this code does is check to see if, first, the localNote is different
05:54from the cloudNote, then we grab the dates that they were last updated,
05:59then we check to see which one is newer; if the cloud one's newer,
06:03then we update the local notes based on the cloud notes.
06:08So now I am going to save and test this on my devices.
06:12Now it's important to note that the previously stored notes do not have this
06:18update data associated with them.
06:20So it's a good idea to delete those notes before adding any new ones.
06:25So I am going to run this on both of my devices now, and I'm going to delete all
06:35the notes on both devices even though you should only have to delete them on one
06:40and then wait a few seconds, but it's faster if I delete them on both.
06:44And now what I'm going to do is create a new note on my iPhone 5. Call this New
06:50note, and then when that syncs to my other device, I am going to edit the note on
06:58the other device and watch the update appear on my iPhone 5.
07:04Now again, that should take 10 to 15 seconds for the update to happen. Of course
07:09it could take longer, depending on how the iCloud server is running, and your
07:13connection, and everything else. And there it is.
07:15So now we've successfully created iCloud interactivity to sync notes using key value pairs.
07:23We've looked at using all of iCloud's data as the latest data and overwriting
07:27all local data with that.
07:29We've looked at how to add notes and save them to iCloud and bring them down
07:33from iCloud, how to delete, and how to edit notes.
07:37So using these techniques you can use key value pairs to store data for your
07:41apps and manipulate it in whatever ways work best for your app.
Collapse this transcript
4. Working with iCloud Documents
Understanding the UIDocument class
00:00We've already looked at how to integrate with iCloud using key value pairs.
00:05Another way to integrate with iCloud is by using individual documents.
00:09You can use those documents through the UI document class.
00:13The app I'm working in here is the same as the basic note taking app, except for
00:20I have the MasterView method and MasterViewController that returns the instance.
00:25And again, that instance is set in viewDidLoad.
00:29We did write this code in previous a chapter, so you may already have it there.
00:33If you don't have access to the Exercise Files, just back up what you did before
00:37working with the key value pairs, and just delete in your copy the key value pair
00:44code from the data class and from AppDelegate.m. In order to work with the
00:49documents in the cloud, you're going to have to create a subclass of UI
00:54document, because the UI document class is an abstract class.
00:58So let's create that subclass now by pressing Command+N on the keyboard.
01:02I'll choose to create a new Objective-C class that I'll call CloudDocument.
01:06It's going to be a subclass of UI Document so I click Next, and Create.
01:11In CloudDocument we have to override two methods, one for saving the file and
01:19one for loading the file.
01:22So first we'll override the one for saving the file, it returns an ID and it's
01:27called contentsForType.
01:31The value returned is the value that's saved to the file.
01:35Each Cloud document is associated with one file or file package, which is a
01:42group of files similar to an application on OS X.
01:47Here we're going to return NSKeyedArchiver archivedDataWithRootObject, and then
01:53what we're going to do is get all of the notes from the data class, so this is
01:58going to convert our notes to an NSData.
02:01So I'll import the data class and then pass in here Data getAllNotes.
02:08And again this is called when the file is going to be saved.
02:13So you can control which data is saved right here.
02:15We're going to use one Cloud document and it's going to save all of our notes.
02:21So that's why we're returning all of the notes there.
02:23The other method we're going to your overwrite returns a Boolean value, and it's
02:27called when a file is opened.
02:29And this is going to be called loadFromContents. Again, we're overriding
02:35an existing method.
02:37Here we'll have an NSDictionary, and what we're going to do is unarchive that
02:43dictionary that we archived when we saved the file.
02:46So to do that, we'll create that NSDictionary, and we'll call it dict and
02:52typecast it to an NSDictionary, and we are going to call NSKeyedUnarchiver
02:56unarchiveObjectWithData, and the data is going to be received in this
03:01contents value here.
03:04So that's an ID and we'll typecast it to NSData.
03:09So contents just like that, and then we can return YES.
03:13Now what we want to do is make sure that when the data is loaded and the file is
03:19open, which happens when it's received from the Cloud as well as when we open it
03:25locally, we want to make sure that we update our data object.
03:28So what we're going to do is create a method in our data class that handles
03:33the update to the dictionary, similar to what we did for when we worked with key value pairs.
03:40We'll call this didReceiveCloudData.
03:40It will be an NSDictionary, and we'll just call it d. I'll just create the
03:52skeleton here at the bottom of data.m.
03:54So now we'll call this method in CloudDocument.m once we've loaded the data.
04:00So we have that dictionary, then we'll call Data didReceiveCloudData:dict.
04:07So I'll pass that in there and save CloudDocument.m, and actually we are done
04:13working in CloudDocument.m. So if you want to override UI document, you just
04:17need to implement contentsForType and loadFromContents.
04:21There are of course other methods you could override to handle other events that
04:25happen when you're working with the document.
04:27You can look those up in the UI Document documentation.
04:31So let's go to Data.h and save it, and then go to Data.m and define what happens
04:37when we receive the Cloud data.
04:38Now what we're going to do is just re-instantiate all notes.
04:42So we're going to set the value to a new NSMutableDictionary that is based on
04:49the data that's passed in.
04:50So we'll allocate it and initWithDictionary passing in d.
04:55Of course, you could handle this just like we handled working with key the
05:00value pairs, and you could take this data as the cloud data, you could save your
05:04updates, you could save your removed notes, then you can organize them however you'd like.
05:08Since we already covered that in depth in a previous chapter, I am not going to
05:12cover that again, we're just going to set all of our notes as one file.
05:17But just know that you have the same option, should you choose to go that route.
05:21You can always work with multiple files.
05:24And later on we're going to look at how to detect which files are in iCloud.
05:29So let's go to the next line, and now what we want to do is save the notes, so
05:33we'll call self saveNotes, and then we're going to reload the table view.
05:37So MasterViewController, masterView reload.
05:43I'll save the file. And then just to cover what we did in this movie, so we'll
05:47save data.m, and just remember, when you're working with a UI document, subclass
05:53it, override the methods for loading and saving the files, then you can do
05:57whatever you want with the appropriate data.
Collapse this transcript
Working with cloud document URLs
00:00Before we can instantiate our Cloud document, we're going to need to grab a URL
00:06for the appropriate file.
00:08Let's scroll down, and we're going to create a method in the data class at the
00:12bottom that is again going to be a static method and it will return an NSURL, and
00:17we'll call it notesURL.
00:18The reason why we're creating a method for this is because we're going to be
00:23using this repeatedly throughout our code.
00:26So create an NSURL inside the method called URL.
00:30And then we're going to set it equal to NSFileManager defaultManager
00:37URLForUbiquityContainerIdentifier.
00:41Now the string here that you would pass in is your ID that you put inside of
00:47your project when you connected it to iCloud.
00:50This would enable you to have multiple apps that can communicate with each other
00:55through iCloud data.
00:56So you could have shared documents for different apps.
01:00Since we're not doing that here I'm going to type nil.
01:04On the next line we're going to return url URLByAppendingPathComponent.
01:11The path component that you append is going to be the name of the file or folder
01:16that you want to save.
01:18And in this case we're going to end up using a file.
01:21But I want to show you how you can control the visibility of these documents.
01:26There is a folder called Documents, and this folder in the cloud contains all
01:33the files that are publicly accessible and editable by the user with that iCloud account.
01:40So if you want it to be a public file, public meaning visibly editable by the
01:45user, then you can put it in the Documents folder, or a subfolder of that folder.
01:50But if you want it to be a private document, then you can put it outside of
01:54the Documents folder.
01:55I am going to replace that NSString with kAllNotes.
01:59So it's actually just going to be the URL to a file. So the UbiquityContainer
02:04gets us our iCloud container, the Document subfolder is going to contain
02:09publicly visible documents.
02:11The Document subfolder contains documents visible to the user, and files outside of
02:16that folder are not visible to the user, and that's what we want to do here, so
02:20that's why I have kAllNotes.
02:23So just remember that when you need to get the URL for your cloud data, you call
02:28NSFileManager defaultManager, and its method URLForUbiquityContainerIdentifier.
Collapse this transcript
Opening, closing, and saving documents
00:00Now that we have everything set up we are going to look at opening and saving
00:05and closing a UI document.
00:08So we go to Data.m and we are going to import CloudDocument, and then
00:14we'll create a static property that will be of type CloudDocument, and we'll
00:20call this cloudDoc.
00:23Scroll down into getAllNotes, and right below where we instantiate all notes,
00:27we'll instantiate cloudDoc, and that's going to be CloudDocument, we are going to
00:34allocate it, and then we are going to call initWithFileURL, and the URL is going
00:39to come from self notesURL.
00:40So remember that's the URL to the file that we want to save.
00:45Then, what we are going to do is try to open that file, and if we can't open the
00:50file because it doesn't exist, then we are going to create the file.
00:55So call cloudDoc openWithCompletionHandler, press return to have Xcode write out
01:01the block for you and then, and then in here we want to see if success is false.
01:06Remember success is going to return true if the file was loaded successfully.
01:10So if it wasn't loaded, and this is probably going to be because the file
01:14doesn't exist, we're going to create the file.
01:18So in brackets, cloudDoc, saveToURL, and we're going to use the same URL, self notesURL.
01:27For save operation, and that's going to be UIDocumentSaveForCreating, and the
01:34completionHandler is going to be nil.
01:38Of course, you can select it and press return if you want Xcode to write the
01:41block and you can handle the successful saving of the file, if you'd like, but we're
01:47not going to do anything when the file is done saving.
01:50So I'm just going to pass in nil here.
01:53So that's how we open a file by calling openWithCompletionHandler, and we save
01:58the file by calling saveToURL.
02:01If you want to close the file, all you do is call
02:06closeWithCompletionHandler. Then of course
02:10you can write whatever you want to do when the file is closed.
02:13And when we look at later deleting a Cloud document, you'll need to make sure
02:17that you close that file before deleting it.
02:20So I'll delete those lines of code.
02:22So we could save the file now, but we're actually not going to see anything
02:26interesting until we start modifying the Cloud document, which we're going to look at later.
02:30But for now, I just want you to remember that when you initialize a UI document
02:35subclass object, you're going to call initWithFileURL, you can open the
02:40file using openWithCompletionHandler, and you can save the file using saveToURL.
02:46And finally, you can close the file using closeWithCompletionHandler.
Collapse this transcript
Tracking changes and autosaving documents
00:00UI document has autosave functionality. All you have to do is tell the document that
00:06a change was made, and UI document will automatically determine a good time to
00:12send the data to iCloud. Then other devices are notified of the updates and they can
00:17display the updated information.
00:20So let's scroll down, and all we have to do is write a few lines of code to check this update.
00:25So find setNote for key, and under the existing code what we want to do is check
00:32to see if the note is not a default note.
00:35So use the not operator, and check if note isEqualToString kDefaultText.
00:43So if it's not a default note, it's either a new note or a changed note, then what
00:47we're going to do is tell our document that a change has occurred.
00:54So cloudDoc updateChangeCount, and then we pass in the ChangeKind.
00:59As you start to type UI document you will see the different ChangeKinds.
01:03The ChangeKind we're putting in here is ChangeDone.
01:06So a change has been made and completed.
01:09So we're letting the document know that, and when UI document determines that
01:14it's a good time to upload the data to iCloud, which typically happens a few
01:18seconds after you call this method,
01:20the data is then uploaded and sent to the other devices.
01:25Copy this block of code we just wrote, and go to removeObjectsForKey, and
01:30make sure to paste it where we remove the object. So above allNotes
01:34removeObjectForKey.
01:36Now of course, the note is not passed in just a key, so we need to grab that key
01:41value from all notes.
01:43So allNotes objectForKey:key.
01:46So we're checking to see if the actual note is default text.
01:51If so, then we're updating the change kind again. And if we wanted to we could
01:55save the file and save notes, but you actually don't have to and in my
02:00experience of doing this,
02:02I've not noticed any kind of performance increase either way with that code in there.
02:09So of course, if you want to, you can choose to save the file there explicitly,
02:13but if not, it will end up saving within a few seconds anyway.
02:17So let's save the file, and now we should be able to test this on both of our devices.
02:27Now when the apps launch, create a new note on one device, and then go back to the
02:34master view controller, and wait for 15 to 30 seconds and see if that change ends
02:41up going to the iCloud server and onto the other device. And there is the example
02:48note that I created on the other device.
02:50Now let's delete that note on the second device, and wait another 15 to 30
02:56seconds and see if that note gets deleted on the original device.
03:02And sure enough, after a few seconds, the note is deleted. And let's do one more thing.
03:07We'll create another note, I'll do that on my iPhone 5, and when the note is
03:14added to the other device, I'm going to make a change to it, and when the change
03:19is made I want to confirm that the change is updated on the original device as
03:23well. And there's the note on the second device, I'm going to add some text to
03:28it, go back to the master table view to where the change is saved, and then I
03:34should see the updated note on the original device.
03:37So now we've successfully seen that we can create, update, and delete notes and save
03:44them through iCloud using the UI document class.
03:48Just make sure that when you make a change to your data that you call the UI
03:52documents updateChangeCount method, and that change will be sent to all the
03:57devices with the same iCloud account.
Collapse this transcript
Handling document metadata events
00:01Our app only has one cloud document. But let's say you wanted to create an
00:06app that had multiple documents, and you wanted to track all of those
00:11documents saved in iCloud and open the ones that you want. To do that, you
00:17can query the metadata.
00:19Now the metadata is something that gets updated frequently, so your app can check
00:27the iCloud metadata that's constantly being updated from iCloud to see what
00:31files exist on a server without downloading them.
00:35Then you can download whichever files you want as necessary.
00:39So let's look at how to read the metadata from iCloud.
00:42Create a static property in the data class, and it's going to be of type
00:48NSMetadataQuery. We'll call this query.
00:58Scroll down to below notesURL, and we are going to define a method that is again
01:06a static method that will return void that's called loadCloudMetadata.
01:14Now what we'll do in here is we're going to instantiate the queries.
01:17So query, then we'll allocate an NSMetadataQuery. Then we'll allocate and initialize it,
01:30standard initializer here.
01:32And then, what we're going to do is set the search scopes.
01:35Now this line of code is not necessary if you don't care where the file is.
01:42So then I'm going to call query setSearchScopes. What is asked for is an NSArray.
01:47You actually don't even have to write this line of code if you want to search all
01:52of the areas in iCloud. That is, all the areas that are available to this
01:57application in iCloud.
02:00Or you can specify that you want to search only in the viewable documents by the
02:04user, which is files in the documents folder, or only files that are not viewable
02:09by the user, which is outside of the documents folder.
02:11So let's create an NSArray in here, and we'll type arrayWithObjects, and if you
02:19want to include the documents folder you can type NSMetadataQuery DocumentsScope or
02:29DataScope. DocumentsScope is again in that documents folder and in all
02:34subfolders, and then the other one is NSMetadataQueryUbiquitousDataScope, and
02:40that's outside of the documents folder.
02:42So I'm going to put both of those in there. And again, if you don't put this line of
02:47code, then it will automatically search in both of those areas.
02:49So this just allows you to control what is searched for metadata.
02:52Next we'll create a value of type NSPredicate.
02:55This is going to specify our search criteria for files that we're looking for.
03:01So we'll call this predicate, and we're going to set it equal to NSPredicate
03:07predicateWithFormat. And now here, we'll create an NSString, and what we can do is
03:15pass in some values in search parameters, and I'm not going to go into what all
03:21of them are here, but you can look up NSPredicate and find out all of your
03:25options for specifying which documents you want to search for.
03:28I'm going to type a percent sign and then a capital k, and then LIKE and '*'.
03:35And what this is going to do is find all files that are LIKE '*', which is a
03:41wildcard character, so all files.
03:44And after that closing NSString, I'm going to type a comma, and then
03:47NSMetadataItemFSNameKey.
03:53So I'm going to go up. So what it's doing is, this is a key that represents the
03:59name in the file system, so it's going to grab any file that's saved in the
04:04metadata. And that %K just refers to a string, but this is going to be a compatible
04:10string with the NSPredicate protocol.
04:13So again, you can look up NSPredicate if you want to find other ways to
04:17search for items in there.
04:20So I'll go to the next line and we're going to set the predicate, so query
04:24setPredicate, we'll pass in the predicate, and then we are going to register for
04:30the notification when the metadata query finishes gathering information.
04:37So [[NSNotificationCenter defaultCenter]] and then I'm going to call
04:46addObserver, selector, name, object. The observer is going to be self, the
04:54selector I have not defined yet so let's define it right now.
04:58Go down below this method and define a method called fileListReceived that
05:05receives an NSNotification that we'll refer to as notification.
05:10So we're going to call that selector when the notification is posted.
05:18So the selector is going to be fileListReceived, and the name is the event that
05:26we're listening for and that is NSMetadata QueryDidFinishGatheringNotification.
05:28Notice that there is also DidStartGatherNotification, DidUpdateNotification, and
05:37GatheringProgressNotification.
05:40So if you want to listen for when metadata is updated you can use the
05:43UpdateNotification and respond accordingly there.
05:47So now the object is going to be the query, the last thing we are going to do in
05:51this method is called query startQuery.
05:55So this is going to search for the metadata. And when the file list is received
06:01we can respond in here. So NSArray queryResults, and we're going to set it
06:11equal to query, which saves all the metadata information after we call query
06:16startQuery, results.
06:19So that gives us our array, and then we can loop through it to check the value.
06:26So I'll use a for N loop, and what I'm looking for is an NSMetadataItem, which
06:36we'll call result in queryResults.
06:44You can access the information inside of the metadata item through a list of keys.
06:50Again, you can always look in the NSMetadataItem reference to find out all of
06:55those keys, but for now we're just going to look at one. Create an NSString.
07:00And we're going to call this fileName, and we're going to set it equal to result
07:08valueForAttribute and NSMetadata FSNameKey.
07:11So that's going to be the name of the file, and now we're just going to log the
07:20file, so we can see that we can access the different files within iCloud. So type filename:
07:27and %@ symbol for an NSString, and then filename.
07:31So this is going to display the filename that's inside of our metadata, which
07:38represents all the files that are saved in iCloud.
07:41So when we instantiate our Cloud Document, I'm going to call self
07:48loadCloudMetadata. And we should see, when I test this, and I'm not going to
07:54show you my phone right now, I'm going to test this on my phone though, and
07:58what I want you to look at is inside of the bottom view in the output window,
08:03I want you to look for the name of the file, and that should show and it should
08:07match the key for all notes.
08:10So once the file is loaded, the output window will then update with the loaded
08:14metadata showing the filename of the file of that we're working with in iCloud.
08:20Of course, if you wanted to, cut and paste this Cloud loading file inside after
08:28your metadata is loaded.
08:29But since we only have one file we're working with, its not necessary to do at this time.
08:35So what I'm going to do is comment out the log statement and the NSString
08:39statement, and if you choose to do anything with a metadata with multiple files
08:43you can get all the information about all the file stored in iCloud here. So just
08:48remember to use metadata queries, instantiate the query, set the searchScopes to
08:54where you want to search, set the predicate, which is the search conditions,
08:59listen for the notification, and call query startQuery.
09:04Once you're done querying the data, and you're positive you're not going to be
09:07using that query anymore, then you can call query startQuery.
09:13I'll put that at the end of fileListReceived and save the file.
09:17So by working with our metadata, we can find out when our metadata is updated,
09:22check against all the files that exist in iCloud, load the appropriate ones that
09:27we want through this for loop, using the pre-existing key result values.
09:34That way you can keep your app updated using the document information that's in
09:38iCloud and load information as its necessary.
Collapse this transcript
Deleting a document from iCloud
00:00Let's say at some point you're managing an app that has multiple cloud documents
00:06and you want to delete one of them.
00:08It's actually a fairly simple process. Above where we instantiate cloudDoc in
00:13the getAllNotes method of the data class, I am going to type double brackets and
00:18then NSFileManager defaultManager removeItemAtURL, and the URL for our notes is
00:25self notesURL, and for error I'll just put nil.
00:31Now to check to see that the file was actually deleted, inside of the completion
00:35handler for cloudDoc's open method, I am going to go inside of the if statement
00:41that checks to see if the file did not exist, and we create the new one.
00:46I'll put an NSLog in there, and I'll type new file created.
00:53This is only going to log to the console if a new file was created.
00:57Let's save this, and what I am going to do is comment out the line of code that
01:03removes the item, and I'll run the app on my phone and make sure to confirm that
01:08it does not say new file created once the app is loaded.
01:12If it doesn't say new file is created in the log window, then that means that the
01:16file exists on the server.
01:18So now I'll delete that comment and test it again, and now I should see new file created.
01:28And in the log window, sure enough, I see new file created.
01:32So, if you want to remove a cloud file, all you do is call the
01:37NSFileManager's removeItemAtURL, passing in the appropriate URL for the item
01:43that you want to delete.
01:45Also remember, before you delete a file, make sure it's closed, which you can do
01:50by calling the UI Document's closeWithCompletionHandler method.
Collapse this transcript
Conclusion
Next steps
00:00I'd like to show you where you can find more information about developing apps for iCloud.
00:06The first and most obvious one is the iCloud design guide, which you can find in
00:11the iOS Developer Library.
00:13Here on the Table of Contents you can see all of the topics that are covered, and
00:19some tips that Apple gives you for developing apps that utilize iCloud.
00:22If you're curious about more detail, about anything that we've talked about
00:28throughout this course, you can find that here.
00:31Another way that you can develop iCloud applications is by using Core Data.
00:36Now unfortunately at this time, the sample code that I've looked up that's
00:40provided by Apple has been known to produce bugs. And I've seen this on Apple's
00:46developer forums that iCloud with Core Data is not quite ready for prime time,
00:53because it doesn't work exactly as expected.
00:56If you'd still like to work with Core Data in iCloud, you can use the Using Core
01:00Data With iCloud Programming Notes in the iOS Developer Library.
01:05Here you can get some tips to get started working with iCloud and Core Data.
01:11This link here is a developer forum that has the Apple sample code for using
01:16Core Data with iCloud.
01:18So here you can download iPhoneCoreDataRecipes and make the appropriate
01:23adjustments, and see if this is sufficient for your apps in working with
01:27Core Data in iCloud.
01:29Again in this forum post you're going to find page after page of developers
01:34discussing bugs with working with Core Data and iCloud.
01:38That's why I chose not to teach how to do it.
01:42Finally, this last link with working with Core Data in iCloud is one developer,
01:47dlpasco, who posted an adjustment to the Apple recipes for Core Data, which is
01:55supposed to address many of the bugs mentioned by the developers.
01:59So if Apple's provided developer recipes aren't enough to get you working your
02:04app in iCloud Core Data, you can try this code as well.
02:08Again, there are many in the iCloud developer community who feel that Core
02:12Data with iCloud is not quite ready to integrate and publish applications at this point.
02:19But should you decide to give it a try, these are some resources that you can use.
02:24If you have any questions for me personally about this course, you can contact me
02:28on Twitter at asktodd.
02:30Also, I would like to see any apps you develop using what you learned in this course.
02:35So feel free to hit me up there and I'll make sure to get back to you.
02:39Thanks, and I'll see you next time.
Collapse this transcript


Suggested courses to watch next:


iOS 6 SDK New Features (2h 29m)
Simon Allardice


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 104,069 instructional videos.

get started learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 2,024 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.

preview image of new course page

Try our new course pages

Explore our redesigned course pages, and tell us about your experience.

If you want to switch back to the old view, change your site preferences from the my account menu.

Try the new pages No, thanks

site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked