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-237
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 237
$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 237] Advanced iC...

WWDC12 • Session 237

Advanced iCloud Document Storage

Essentials • iOS, OS X • 43:34

This is a practical session about writing applications that store files in iCloud but don't use AppKit and UIKit's convenient built-in document classes. Learn how apps that need to manage their own files and directories independently can take advantage of NSFileCoordinator and NSFilePresenter. See how your app can use the tips and techniques in this session to better integrate with iCloud.

Speaker: Mark Piccirelli

Unlisted on Apple Developer site

Downloads from Apple

HD Video (123.4 MB)

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Good afternoon welcome to session 237 my name is Mark Petrelli I'm an engineer in the Cocoa frameworks group. Today I'm going to talk about advanced iCloud document storage. And what I mean by that, if you were looking at our documentation, you know there's iCloud storage, which includes iCloud document storage and also key value storage. I'm not going to be doing any talking about key value storage today. Interestingly enough, from the title of the session, I'm actually not going to talk about documents at all either today. iCloud document storage is what we call the ability to store files in iCloud. And I'm not going to talk about storing what the user thinks of as files in iCloud. I'm going to talk about shoebox applications.

So what do we mean by a shoebox application? Well, many applications, of course, do not deal in documents. They show the user their data, but they don't reveal that the data is stored in files on disk. So popular examples are things like iPhoto and iTunes. So, and we call them this informally, it's an informal term, and we tried to make a formal term, but we couldn't, so we just stick with this. It's like a shoebox of pictures or tapes.

And what's interesting from the point of view of this talk is that I'm going to be talking about the kind of applications where you don't get to use app kits and s document class or UI kits UI document class because you know those provide a model it's very useful but it's just not appropriate for most kinds of what we call shoebox applications.

And so the kind of thing I'm talking about is something like this, a picture viewing application where the kind of entities that need to be stored in files are things like pictures and film rolls and albums. A pretty likely model for this kind of stuff on disk is to store each one of those kinds of things in individual files or file packages in one, you know, great big directory that your app is presenting the contents of. So if I'm not going to talk about an NS document, a UI document, well then what stuff am I going to talk about? I'm going to talk about one layer lower, the stuff that those are built on, the stuff that those are built on top of, things like foundations NS file coordinator class, it's file presenter protocol, NS file version, and NS file manager. And I'm not just gonna do a header crawl of those, I'm gonna talk about things that happen in iCloud applications and how you use those APIs to deal with that. Things like changes to your applications files, especially conflicts. And then I'll finish up with some tips and advice.

So file coordination in the context of iCloud document storage is pretty important because iCloud document storage, maybe more than anywhere else, introduces multiple processes accessing the same file. Of course, there have always been situations where multiple processes are accessing the same file, but now with iCloud, they're really going at it. iCloud is downloading things to disk while your application is showing those things on disk to user. And of course when I say disk, I also mean SSD about half the time.

But since these introduce a situation, we have to deal with problems like the fact that one process writing a file while another is reading is bad because the process that's reading might read inconsistent or incomplete contents because the first process is still writing stuff. So it begs the question, how does a process know when it is safe to read or write?

Another thing that comes up is the fact that iCloud changes files and then your application must read them. It automatically downloads data from the network and leaves the files on disk and then your application has to bounce that up and show it to the user. So we need an answer to the question, how does the process know when it must read?

And one last thing is that iCloud needs your files up to date on disk to do conflict detection. So in the example I showed you of a picture editor, when the user's editing things like the rating or adding keywords on there or something like that, there are times where iCloud needs you to stamp that stuff on disk because it needs to read it immediately. So we need an answer to the question, how does a process know when it must write?

And the answer to all these questions is file coordination, which number one is a locking mechanism in that it prevents your application from reading while iCloud writes and vice versa and actually also vice versa. So this is not totally novel. There have been plenty of locking mechanisms in the history of computers. But file coordination is also a notification mechanism in that it tells your application when iCloud changes have happened. And once again, this is not the first time this has happened. There's FS events, there's KQs, and there's Vino dispatch sources. But one good thing is that this is all one mechanism and it helps us avoid a lot of race conditions and things like that. But the most novel aspect of it is that it's also a triggering mechanism.

in that when something like iCloud reads or writes files, your application gets a chance to do things first. So that's kind of new. The file coordination API takes the form of, number one, a class called NSFileCoordinator, which you instantiate on the basis of, you know, one per fairly large-scale file operation, like saving a big batch of changes or moving the file or something like that. You instantiate it, and then you use it to do coordinated file access.

