Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2012-218
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 218
$eventShortId
Shortened ID of event: wwdc12
$year
Year of session: 2012
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Session 218] Using iClou...

WWDC12 • Session 218

Using iCloud with UIDocument

Essentials • iOS • 54:06

UIDocument provides a powerful way to adopt iCloud Storage on iOS. Learn how UIDocument can help your document-based app more easily adopt iCloud Storage and understand the key concepts for working with documents and storage in iCloud.

Speaker: Luke Hiesterman

Unlisted on Apple Developer site

Downloads from Apple

HD Video (607.7 MB)

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

We're going to deal with this sort of big topic that is building document-based applications, and in particular, building document-based applications that are going to live in iCloud. Because if you're going to write a great document-based application on iOS, you're going to use iCloud. So it's a big topic, document-based applications, and in particular in iCloud. But we're going to try to hit on some key points that will help you write better document-based applications using UIDoctument.

So how we're going to do that is walk through actually building an app. We're going to walk through some of the key steps today in building a working iCloud application. And the main areas we're going to focus on in building that app are application design, things we can do up front to ensure that our application is well-tailored for working in iCloud. We're going to talk about document design, which is the thinking that we have. We're going to talk about the things that we need to put in to making sure we have a well-tailored format for writing our data to storage and syncing up to the cloud.

And lastly, we'll talk about finishing touches, things to make sure we have taken care of in our application before we ship it to users. So I'd like to start out today by actually showing you the app that we're going to be working with. And this is version 2.0 of the CloudNodes app that some of you may have seen. In last year's demo. Here's my application, CloudNodes.

And it wants to know if I want to use iCloud. I, in fact, do. And this application presents me with a list of note documents. I have a couple. And each note document has -- contains within it several note records. So a note document is a list of notes where each note has a -- has a picture associated with it and some text. In this app, I can create new documents. I can make some stage notes for myself. You know, something like, you know, talk loudly. I have a microphone for that, thankfully. And, you know, enunciate.

And that's the kind of thing that this app does. I can go back, close it, save it, reopen it, my data's there. Pretty simple application, document-based. And we're going to spend the rest of the talk talking about the main -- the key pieces going into building an app like this. So that's what we can look forward to. First step in this is application design.

We want to determine what are the key steps that we want to take care of up front when we're first building our application to ensure that we're in the best place to work with iCloud. And the first step in this is deciding where are we going to store our documents.

This is a very key question if we're going to be a document-based application. Where do our documents live? And our basic choices are files are going to live locally or they're going to live in the cloud. And the message that I really want you to take away from this talk is let your users make this decision. Documents are either stored locally or they're stored in the cloud.

And we want the decision when we ask the user to make it to be as easy as it can be. So we want to ask them once, like the first time that they launch the application. And when they decide, it's completely all or none. They say, yes, I want to use iCloud. Then you're going to store all of their documents in the cloud from that point on and not even ask them again.

One of the things that this implies is we don't want to necessarily think about not only storing some in the cloud and some locally, but also let's not worry about... trying to sync, having mirrored versions of our documents locally at the same time that we're putting in the cloud. It's a very simple user experience to understand. We're just going to put everything in the cloud or everything locally if that's what they choose and not try to maintain local versus cloud versions. If they sign out of iCloud, they won't be able to access their data anymore.

But that's fine. They'll be able to sign back in and access their data if they so choose. They can understand that. So we want to have something that looks like this. On the first time. They launch their application. You don't need this exact UI, but this is the rough idea of asking your user, hey, do you want to use iCloud? They decide yes or no. You don't have to ask them again.

After you know what you're going to do, and we're going to assume that what you're going to do is use iCloud for the purposes of this talk, let's talk about how you write sort of the bootstrapping code in your application, the stuff that you do up front so that you're prepared to access iCloud Storage. And many of you understand that as accessing URL for Ubiquiti Container Identifier. So this is work we're going to do in Application Did Finish Launching.