The other big piece of the API is a protocol called NSFilePresenter, and it's a protocol that you implement in your application to hear about coordinated file access done by other processes that are using NSFileCoordinator. So, and I mentioned NS document, UI document, do a bunch of stuff for you, you know, in applications where those applicable. This is one of the big things they do for you. They implement the NS file presenter protocol for you.

And it's not new this year, it was introduced last year in OS X 10.7 and iOS 5. And it's used by more than just iCloud, it's also used by Lion's auto-saving feature. It's what triggers the saving of documents sometimes when the user does things with them and their contents have to be up to date.

So I'm not gonna go through all the NSFile coordinator API at all, just for people who are brand new to it, I'm just gonna give a little introduction here. These are the two most unsurprising methods in the class. Coordinate reading item at URL and coordinate writing item at URL.

And the way they're shaped is that you instantiate on its file coordinator, and then you invoke a method like this, and you pass it a block, and the block of code is invoked when it's safe for your application to do its reading or writing. And what I mean by when it's safe, I mean there are no other writers right then, and if you're writing, there are no other readers right then. We do allow simultaneous reading. And before your block is invoked, a bunch of other things might have gone on first, and I'll talk about some of those in a little bit. So one of the things that can happen while your coordinated file access is waiting its turn is that something else might move or rename the file, and that's why we pass in a new URL. 99% of the time, the URL that your block has passed is the exact same one that you passed to it, but if the file is moved, you're told where it went. So it prevents a lot of kinds of race conditions.

So the NS file presenter half of the API, there's two main ways to use it. The first one is that you register a file presenter of an individual file. So you instantiate some class of your own, it could be the application delegate for example, and you invoke a method add file presenter. And when you do that, from then on while you're registered, you'll hear about the file being changed by other processes and also being moved by other processes.

So you'll also get asked to save changes. I'll tell you in a little bit why iCloud might ask you to save changes, but the idea is that this is very generic, so you're not even really supposed to care why you're being asked to save changes, just that the contract is that you have a file presenter, and if you're letting the user edit what's being presented, when you're asked to, you write that to disk. You might also be asked to do what we call accommodating deletion, and accommodate is an intentionally generic term. What do you mean accommodate?

Well, it depends on your application. You know, very often it means stop presenting that file to the user, 'cause it's going away. And if that's something that's selected in the UI, you know, select something else, the next thing in the list or something like that. And a somewhat more abstract concept is that you might be asked to relinquish to readers and writers. And I'll talk about that in a little bit.

So the other thing you can do with NSFilePresenter, even though its name is NSFilePresenter, it's actually kind of abbreviation for NSFile or file package or other kind of directory presenter. You can register a file presenter of an entire directory tree to find out as files are changed and moved anywhere inside that directory tree. So in a substantial shoebox application, you'll probably end up using it both ways. Your shoebox has a directory full of files and you wanna know as they come and go. But when your UI is letting the user focus on one particular item or something, you'll register as a file presenter of the file that backs that item.

The kind of messages that you have to respond to when you're a file presenter include the ones where you get notified about an individual file. For example, presented item did change is sent to your application, your file presenter, whenever the file is changed, whether that's the contents or the metadata or what. The idea is that you're supposed to look at what's on disk and find out what changed. And you also get messages about presented item did move to URL when it's renamed or moved. Your file presenter isn't merely notified about things, it also gets told to do important things.

when you register, you register a file presenter, again, when you first present the corresponding item in the UI so that you get messages like this. And the idea is that you stay registered until you're done letting the user view or edit the item. So, you know, you don't want to let the file presenters pile up or something like that because you will get messages for each one of them when appropriate. In the context of iCloud, When iCloud needs to write, you get a chance to write first. So stop for a second. What I just said, when iCloud needs to write, not read, iCloud doesn't need any help from you when it needs to read files because it's watching them.

It's using FS events. And if you keep changing the file, it senses that each time. So if you're letting the user edit something, it's in no hurry. You can keep on letting them edit. When you finally do save it, it'll propagate that to other machines. But when iCloud needs to write, as far as its conflict sensing mechanism, it needs you to write first if you're letting the user edit the file. And then when iCloud needs to delete a file because it's propagating the deletion that a user made on another machine, you get a chance to stop presenting that file first before it happens instead of being notified after the fact.