Right when your application gets started, we're going to call a new API that we introduced in iOS 6. And this is NSFileManager Ubiquiti Identity Token. And the reason we introduced this API is because the URL for Ubiquiti Container Identifier can take up to a few seconds to complete. Many of you have run into this problem. And it's not something that's safe to call on the main thread.

And it doesn't guarantee a quick answer to the question of, is iCloud? So this is a method you can call safely in your Application Did Finish Launching method that if it returns non-nil, that tells you iCloud is in fact enabled on this device. So you call this right away in Application Did Finish Launching to know if iCloud is enabled. After you've determined whether it is enabled, you register for another bit of new API in iOS 6. A notification, NS Ubiquiti Identity Token did change notification, that tells you whether the aforementioned token changed. And it tells you when it changed, rather.

So if you launch your application and the user signed in, but later they sign out, you'll get a notification that tells you that. Or alternatively, if your application launches, but the user hasn't set up an iCloud account yet, if they sign in later in the running of your application, you'll get a notification so you know that they signed in. And then maybe you can deal with iCloud at that time. So let's go ahead and assume iCloud is enabled. The user has an account.

Now we have to access URL for Ubiquiti Container Identifier. This is where we're going to get the actual location in storage where we store our data. And as I mentioned, since I can take up to a few seconds to launch, it's only safe to call this method on a background queue.

We don't want to call it on the main thread lest we potentially lock up the UI. So what makes this sort of interesting is it might take up to a few seconds, but it also might be fast. We don't actually know. This is sort of undefined from the application's perspective whether it will be really fast or a little bit slow.

And so we have to do a little extra work to accommodate for this sort of known unknown in that if it does take a long time, we want to put up some UI so the user knows what's going on because we're not going to allow interaction with our application until we have that cloud storage identifier.

And so what we do is we start some kind of timer. And we're looking to see how long it takes to return from this method. If it takes some non-trivial amount of time, say 150 milliseconds, we hit a timeout. And we say, OK, it's taking a non-trivial amount of time. We want to display some UI to the user so that they, in fact, know what's going on.

The reason we have this timer is because if it does return quickly, we don't want to flash any UI on the screen. That'll just be distracting. But if it, in fact, does return quickly, we don't need any of this. We're gone. Don't flash any UI to the user. And we can move on with life. We have our URL. And we can use it.

So with that, I'd like to show you actual code that we will use in Cloud Notes to bootstrap our application. So this is Cloud Notes 2.0. And you'll notice on the left side of the screen, we've got our source files. And I just want to give you a quick rundown to begin of what you see here.

We have an application delegate. We're going to need that. We're going to handle application launching there. We have a note document. That's our UI document subclass handling our reading and writing of data. I wrote this extra little class here that I call Cloud Manager that, from an application design perspective, I did this so that I could just have sort of a pulled-out abstraction of the non- UI-related bits of interfacing with iCloud.

This is actually where the code lives that goes and accesses the URL for Ubiquity Container Identifier. And then it will store that value within itself. It's a singleton class. So that it is globally accessible. And any other pieces of my application will be able to use that object to determine the URL for Ubiquity Container Identifier because it will just store it.

And other bits of my application can access it directly and synchronize. So I'll just show you the API actually that I've put here in my Cloud Manager. It has a property to tell me if the cloud is enabled. And the semantic of that is, one, I determined that iCloud is enabled on this device and the user selected to use iCloud. And if both of those are true, this will be yes on my little singleton class.

And then it has a property that tells me where the data directory is and the documents directory as derived from the URL for Ubiquity Container Identifier. And for those of you who maybe don't know, the distinction between a documents directory and data directory is documents is where user visible documents live and data is where your application metadata lives.

And to make this sort of extra convenient for my application, this Cloud Manager will automatically make the data directory and the documents directory be the right thing whether I'm using iCloud or using local storage. So it will return a local URL if I'm using local storage. And it makes the architecture of the rest of my application easier.

And then I have a root view controller, which is the thing that shows my document list. So right now we want to look at what we do in application did finish launching. There's a few steps we're going to walk through. And actually, you know, before I run this, I'm going to go ahead and just run the application on my device. And we'll see where we are before implementing this iCloud code. Launch Buddy. There we go.

Okay, so in this state, I haven't implemented the iCloud stuff, so my application is just showing local notes. I can -- even though I'm not using iCloud, I can add something local. So, you know, I can add a local file here in the local zone. I forgot to set something up on this app, so that doesn't work. That's a setup problem with my phone. Sorry about that. But so I'm in just the local state. And I'm going to go write some code to enable iCloud. Let me kill that.

And so let's go. Ta-da! Back to my machine. Good! And so let's walk through the steps of this. The first thing I'm going to do is check what the user's iCloud preference is. This is the idea for the very first time we launch our application, we want to know whether the user is going to use iCloud. And it's also called setup if necessary because we're going to set up our iCloud storage if the user is in fact using iCloud and it's enabled.

Okay. So I'm also going to register for a couple of notifications. And two of these notifications are ones that I've defined myself. They're container will begin fetching and container fetching did end. These are actually defined by my Cloud Manager with the idea that this is how I set up that timer for the UI we talked about earlier.

When the Cloud Manager starts fetching the ubiquitous container identifier URL, it sends a will begin fetching notification. My app delegate, which is responsible for the UI, will start a timer. And if it does not receive the fetching did end notification, by the time the timer times out, it will put up UI. If it has put up UI, it uses the did end notification to tear down that UI.

And let's look at the implementation of this sort of critical method, check user iCloud preference. And set up. All right, great. So this is, as I said, the first thing we end up actually doing is asking for the Ubiquiti Identity Token. This is our new bit of API.

Then we're going to check our user defaults because we're going to use NSUserDefaults to store the user's preference as to whether they use iCloud or not. And so I'm asking for a key that I've just called use iCloud Storage. And I'm going to skip over this a little bit of code later because we're going to use it in a subsequent demo.

But then I check if my -- If my token is valid, which means the user is signed into iCloud,

[Transcript missing]

So the things we're going to talk about are using file packages here, things to avoid storing in your document, how to have sort of forward-thinking design principles, and we're also going to talk about how to design a document that is able to have previews. As you saw when I first showed the application, my document had previews.

That wasn't true in the last demo, but we'll get there. So first of all, file packages. This is really the biggest thing about document design that you want to take away. If you're not using file packages today, then it's something I hope you will be thinking strongly about when you walk out of this talk.

And what file packages are is a directory on the file system that is treated as a single document to your user. And this has some advantages. At a very simple level, it makes it so that you don't have to take maybe disparate parts of the user's data and shove them into a single file blob.

But more than that, it can make your application perform better, because when you change just a single part of your document, if that single part is represented by one little file inside your larger file package, we only have to write that one little file out to the NAND storage. And likewise, we only have to upload that one file into the cloud. So we use less network traffic, and our saving action can be faster.

So let's talk about how we do that. If you have a, you know, if we started with a note document and we think of, okay, we've got all this content in a note document, and maybe in our first iteration of writing our application, we just took all that content and we wrote it out into a file, and we have a file blob.

And we're thinking, you know, I think I can make this better by moving the file packages, but how do I do that? Well, the first step is breaking things out into logical components. Since a note document has several notes associated with it,

[Transcript missing]

We can actually go a step further.

We can think about separating assets from content, which is to say, if you think about assets as things like images, movies, things that would be treated as attachments in your document, that are separate maybe from the core content, like the text. In the case of Cloud Notes, each note itself has text associated with it and an asset, which is the limits that you see.

[Transcript missing]

And when the user edits, say, the text, we can just save out that file that has the text for that one note. And we save out one file out of ten, one little text file. And we don't have to spend the time writing out all of that content, nor do we have to spend the time syncing that content up to the cloud. Another thing you can do is try to separate out things that are edited less frequently.

This also applies to actually things that are edited quite frequently. If you have knowledge about your application that a particular part of the document is rarely edited or there's a particular part of the document that is edited all the time, those components are candidates for moving out into their own file. So if you have something that the user edits constantly and you can stick it in a nice separated component from everything else, then when the user makes those edits all the time, you're only just writing out that little component. So it'll help you.