And in the typical usage, when a file's been deleted, the usual action, I say typically, and I say words like should instead of must, because there's gonna be a great variety of applications that people write with us, so it's a little early to lay down a blanket rule. But you should deregister your file presenter when the file's going away.

So the more abstract concept that I mentioned before, this concept of relinquishment, when something does a coordinated reading or writing of your file, your file presenter is asked to relinquish to it first. And this is another one of those intentionally generic names. Relinquish what? Well, it's relinquish doing whatever you can't do when something else is reading or writing the file. So it might be something that's reflected in the UI, or it may make a timer stop or something like that. But aside from that, what's interesting about these methods is that they're both your first and last notification that something has happened. So notice by the way, those methods take a block that returns, they take a block that returns a block. So what you do is when you're asked to relinquish a presented item to a writer for example, when you are able to, you invoke the writer block that you passed and you pass it, what's called a reacquirer block, when that writer is done, your reacquire block will be invoked. So you find out both the beginning and the end of operations by other processes. And what's interesting about that is that it ends up being a very handy way to delineate batches of the other messages that an NSFile presenter gets. A pretty common situation in iCloud is that iCloud needs to both change a file because that's what the user did on another device and also rename it all in the same operation because that's what's happened in another device. So in response to messages like presented item did change or presented item did move to URL, very often you don't actually do anything right then other than note the fact that it happened.

And when your file presenter reacquires the file, you handle both things at the same time if that's what it takes to be correct in your application. Thank you. So those are messages about individual files. You can, as I mentioned earlier, you can also use NSFilePresenter to get notified about things in your directory.

For example, presented sub-item did change at URL or presented sub-item at URL did move to URL. And these are the two most interesting ones that are other ones in the headers, like presented sub-item did appear at URL. You should probably ignore that. If you've been experimenting with this, you find out it doesn't actually get sent in the context of iCloud. So when a file appears, you'll just get presented sub item did change at URL. So let's go through a sequence of how this stuff ends up appearing to be used in a typical shoebox application.

Here's our picture viewing application. It just has two pictures appearing. They're pictures of the numbers one and two. So, and the user adds another one, they add a third item. So what the application has to do is it has to do a coordinated write of the file to disk to the applications iCloud storage space.

And a quick code sample, the way you do it is you instantiate a file coordinator and you invoke this method coordinate writing item at URL. And these kind of methods, by the way, you wanna try avoid invoking them on the main thread because other processes might do other things before it's your turn to do things.

So, but when it is your turn to write, your block will be invoked And then you'll use the APIs we've always had, things like NSData and NSFileWrapper, the POSIX APIs for writing to disk. The file coordination mechanism is something that you wrap around file access. So it doesn't actually introduce any new file access APIs of its own. So here's a pretty typical use of a file wrapper where this picture object that can save itself knows how to create a file wrapper and then just stamp it on disk where it goes. When the application has written the file to disk, iCloud without any other action on your part uploads the file to the cloud if the machine is connected to the network. If it's not, it waits until it is connected. So it's its job to deal with issues like that.

So, but iCloud is most useful when you have multiple devices connected to it. So in our example, we have the same application running on iPad. And iCloud will download, and let's slow down for a little second here, iCloud will download to the iPad the file metadata of what just happened.

So a big difference between what happens in iOS 10 and iOS is that Macintoshes, because they have great big hard drives or whatever, are always downloading eagerly from iCloud. Because you're not going to fill up your hard drive, you know, accidentally that way probably. But on iPad, where storage and, you know, iPhones are storage is more limited, iCloud waits for you to do a coordinated read before it triggers a download. So what's on disk right at this moment is a file that has empty contents, but otherwise, you know, its modification date and its name and all that stuff is correct and up to date.

So to get the actual contents, well, first in response to that appearing, the application's file presenter receives a message, and it'll look something like this. Presented sub-item did change at URL, this being the file presenter for the directory of all the pictures as a whole in this application.

And what it does in response is it does a coordinated read. And the reason it does that, besides merely being coordinated, so, you know, it's reading when definitely nothing else is writing, is that that coordinated read is what triggers the downloading from the cloud onto the iPad. And then at that point, the file presenter's coordinated reading code is allowed to go. And here in this example, you know, it's checking to see if the file is something that is new or is something that is already known to the application. It does picture it URL, which is, you know, some sort of method that looks up what is the picture object that would be stored at this location if there were one. And if there is, it just tells it to reload. And if not, the application knows that something new has appeared and it adds it. So, and at that point the application displays the new item and the iPad and the Mac are showing the same items.