Now let's think about the actual code for reading these file packages. If you've never actually done this before, maybe you'll find this helpful. So this is how you do this in a UI document world. We have our canonical reading method in UI document, which is load from contents of type.

And when we're using file packages, that type -- or rather the contents will be a NS file wrapper. That's what we give you when you're using a file package. And the way to put this into your model is to have sort of a master file wrapper that you keep a reference to. It can be like an instance variable in your document.

And you just take the contents, which is a file wrapper that we've given you, and assign that to your instance variable. So now your model, which is a master file wrapper, is the contents that we've given you. You can also use an index file that is part of your document file wrapper that helps you understand what all everything else is in this document.

In the case of Cloud Notes, I've got, you know, some arbitrary number of notes inside a note document. And I use an index so I can -- that index will tell me how many notes are in there and what their file names are, how I can access them.

So you can peer inside the index to know what you actually have in your file package. And right away when you load your contents, you just kind of load your little index into your model. So there you've got your model. When it comes time to write out your model, the way that that works in UIDoctument is we always want you to snapshot your model and return to us a snapshot that we can write to disk. that we can write to disk.

So when you're using this sort of master file wrapper as your model, then snapshotting it is really just a matter of creating another NSFileWrapper instance with the contents of the sub-file wrappers of your master file wrapper. So that will act as a snapshot of your data that we can take with us and write to storage.

This is actually where the NSFileWrapper smarts kick in and optimize the behavior for you because NSFileWrapper is smart enough to notice subfile wrappers that haven't changed and it won't bother writing those out to storage. So only the stuff that you've changed will actually get written out at this point, and that's what makes it fast.

In between reading and writing, how you manage your model is as changes come in, you make those changes directly to that master file wrapper that we've saved off, and that's what we're treating as our model. So if I add a new note... I create new file wrappers, add them inside my master file wrapper, and I update that index file, and then I move on with life.

And part of the way that UIDocument is designed is that we only save when we're asked to. So we will update that file wrapper that we're containing as our master file wrapper, and then wait for contents for type to get called, which will be invoked when autosaving happens or when the user closes the document.

[Transcript missing]

And really what this is going to be all about is thinking about the fact that when you write version 2.0 of your application, you may also write version 2.0 of your file format. If you decide that there's extra data that you want to store in the document, or you want to store your existing data in a different way, You want a way that your previous version of your application can deal with this in a graceful manner.

And the way that we do this is version your file format. This is something that we want you to write into your document format today so that you have a field and if you call your document format version 1.0, you have that field that you can mark up to 2.0 in a future update in your application.

And this is especially important in today's world of multiple devices because it's likely that you're going to run into a situation where the user doesn't update your application simultaneously on all their devices. So you can very easily run into a situation where the user is actually using different versions of your application on different devices and all of those versions need to be able to coexist as gracefully as possible. So that's what file format versioning will help you with.

And you can... Make your versioning help you as much as possible by defining levels of interversion compatibility. And what that means is maybe in an ideal world, you make a change to the file format in version 2.0 of your application and your document format. But you do so in a way that version 1.0 of your application actually is able to safely read and write that document.

So you can actually, in addition to the format version, have a field also contained in your document format that tells previous versions what they're able to do with that. And it might say, hey, this is actually read-write compatible with version 1.0 of my application. And one of the ways you can get to that place is by designing version 1.0 of your application to ignore fields that it doesn't understand. So if it sees a field in your document that... that you didn't have code for in 1.0, the version 1.0 of your application should probably just not touch that field.

That will give you the best chance to be able to write updates in the future that are able to write out new fields and version 1.0 of your application can actually read and write that document without screwing things up. So that's how you can get there. In some cases, that won't be possible.

You'll make changes that... that the old version of your application won't be able to safely write out. But you might still be in a place where it can safely read version 2.0 of the document format. That's another marking you can make in the file format itself. You can say, this is version 2.0, but version 1.0 of my application can safely read this file. And so version 1.0 can then do the right thing in terms of being able to read it, display it to the user, but it won't let the user make changes to that document.

And of course, in the worst case scenario, we make an update to our document format that is simply not compatible between versions. And then we mark that in the file format itself. So version 1.0 of our application comes along, reads a file saved by 2.0, says, oh, this is 2.0 version of the file format, I don't understand this.

And more than that, I was told by the info I find in this document that I shouldn't try to read this. So putting that information, putting those fields in your document format today will help you tomorrow. Let's talk about things not to put in your document, since we've just talked about great things to put in your document.

One is maybe not so obvious. Try not to save scroll positions in your document format. And what I mean by that is like UI scroll view content offset. That's something we want to avoid saving in our document. There's a lot of reasons for this. One is very simply like between different versions of your application, different versions of the iOS software, and different devices perhaps, then the scroll view position can change.

And you wouldn't want to be stuck in a situation where the user is opening a document and you're writing data out just because they opened the document. That's something we want to avoid. And so to sort of go along with that. We also wouldn't want to save the timestamp for the last time the user opened the document.

One bad situation that this particular practice can get us into is imagine that we have one instance of our application running with the document open. And we take another instance of our application, open that same document. And our new instance says, hey, I've just opened the application or I've opened the document. I'm going to write out a timestamp for this. And this already running instance of our application sees an update because it comes across the cloud to this document. That we wrote out the timestamp.

So it reopens the document. And because it did that, then it rewrites a last open timestamp. And then that goes to the cloud, back to the other device. And now they start fighting for who gets to write the last open timestamp the most. And that's going to chew up network bandwidth, chew up CPU, chew up everything. That's a bad situation. So the moral of that story is avoid anything that can result in a sync loop.

Last thing we want to think about here is if you have any sensitive data that can be stored in your document, think about this if your application has a means of publishing the document. This is pretty common that we have a way to put a document on the web, email it to somebody just with a couple of taps.

And if your application has functionality like this, and yet you have information in there like an undo stack or user information, any kind of user information that people wouldn't necessarily want to publish along with their document, then it's a good idea to think about having a step that strips out this kind of information.

So one feature that people ask how to do a lot is having a document preview that they can access on their device so that they can have something like a document chooser that shows previews a la iWork without having to download all the documents. Because the basic story in iCloud is you have to download a document before you get to access any part of it. So we're going to talk about now how to write that. So in the beginning, there was your documents. And they were in the documents directory.

What we want to do to create a preview is move over to the data directory, which is where we put application metadata, and create preview files over here. And we want to establish a one-to-one relationship between preview files and documents, and that relationship is mapped to by the document name.

That way, just by discovering the document, then we can also know the URL where its preview file is. And once we have this, we know our file list from an NSMetadata query, and then we can access the URL for the preview file and download just that rather than downloading the entire document.

You might have a 200-megabyte document file, but you've got a nice thumbnail image that you include -- that you write out as a preview image that's only a few K, and you can just download that and display it to the user in some kind of document chooser and not download the whole document unless and until they choose to open it.

So let's look at actual code that will do that for you. So we're going to look in the UIDoctument low-level writing method, write contents and attributes safely to your URL. This is a method on UIDocument that you can override that is called in a background queue UIDocument's managed file access queue.

So you're safely in the background. And in here, we want to, using that method, write out a preview using the preview URL that we've established and write it to that URL. Now there's one thing. I had an updated version of this slide, which I guess I didn't include in here.

So take note of what I'm about to say, because it's actually important. The part that is highlighted right now should be in NS file coordination. So you want to create an NS file coordinator instance and do coordinate for writing. You want to coordinate the write out of that preview data as you would anything that's in iCloud storage. So we're working on pushing out to you actually the code for this Cloud Notes app that I'm showing to you. And in that code, this is done right. So take note of that. So I'll show you that now.

So, if we go over to our note document, and this is where we're going to write out the content, I have this little method up here that returns me a little NSData of preview JPEG that I'll be able to use for my preview data that I'll write out to disk. And then the code here is essentially what we just saw on the slide. After I have called super, which will write the actual contents of my document out, if that has succeeded, I will go ahead and create a URL.