So that was the regular case of the application on one device writes to disk, and that gets uploaded to iCloud, and it gets downloaded onto another device, and it's read there. But there are more complicated situations, and they introduce the need for file versions. So users, of course, can edit on multiple devices at once, and at once can mean literally at once, though people probably don't do that that often. But it also means virtually at once, where because one of the machines is disconnected network, one or both, and then eventually when it gets reconnected, a whole bunch of uploading to and downloading from the cloud happens. And then all the other machines hear about it.

Subtitles by the Amara.org community So, and this can result in conflicts because the user can make a change on their iPad while it's in airport mode, for example, or airplane mode. And, you know, and then they get home and they start working on their Macintosh and they forgot they already changed the stuff on iPad, so they edit on Macintosh. And then they remember to take their iPad out of airplane mode and then everything, you know, crosses paths as things gets uploaded and downloaded. And now we have two versions of a bunch of files. So, and those are conflicts.

So the iCloud machinery is pretty cool in that it senses conflicts and it picks a winner. So when you have a file and there's two possible contents because they're saved on two different machines, iCloud picks one and I think the rule right now is it picks the most recent change, though it doesn't have to be that way, that's what it does, and it puts the winning contents actually in the file, in the file system where the application expects to find it. So, and it does this even when your application is not running and the idea is that every file always has some decent contents in it. Might not be exactly what the user wants because the user hasn't been told what they did yet but there's always something in there and it's good. This matters a lot more in document based applications where on OS X it's possible with the all my files window to actually drag things out of iCloud onto the desktop or email them to somebody as an attachment. So we want to make sure that every file has something good in it. But even in the context of a shoebox app, it's good that when your application first launches, there is not something crazy in the files.

So that's iCloud sensing, that's not really iCloud, that's not really iCloud resolving conflicts though. So who does resolve conflicts? Well your application must resolve conflicts, either 'cause it gets a message while it's running or the next time it's launched. So when I said iCloud picks winners, well, your application might have to look at the losers. And that begs the question, where did iCloud leave them?

And the answer is that it leaves them in NS file versions. So NS file version is a really generic API. It wasn't, I don't even know if it came up first for iCloud. It's also used by NS documents auto saving mechanism. If you go to the versions browser and something like text edit on OS X, you'll see multiple versions of your document. are all NS file versions. So the API is pretty generic, you can get the file version that corresponds to the current file.

And also NS file versions that correspond to other versions of a file. And where are they stored? Well, they're stored somewhere else on the volume. If you want to find out just look at the URL for them. But you know, if that's subject to change, you're not supposed to care too much. And if you're wondering, hey, doesn't this waste space? No, it diffs stuff. It just saves changes on disk. So but for iCloud, what really matters are the versions that represent unresolved conflicts of any particular file. And you get a list of them using this method.

And once you get an NSFile version, what you do, and it really depends on your application, how you do this, the UI that you use to do this, you present the different versions to the user. That can be as simple as, you know, a sheet that tells the user, here are your different versions, and they can be identified by localized name. You know, and when we say localized in our API, we usually just mean presentable to the user. So this is the display name of the file. And by the way, the reason this is a property of NS file version is because files can get renamed, but still be considered the same file. So that is something you can show to the user when the user creates conflicts that way. Also, the display name of the saving computer, that's a really important hint that you can show to the user about what this conflict is about. And also the modification date, when they changed it. And first of all, of course, is the URL. Where are the contents of the version? So, you know, you might want to show that to the user or you just use it when you resolve the conflict. In general, the way that you use NSFile version to resolve conflicts, well, there's a couple things.

One of the methods NSFile version has is called replace item at URL. And what that is is it takes the contents of a file version and puts it in, you know, a normal place in the file system. It makes a real file out of a version instead of something that's merely attached to another file as a version.

And there's a couple different ways to use it. For example, if your application shows the conflict to the user and the user disagrees with what iCloud chose when it sensed the conflict, use this to override it. You can pass as the URL the URL of the original file when you invoke replace item at URL. You can replace the actual file that this version came from.