In my case, I simply append .preview to the end of the document name, and I do store it in the data directory rather than the document directory. So if I have note1.note, that's how I use the .note extension for my files, then the preview file is called note1.note.preview. And I put that in the data directory. And then I actually... I properly create an NS file coordinator and coordinate writing to write the preview file out. And that's just taking the data and writing it to the proper URL.

The other bit I want to show you is how to handle this on the other end, the receiving device. And that's going to be in my root view controller, which has a table view that shows all my files. That's my version of a document chooser. I have this very simple self-wrote index path.

And I'm going to put the preview image in the cell's image view, but I start out just by setting it to nil, and then I'm going to call this method, which is load preview for index path, and this guy is a little asynchronous operation that goes and fetches the preview image and will apply it to the cell. So I'm going to do that, and then let's go look at that code.

In here, I go look at my model, which is this little file representation, and the file representation will tell me the document URL for each row, and also the preview URL associated with it, which I know because I know to take note1.note to note1.note.preview and look in the data directory for it.

[Transcript missing]

And I also have this little guy, which is a dictionary of preview loading operations, this actual NS block operations.

I'm going to use that in a little bit for the purpose of actually being able to cancel this operation if the cell becomes no longer on screen or I reload data, something like that. So all I need to do is go to a background thread, so I dispatch async to the global queue, and I use an NS file coordinator to coordinate reading to the preview URL.

And that operation, coordinate reading to the preview URL, will result in downloading the preview file. So I download what hopefully is a very small file while not downloading my document. And when that operation is done, on the main queue, I can enqueue the preview loading operation that I created up here, and that will go and apply the image to the cell.

The last little bit of making this code great is implementing a delegate method on the table view. This is actually a new method in iOS 6 for UI table view. That's didEndDisplayingCell. And that tells us when the user scrolls a cell off screen or when reload data is called, we no longer need a cell.

For any reason that a cell gets reused,

[Transcript missing]

Now that I should have preview, let's try this. So there's -- I now have previews popped up in this document. And, you know, I can edit this preview, too. You know, for my WWDC, you know, maybe I want to change this. I think about this as more of an Xcode kind of thing. Oh, I don't have that on here. Maybe I have it on here. Let's see. Yeah, I've got some images on here.

We'll also get to see this sync across and it'll be fun. So I'm going to change -- it changes the preview and momentarily we should actually see the preview come up over here. Ta-da. is a great guy. He's a great guy. He's a great guy. He's a great guy. He's a great guy.

All right. So that's preview. It's not that hard. Write out the data in the background thread using UIDoctrment override. And then we just download that individual preview file without downloading the entire document. Save network, save storage on the user's device. It's a great technique. All right. It's time to talk about The last things we want to think about to polish our app and get it ready to put on the store and maybe think about some of the ideas that are what we would consider being a good iCloud citizen.

And being a good iCloud citizen is really about handling conflicts in addition to using -- trying not to use a lot of network bandwidth, which we've talked about a little bit already. But conflicts, I'm sure you've heard about this before if you came to this talk last year or if you've been to other iCloud talks. It's always going to be reiterated because we simply can't avoid in a cloud-based world the idea of conflicts happening. We've got all these devices all living under the cloud all at the same time.

User can be making edits on any of them. But any of them may or may not be connected to the network at any given time. That's the network -- or that's the nature of the beast. So if we end up in a situation where we make different changes on different devices and they're not connected to the network at that time, but eventually they do get connected to the network, at the time they get connected to the network, we've got a conflict.

So how do we handle it? In an ideal world, we have to, well, in a definite world, we must always handle conflicts. And the reason for that is the way that iCloud manages conflicts is when it detects them, it creates a version that is, it decides is effectively the winner of that conflict. And that is the current version of your file on disk. That's what you will read if you go access your document even after the conflict has occurred. Whatever iCloud has decided is the winner. winner, which is basically the last one.