Or you can maybe make a new file off to the side. I don't know if you've seen iWork on iPad. It's not a shoebox application, but some of the same concepts can be applied to shoebox applications. One of the options it gives you when you create a conflict as a user is you can take one of the versions of your document and just call it a whole new document and look at it later. So you use replace item at URL. And by the way, it's okay if you're not actually, if there isn't an item there already to replace, that's just a no error situation. It's called this to make clear that if there is a file in the way, it's going to get stomped on. So instead of returning an error about the file being in the way. So when you pick the winner and you call replace item at URL, there might still be other loser file versions that the user didn't choose. And what you do with those is you set the value of this property resolved to yes.

So this is one of the few properties that's read-only because it's made for you to set the value. And when you sell it to Yes, what's that telling iCloud is that it can discard this version lazily. It doesn't have to do it right then. But it'll delete the contents eventually, and the deletion will propagate to the other devices that this iCloud account is hooked up to. But how do you find out when you even have to do any of this that you have to present a UI that lets the user resolve conflicts? Well, it's just more NS file presenter messages. So if you're a file presenter of an individual file, you'll get presented item did gain version. And if you're a file presenter of a whole directory tree's worth of files, you'll get presented sub-item at URL did gain version.

So as I mentioned before, this stuff is kind of generic. Not all file versions represent conflicts. So when you do get one of these messages, you should check to see if it is a conflict. This is a Boolean property, just conflict. So conflicts can also be resolved from the point of view of your application running on a device at any time because the user might resolve the conflicts on another device. So it's pretty common for, you know, while you're testing to see conflicts, your conflict UI appear on two devices at the same time right next to each other because you're doing crazy things that a user probably wouldn't do, but you still have to do them during testing. But the result is that when you dismiss a sheet or whatever that you're using on one device, that should be dismissed automatically on the other device too. The user shouldn't have to repeat themselves during conflict resolution. So what happens on the other device, the one that the user didn't resolve the conflict on, it'll get messages like this, presented item did resolve conflict version or presented sub-item at URL did resolve conflict version. So it's important to keep in mind, you know, while you're presenting UI for conflict resolution, you know, more of these messages will be coming and going. You know, more conflicts might be discovered if the user, you know, plugs in another device to the network or something, or they might go away completely dynamically. So you have to keep listening for these messages even when you're handling ones that you've already gotten. So what's it look like in practice? You know, how do these APIs show? when something is actually happening. Well, let's show up, let's start with, you know, our iPad and our iMac again, but this time the iPad is in airplane mode.

And the user changes an item on the Macintosh. They, you know, change the cropping on picture three or something like that. And the application does a coordinated write. Of course, all writes are coordinated in this situation. Does a coordinated write of the file on disk. And then our iCloud machinery automatically uploads the change. But because the iPad is not connected to the network, the change doesn't automatically get downloaded to the iPad. It just can't happen yet. and if the user for some reason changes the same item on their iPad, they forgot they changed it on their Macintosh, they forgot that they're in airplane mode, and they make a different change, that won't get uploaded because the iPad isn't connected to the network.

So it'll leave it on disk, and that's where it'll stop for now. But when the user eventually does take the iPad out of airplane mode and lets it connect to the network, What happens is that the version that was in the cloud gets downloaded, the version that was on disk gets uploaded, and now both the cloud and the iPad have two versions on them. Actually, the Macintosh does too, but it's out of the picture right now. And so the applications file presenter on the iPad will receive a message that looks like this. Presented sub-item at URL did gain version. And the UI that you're going to do for this is very different for each kind of application. So, but what it should do is it should check to see if the version is a conflict and really in the context of an iCloud application handling the files that it's put, you know, in its iCloud store on disk.

Of course, it's going to be a conflict, but before you start doing conflict type things with it, you should at least check just to not crash basically. When it does this, it finds out that there's a conflict for a file or a conflict version for a file. It should say, you know, is this a file I even recognize? In this case, this program has a method, picture a URL. Do we even recognize this URL? And if so, and it discovers that there's a conflict of some sort for this picture, it should present that to the user. And while it's presenting that to the user, it still has to handle other messages of this sort coming in. But when your UI is all done and the user's made their choice, the application writes the contents onto disk. It actually probably isn't taking it from memory and writing it on disk. It probably just used NSFile version, replace item at URL in this case.