iCloud will also pick a conflict loser and create a new version of the file that represents the conflict loser. You access that through the NSFileVersion API. If you don't deal with conflicts in some way, these conflict versions stay on your user's device. And if you've got large documents, you can build up conflict versions that stick around forever and needlessly waste space. So always handle conflicts.

Now, getting back to my perfect world, a great strategy for handling conflicts is some kind of automatic merging. If your application has the smarts to be able to take two conflicted versions of a document, put them together, figure out what the right thing is, and save that to disk, awesome. I will personally give you a high five, a handshake, and a pat on the back.

Nobody has done it. Oh, man. Okay. Well, you'll have it done by next year.

[Transcript missing]

This isn't always possible. In those cases, when it's not possible, the direction is be lazy about conflict handling. And what we mean by that is don't ping the user as soon as you detect a conflict and force them to stop whatever they're doing and deal with the conflict.

I mean, it's great to tell the user that there was a conflict, but don't make it so they can't do anything else before they deal with the conflict. So what we want is maybe something like this, where we show a little bit of UI that lets the user know that they're in a state and gives them a way to access conflict resolution on their own time.

So when they decide, "Hey, I wanna deal with whatever conflict happened "and run through whatever conflict handling mechanism "you have created for them," They can do that on their own time. So that's dealing with conflicts. Let's talk about some advanced error handling stuff you can do in UIDocument.

I'm putting this part in there because I think it's a great polished thing, and I think that nobody really knows about what I'm about to tell you. And that's that reading and writing methods in UIDocument, they all return this NSError instance by indirection, but what happens with that? Well, the UIDocument machinery takes that NSError and it sends it to this method, which you can override on UIDocument, which is handleError user interaction permitted. Great.

What does that method do? Well, what that method does is take the NSError and look at it and see, you know, first of all, the error message, but more importantly, does this error have a recovery attempter associated with it? And if that's true, if the error has a recovery attempter, and the user interaction parameter is yes.

Then the default implementation of UIDoctument will actually put up an alert that shows your error message to the user and gives them the option to invoke your recovery attempter. So you can get in the way by overriding this method in your subclass. And when we send an error, you intercept it. You can then customize that error, possibly by changing the message and by including a recovery attempter, sending your customized instance to UIDoctument's default handler.

Another way that you can get in the way of this is simply in the reading and writing methods. If you've overridden these, you can create your custom NSError instance there and our machinery will send it over to HandleError. Additionally, you can also just yourself call HandleError user interaction permitted at any time. Or if you don't like our UI, you have your own UI, you can implement HandleError user interaction permitted yourself and do essentially whatever you want. You just need to respect the user interaction permitted flag.

So if that's no, you don't put up UI. And that flag would be no in situations like when we're closing the application. If the application is closing, UI doesn't help us because the application is going away. So let's talk about signing out of iCloud. This is kind of the last architectural thing that we're going to deal with.

[Transcript missing]

The application really just wants to get out of the way. That's all it wants to do.

It wants to determine that this has happened by registering for the NS Ubiquity token, identity token to change notification, the new notification iOS 6. This will tell you when that token changed. And when that happens, it just closes any open documents because they don't make sense anymore. If the user had a document in iCloud and the user signed out, Get out of the way. Close those documents. They're no longer accessible. If the user wants to get those documents back, they will sign back into that iCloud account. Simple as that.

And you want to provide user feedback so that the user understands that their data isn't gone. It just requires their, you know, nice little credentials, their account name and password and settings to sign back into iCloud and get their data back. So that's pretty easy. We're going to talk about code for that in a second.

But, you know, you can write a little alert that looks like this that shows them, you know, hey, you signed out iCloud. Sign back in to get your data back. Last thing is, should be the last thing for any application that you ship, but really especially in the iCloud world, that's testing.

You can do testing effectively in iCloud by using Airplane Mode to create conflicts. That's something people ask about a lot, like, you know, how do I test a conflict pass? Well, Airplane Mode is a great way to do that. Take two devices, put one of them on Airplane Mode, make changes on both devices, bring the one in Airplane Mode back online. When they get to talk to each other again, conflicts come in. This is a great analog for user scenarios because Airplane Mode simulates simply being out of contact with the network for a while, and that's a great simulation.