And once that's done, that is automatically propagated to the cloud by, you know, the iCloud machinery, the agents and daemons running on OS X or iOS. And then that conflict resolution will automatically propagate to other devices like this Macintosh that's connected. So because the user chose the version that the user had edited on the Macintosh, there's actually no news to report to the file presenter on the Macintosh. So that one doesn't get another message. It's just that now the iPad and the Mac are showing the same items. Thank you.

So some tips and advice. First of all, at application startup, there's a new method for you to use to even find out if iCloud is turned on. There's a couple different ways to turn off iCloud. There is not being logged into iCloud at all. And then also in the iCloud preferences pane, there is a checkbox where you can turn off iCloud documents and data. And that's, there's a bunch of checkboxes there, but this is the one we're talking about, the documents and data one. So your application is probably going to do different things depending on whether or not iCloud is turned on. It's still pretty early days in developing UI for this sort of thing. Some people just want to say, you know, my application only works if you're logged into iCloud. Other people are storing two copies of things so that in case the user logs out. So we'll see which kind of applications people like best. when your application has to make decisions at launch time, this is the one that it should do. So what's interesting about ubiquity identity token is, number one, this method is fast enough to use on the main thread, unlike this existing NSFileManager method, URL for ubiquity container. So this has been kind of a big issue. You know, what do I do during startup? I don't want my application to just block.

Well, the answer is that you invoke ubiquity identity token. By the way, what's... Oh, you're welcome. So that one is fast enough. And by the way, what's a token about it is it's a different token depending on what account the user logged into. So the user, of course, can log out and then log back into a completely different account. These tokens are both encodable if you need to record something to match, you know, what the user's logged into. And they're also comparable, too. You still have to invoke URL for Ubiquity container at some point. The answer is it didn't get that much faster. It got a little faster. It didn't get that much faster. You should try not to invoke it on the main thread. So while you're doing stuff on your initial reading thread, you should be showing good UI, letting the user know what they're waiting for. And so because the user can log out or log back in or just turn off iCloud documents and data completely, you should be listening for this NS Notification Center notifications, not NS Distributed Notification Center notifications, NS Notification Center notification.

NS Ubiquity Identity did change. And the Ubiquity Identity token stuff is new in OS X, 10.8, and iOS 6. Thank you. So when you're reading files, as I showed you in the graphics before, coordinated reading can trigger downloading. And that, of course, can take a while. Everything that involves network access can take an unpredictable while.

So this is something you want to avoid doing on the main thread. So I think, in general, the advice, don't do file system access on the main thread, is just getting repeated over and over again on both iOS and OS X. So the fact that reading is coordinated doesn't really change that. You still have to do good error checking. For example, NSFileCoordinator coordinate reading, it returns an NSError. It might be an error about something like the file couldn't be downloaded. Even if that doesn't return an error and your block, your reading block is invoked, you still have to do good error checking. The file might not even exist. And that's correct. So the file might have been deleted while you're waiting to read it. NS file coordination isn't going to call that an error just because somebody else deleted the file while you were waiting to read it. So... One thing about that, by the way, is that when you are, you know, lazily loading some part of your application, like a picture to put up on the screen, you waited until the user actually scrolled to it to put it up on the screen. You know, you find out that, hey, the picture is gone. Well, all you have to do is just do something graceful right then, like don't draw it, you know, and of course not crash. But you don't have to go through the trouble of, you know, this isn't where you have to go through the trouble of going, oh, this picture is gone. I now have to update my model elsewhere. When this happens, when coordinated reader is the first thing that discovers a file has been deleted, that's okay. Your application is still about to get the regular notification about that deletion where the code that you already wrote to handle deletions lives. So you don't have to put that kind of code in two places. So when you're resolving conflicts and you're using the NSFile version API, keep in mind that accessing a file's version is like accessing its contents from the point of view of file coordination. So you should do coordinated reads even when you're merely enumerating or reading the versions of an NSFile version. And you should do coordinated writing when adding, removing, or resolving versions. That way, two different processes don't do conflicting things. When iCloud deletes files, you will get, your file presenter will get a message like accommodate presented item deletion with completion handler.

So, and then you might notice, if you've already written code with this, had an NSFilePresenter, the file doesn't actually get deleted a lot of the time. A lot of the time it just gets moved. So that is iCloud's caching machinery. When a file like that gets deleted, it might come back pretty quick in a bunch of different situations. So iCloud isn't in a big hurry to delete it off disk.

It will eventually. But in the meantime, it just moves it into a directory that was supposed to be kind of a secret, but ended up not being so secret, whose name starts with.ubd. You only notice this if you don't deregister your file presenter when you're told that a file is being deleted. But what we want you to do is try not to take advantage of the implementation details like that. They will see if you don't deregister your NSFilePresenter. So when we say deletion in the context of NSFilePresenter or something like that, it's a pretty high-level operation. It doesn't necessarily map exactly to the underlying POSIX calls. It's at a higher level, what the user might think of as deletion. And, of course, something like deletion to the user, the item is gone, doesn't necessarily mean that the file has to be gone. So this is kind of the thing throughout the file coordinator and file presenter APIs. They operate at a higher level. So you don't do, for example, a coordinated read for each individual POSIX call. You do it when you are loading a document's contents, things like that. So they're meant to wrap around sequences of lower-level operations. Some advice that comes from, you know, watching how people have been using this already. Save settings in the right place.

So preferences like scroll bar and window positions, it's kind of questionable a lot of the time whether or not those go in your document at all because all of our devices support different screen sizes. because even before then there was always a possibility of giving something like a document to another person.

But even when we're not even worrying about giving documents to other people, we're just talking about the same user using the stuff in their shoebox, they're still using multiple devices to do that, so it may as well be different users from the point of view of things like screen sizes. So be careful about what you write in files that get uploaded to iCloud.

A nice design pattern that people have noticed is that some of these settings, just call them discardable. So an example is Keynote's current slide. Whenever you save a Keynote document, I'm sorry, whenever a Keynote document is saved for you automatically, the selection of the current slide is saved in there. But if you're just, you know, if you're just viewing a Keynote document and scrolling through things like that, that doesn't trigger the saving. So that turns out to be a pretty good model. It's both handy and it's well-mannered.

So the rule is that it's good to save your files when you're saving for what the user would think of as real changes. They change their content. They didn't merely scroll or expand an outline view or something like that. So it's okay to let settings tag along. Some things are just never okay to save in iCloud files. The example that we've noticed is things like timestamps where a file gets open and the first thing that happens to the file is that it gets saved right away with some information about the fact that the file is opened. So that's really bad in the context of an iCloud application because number one, it can cause false conflicts. You know, it tricks your own UI into telling the user, you know, you did something to the file differently on these two devices.

It's just like I didn't do anything to the file except look at it. So you want to avoid writing needlessly into files. And in addition to confusing the user, it can be a performance disaster when sync loops occur, which is that on one device, your application saves the file, and then on another device, your application notices the change and opens the file and then saves it right back again and just bounces back and forth, bounces back and forth until our throttling kicks in.

So file format compatibility. File format compatibility has always been a pretty important issue when you're talking about document-based applications. But now it's also an important issue even for shoebox applications where the user doesn't get to manipulate the files. What is the problem? If they update their application, just update the files. No problem. The problem is that, number one, these things stay around forever. So that's a basic file format compatibility issue. but um But one thing to keep in mind is that you want to do things that work on both platforms. So if you're writing an iOS app now, don't take it for granted that you're not going to write an OS X app later and vice versa. By doing things like using encodings of classes that only exist on one platform or another. So different versions of your application running at the same time can happen because the user does not have to update their apps simultaneously on their iPad and their iPhone and their Macintosh. So this is something where the more work you put into it, the better. You can do really heroic things or you can do pretty quick things. But you do want to keep in mind that the user is probably going to try and do, you know, even editing with old versions of your application. So you want to make sure not to destroy data and that can get pretty sophisticated where you, you know, merely preserve new entities in applications that don't yet support those entities and just, you know, just leave them so the user can still work with a new version of your app.

Or it might just mean, you know, falling back to read-only access to the files. So just don't break anything until the user resolves the issue by updating their app. So to summarize, I just talked about the low-level APIs today. So use NSFileCoordinator when you read and write files. Use NSFilePresenter to hear about changes that happen and get notified of things that your file presenter should do. And use NSFileVersion to deal with conflicts.

So we're eager to help you use these. The person who coordinates our eagerness is Mike Jurowicz. He's the developer tools and frameworks evangelist. There is new documentation, the iCloud design guide is brand new. It's up at developer.apple.com, just search for it there. And as always, there are the Apple developer forums. Related sessions have all already happened, so there was stuff that builds on this, using iCloud with UI document or NS document or core data all yesterday. So that's it. Thank you very much. Thank you.