basic changes, things you can do to your document while that document is open on one application. So that's as simple as have the document open on one app, make a change from another device, and make sure that syncs over properly and everything happens correctly on the remote device.

Also, while a document is open on one device, change the name of that document from another device. You can also delete the document while it is open on one device. You can also do all of these things rather than while the document is open and running, while the document is sort of conceptually open but your application is waiting for state restoration. And if you're expecting sort of that document to come back up when you do state restoration, you can exercise all of these things, changing concepts. I'm moving and deleting.

And then see that your state restoration machinery handles it correctly. The last thing there is use Network Activity Monitor to -- if you are interested in looking at the performance of your application while it's doing all these things. It's great if you get all the right things happening at the user level, but you might also want to ensure that you're not creating unnecessary amounts of network activity and using bandwidth in the process, maybe doing -- syncing more data back and forth than is actually required. So I'm going to show you one last demo. And that's the code for responding to sign out in Cloud Notes. So we're going to go back to our application delegate.

And so we registered for the NS Ubiquity identity did change notification up here and application did finish launching. And the selector that we used for that is check user iCloud preference and setup if necessary. That's the same thing that we called up here. So the idea is this is a method that we run when we launch our application. but we also run it any time the iCloud state changes.

And to make sign-out work great, if the user is set up having chosen to use iCloud, we're going to make sure we check the ubiquitous token, which is something that we're going to store also in NSUserDefaults alongside the user's iCloud choice. So that method is this thing that looks in NSUserDefaults, takes token data, unarchives it to create a version of the ubiquitous token, and then our logic is -- oops.

If that token has changed and we used to have a token, meaning in our previous time we went through this, we were signed into iCloud, then we have signed out and we should let the user know that this happened. We use a UI alert view for this again.

Then in our backup and check user iCloud preference and setup if necessary, a little bit of work we do here is check whether -- if the user is not signed into iCloud, we make sure we set our Cloud Manager to not using iCloud. That way all the other bits of our application get to know about that state. And then -- hopefully that works. Yay.

We actually remove from the NSUser defaults our user choice. And the idea for this is if you sign out from iCloud and then you sign back in later, we'll actually want to ask you again if you want to use iCloud now. So we have to switch to local storage when they sign out, but when they sign back in, we may want to switch back to iCloud Storage. So the last bit to make this all work is to store the Ubiquiti token.

And that's just in NSUser defaults right down here. We take a look, and if we have a token, we'll go ahead and archive it up, set it in NSUser defaults. My key is Ubiquiti identity token. If we don't have a token, we'll just go ahead and remove that key from NSUser defaults. And let's go ahead and run our application. Okay. So here's our app. Let's try signing out.

I can short-circuit this operation pretty easily by going into Documents and Data, turning that off. Documents are off. Go back to Cloud Notes. Pops me up an alert that says, "Hey, I've signed out. I can sign back in if I want to retrieve my documents." I can see that, hey, I'm just down to local stuff now, and I don't get to use the cloud.

I can sign back in, though, which is really just turning documents and data back on. Go back to Cloud Notes. I'm re-presented with the alert that asks me if I want to use iCloud. I do. My data comes back. I download the preview files and put them back up. I can even access a file. It'll download that from the cloud and my content comes back. Everything's great just by signing back into the cloud.

That's it. If you have any other questions about using iCloud, we've talked about high-level concepts, key things that will make building an iCloud application easier and make it work for you. We're working on getting you the code that I just wrote today so you can see other pieces of it, like how the little cloud manager singleton works and such like that.

If you have additional questions, you can email jury.apple.com. Our evangelist, Mike Jurowicz, always happy to help you. We have some additional sessions. Thankfully, these are all still yet to happen. A couple this afternoon on using iCloud with NSDocument if you're interested in iCloud on the desktop. Using iCloud with Core Data. I know a lot of people are interested in that. And Advanced iCloud Storage tomorrow. Good luck to you in writing fantastic iCloud applications. I can't wait to see them.