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-225
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 225
$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 225] Up and Runn...

WWDC12 • Session 225

Up and Running: Making a Great Impression with Every Launch

Essentials • iOS • 45:32

Your application's launch is the first chance for your app to impress users. Learn how to use this time effectively to create the best possible user experience, while launching and configuring your application efficiently and consistently.

Speakers: Brandon Newendorp, Jim Turner

Unlisted on Apple Developer site

Downloads from Apple

HD Video (314.6 MB)

Transcript

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

Welcome to the session Up and Running. My name is Jim Turner. I'm an iOS Frameworks Engineer. Application launch is a critical point in your app's lifecycle. Users have come to expect a really great experience from our apps, and they start forming opinions about your app from the moment they launch it. Now, when you fail to deliver on that experience, they tend to notice. They write bad reviews, they delete your app, and that's certainly not what we want our users to do.

Now, this talk could talk about a whole bunch of different things that we could do to help your apps launch performance. But today we're going to focus on a single method, and that is application did finish launching with options. And focusing also on the topic of blocking, blocking the process, blocking a thread or a queue, or even blocking the user from even using the application at all.

When it comes to getting your app up and running, blocking is really what you want to avoid at all costs. So to help with this today, Brandon's written -- my co-presenter Brandon has written a really great application that's going to augment our conversation. It's going to focus on a lot of the topics that we want to talk about, and I'd like to have him come up on stage right now and show it to you.

Hi, everyone. My name is Brandon Newendorp, and I'm an iOS software engineer. To help us talk about application did finish launching with options, I've put together a really great demo application. I've tried to think about the things that are common tasks a lot of you do in your applications and tried to build them into my app.

I called it Quick Pick, and it's an application for social photo sharing, and I'd like to show it to you now. I have it running on my phone here. You can see I have this lovely eggplant icon. Let's just go ahead and launch it. Interesting. All right, I'm going to find some crash logs. Give me a second.

So I found a crash log, a couple of them since I crashed twice, and I see a couple things that are interesting. I have this exception code of ate bad food, and the reason is that my application failed to launch in time. This is not good. You know what that is, Brandon? That's the watchdog.

The watchdog is a timer that's used by iOS to monitor your application's launch times, how quickly it responds to system events, and how quickly and how timely it can be terminated or put into the background. Now, the amount of time that we actually document -- we don't actually document the amount of time that we give you to launch your application, but we highly recommend that you get it launched in five seconds or less.

And that's really not a lot of time, if you think about it, especially if you're trying to do a lot of network communication or complex computation, especially at startup.

[Transcript missing]

We make some network calls. We are a social photo sharing application, so we're going to talk to at least one or two servers. There is a couple databases we maintain for our geotagging and our comments done on photos.

We have a couple thousand templates and default photos that we need to install in the application documents directory when the app starts up, and we want to make those available to the There's an external library that we employ that adds some imaging post-processing that needs an initialization routine called on it.

And finally, there's a couple other odds and ends that we do, like starting up a Core Location Manager. We also use an IAD banner, and we also look at what other options are passed to us.

[Transcript missing]

Like Brandon is. So we really only care about Objective C in this call, and we're spending 90% of our time in app launch. So we'll just drill down here.

Oh, okay. UI application under load main nib file name bundle. That's one of our methods. That's saying that we're trying to load your main interface file. Could you show it to me? Yeah, I just have one zip for my application. I just thought it was kind of straightforward. So here's my app's nib.

Okay, so clearly we need to talk about interface loading. Most developers create their interface graphically using the interface builder in Xcode. And if you're not, we highly recommend it because it really does ease the creation of creating a UI. You get to see what your users are going to see, and if you don't like it, it allows for a lot of exploration and experimentation with things that you can do in the UI that represents the data you're trying to get displayed.

It also reduces a lot of code. Those of you who do create your code programmatically, it's perfectly fine that you do that. But a nib can really get rid of all that code that can be tedious and sometimes problematic. But zips play by a couple of rules that everyone needs to be aware of.

And the first being, and the most important, is that no matter what you put in that zip, we have to instantiate the entire object graph, even if you only need one or two parts out of it. And because this is all dealing with user interface, this all happens on the main queue. And when you're doing this when you're trying to launch your app, the watchdog kills you.

And there's also some other subtle side effects that can come into using a nib or a zib that some people don't -- it's not obvious. Is that if you have an object inside that zib that has an expensive initialization routine, you're going to pay the penalty for that time as well when you instantiate that nib, because we have to initialize that nib or that object at the same time.

So how do we fix this problem? Well, there's two solutions that we can do. And the first is to throw more zips at the problem. When you create your user interface using multiple zips, you need to focus on lazy loading. Put into your nibs only that what you'll need when you load the nib.

So if you only need a table view controller or a tab view controller, or maybe you only need a table view cell, don't put a bunch of other stuff in there because, again, you're going to have to load all of that code. So in Brandon's case, we could maybe take that really complicated zip, break it into six or seven files, and we load a much smaller part of it at app launch.

The second way is to use storyboards. And storyboards are actually the preferred way of creating your user interface because it abstracts and handles a lot of the issues we just pointed out with ZIBS. They're perfectly fine and they're perfectly happy to allow you to create your entire user interface in a single file. And they get to do that because both the tools and the framework have really deep, intimate knowledge of how a storyboard is actually built and constructed and how it wants to be unpacked at runtime.

If you use Storyboards, it really will simplify your user interface creation, and it allows you to not worry about the implementation details of how you're going to get your UI on screen when your application runs. It sounds like Storyboards would be a fantastic tool for my application, because I have a pretty complicated user interface. There's a lot going on there. But I'd really like to get my demo application working for the talk today, so I think I'll migrate to Storyboards in the next week or two.

What I'd like to do right now is take a couple key parts of my application's interface and migrate them over to separate zips, just to get things up and running nice and quickly right now. That's a good idea, Brandon. So once again, this is my application's main zip right now.

There's a lot going on here, but there's really only two pieces that I have to have in order to get my application launched. And that's really the key part, is to think about what do I need just to launch my application. I need to have this table view, and I need to have the corresponding table view cell. So I'm going to migrate each of these into their own zips.

Going to make a couple of new zips for my project. Start off with empty ones. And first I'll create a QP photo table view cell. Create the one that I incidentally had around already. And we'll go ahead and make a second one here for the QP Photo Table View.

One of the convenient things that we have in Interface Builder is the ability to cut and paste objects from one zip into another, which is going to help me get this process done nice and quickly. All I need to do is I'm going to cut my table view out of one and move it into the QP photo table view.

I'll go back to my view controller and I'll grab that cell and simply paste that into the new ZIB. Now I've moved them into ZIBs, but I need to do a little bit more work before I can actually have my application use them. My app needs to know to actually use these new ZIBs. It's very straightforward for me to get my application to use a default ZIB when it launches.

If I pick my Xcode project, I can select my target and go to Summary, and change my application's main interface to the QP Photo Table View. This is how Xcode defines what ZIB to load when my application launches. And now it will just load that nice, lightweight table view. I also need to update my table view to use the cells. And there's a couple interesting things that we can do to make that happen. I'm going to take advantage of UINib to make this happen. So first, I need to make a UINib property.

Pretty straightforward. In my viewDidLoad, I need to do a little bit of setup for this. The first thing I need to do is assign that property to my nib. UINib has a method, nibWithNibName. I'm going to tell it to access my QP Photo Table View cell and to look for that in my application's main bundle.

The second thing here, the second line is one of the really interesting bits that we offer. In iOS 5, we added the ability for your table views to have a template cell that you can assign a cell reuse identifier. And this actually simplifies part of your table view code. In my case, I'm going to tell my table view to register this nib. The key thing with registering the nib is I can only have a single item in that nib, my table view cell.

Now, I mentioned that this simplifies things. If I take a look at my cell for RowIt index path, if you've ever used a table view, I'm sure you're familiar with this method. And this bit of code probably looks familiar. You dequeue a reusable cell with identifier. If it's nil, you create a new one.

The advantage of assigning that nib to my table view is I can delete all of that, and the dequeue reusable cell with identifier will automatically reuse a cell for me or instantiate a new one from my nib. It's incredibly straightforward, and this makes my application's interface loading occur much faster.

All right, so what are the changes that Brandon just made? What do they look like to the watchdog? We've reduced the amount of time that we're spending in interface loading by quite a bit. But we're still incredibly long on time. There's no way our app's going to launch still. So let's go right into handling network communication.

When your application is talking to a server or service over the network, it's really easily defined as you're writing in undefined behavior into your app. We can code as defensively as we possibly can around every conceivable idea or problem or situation that our network communication can get into, but there's still going to be three things we didn't think about. And when you're programming in the face of this, and when you're trying to get your app launched in front of the user, you really want to focus on what's going to block and what's going to remain usable while this is happening.

And this actually applies to any kind of synchronous method or behavior that you find in your application. So let's take a look at some basic networking code that one app could actually do when it launches. And here it's nothing exciting. We have an NSXML parser that's doing init with contents of URL. And init with contents of URL is a blocking call.

If this is happening on the main queue, we're going to block until it returns an answer. Any method that has the name with contents of URL in it is a really good indication that it's implemented synchronously under the hood and it needs to be used with caution. Same goes for NSData, data with contents of URL. And also the same for NSURL connection, send synchronous request, returning response error. It's right in the name. It's synchronous.

Another way of thinking about this is, imagine that you on app launch have some files that you'd like to download, and you start downloading your current info data, but all these other files are waiting for you to download. These are going to take a long time, especially in a bad network conditions, and your app's never going to get launched, especially if this is happening on the main queue. So the first approach to trying to solve this problem would be to, well, if I'm blocking the main queue, I'll just unblock it. Dispatch async puts it in a background queue, and the problem goes away, right? Well, not exactly.

While you have maybe solved the problem of the main queue being blocked, the issue still remains. We'll put this in the background queue, and now the user is able to use your app, but what actually happens in this case? Your current info gets downloaded, but this JSON file is coming from a server that is just getting buried. It's responding incredibly slow, but it's going to take it hours to get that data down.

Your other parts of your application are not going to be functioning correctly if they depend on the image data or maybe a Twitter feed being pulled, because it's never going to get that information. Okay, so if each individual part needs to be asynchronous, we'll just wrap each one of them in Dispatch Async, right? And you see where this is going. Not a good idea either. And there's a couple different reasons here. First, you lose a lot of ability to control the resources that your device is consuming when you do this. And second, there's no real clear way to build dependencies between one block of work and another.

And third, when you come back in six months and you need to add code to this, where do you put it? I mean, it gets kind of confusing. There is a much better tool. It's NSOperation. NSOperation is both asynchronous and can be made concurrent, which, when you're dealing with network traffic, is really what you're looking for. Things happening in a background queue and using the device's capabilities to its fullest.

You can also tune resource usage with NSOperation through NSOperation Queue. And NSOperation Queue is an object that just manages a light collection of operations. You can form dependencies between each NS operation. It's trivial to create one S operation that can't run until another operation finishes doing what it's doing.

And finally, they had the idea of cancellation. If you create an NS operation and add it into a queue and find that before it runs or before it completes, you don't need that information anymore, you can just go ahead and cancel it, and you don't pay the penalty for computing that work or pulling that information down from the network.

Now, Brandon, I have to ask this. How is our networking stack looking, QuickPix? Does it have any problems like this? We probably have quite a few problems. I had no idea that data with contents of URL would block my main queue, and I'm using it all over the place. Excellent. That's not good.

This is really important, especially in our application, because we're so network bound. We probably should take some time and fix it up now. That's probably a great idea. There's a number of things that QuickPix does when I launch it that are network dependent. My application launches, and it needs to download two JSON files for a couple of different purposes. The application also launches and downloads eight different photos from my servers. They change on a very regular basis, which is why I download them every time my application launches.

The problem right now is that my application is using data with contents of URL to access all of those files. And if any of my servers is slow, that JSON file, for example, will never download, and my app will fail the launch. I'm going to take advantage of NSOperation and NSOperationQueue to make this better, and I'd like to show you how that works. NSOperation and NSOperationQueue are fantastic tools for deferring some of the stuff and getting these tasks off of your application's main queue.

We provide NSOperation as something that you can subclass and create your own operations. I'm going to be implementing the start method in NSOperation, which is what you should implement if you want to make concurrent operations. I've put together a couple of NSOperation subclasses that we'll be using, one for JSON and one for photos. I'm going to bring my JSON operations into my project here.

And I'm going to go ahead and show you a couple of the key things in how this works for networking. This is my QP JSON operation, and the interesting stuff here is inside my start method. I'm taking advantage of NSURL request and NSURL connection to download these files asynchronously and to make sure that I don't block anything and to take full advantage of my device's networking resources. I create an NSURL request with the URL that's passed in, and then I create an NSURL connection.

The key part of this is that I don't want to start that connection immediately. Now, you might be thinking, "Brandon, why would you not start that right away? We want it to be fast." The next line is the real answer to that. We need to schedule our connection in my application's main run loop, where we call data back with it. That way, we're not blocking anything while my URL request is out there and sending data back. Once it's scheduled, I can start the connection.

So that's what my operations look like. How can I use them? This is my application's "application did finish launching with options" method. I have a couple of things at the top here just to get things up and running. We'll not worry about those. But here's my JSON operations, or rather my data with contents of URL right now. They're going to block if the network is slow or if a server is not responding very well.

So what do I need to do to take advantage of NSOperation? The first thing I need to have is an NSOperation queue. I'm going to create that as a property. We'll call it Operation Queue, because I like creative names like that. I'll create, or rather instantiate my operation queue, and since I'm using this for a lot of processing tasks, I'll call it my processing queue.

So I need to use my JSON operation. Of course, I need to import a header before I can do anything else. And then I'm going to add my JSON operations and just replace my data with contents of URL with my two QP JSON operations. Each of those get initialized with a URL, and then I simply add them to my operation queue. I'm going to do something incredibly similar for the QP photo operation. I'll add my operations to my project and go through those same steps.

Just import the header for it. And then down here is where I download those photos. I'm creating an array of the URLs that I'd like to download, and then I'm just enumerating through that array, and right now calling data with contents of URL for each one. For the photo operation, I'm just going to replace that for loop and create a set of QP photo operations and add each of those to my operation queue.

And with that, I've taken my networking and used NSOperation to make it both asynchronous and concurrent, so I can take full advantage of my device's networking resources to download multiple files at the same time and not blocking any of my application's queues while it's doing it. This should make my networking for my application much better. So to recap a little bit, what we had before was a very synchronous, very serial process, one file at a time, happening on the main queue and irritating our user.

And now what we have is a very asynchronous process happening concurrently, downloading multiple files and bringing your application and its functionality up for the user much quicker. What does this look like to the watchdog? We move all that network time to the background processing queue. But we still have a couple other things that we're doing. We might launch okay, but let's -- we should probably explore what's actually going on here.

These three blocks of work are database management, our payload installation, all those images that we need to install, and the external library that does some image processing. These things aren't really specific to -- or not really general to any application. It's specific to what we're doing. You may do some database management as well. But in our case, it's what we do to make our app what it does. And so it's just basic work that we need to get done.

But it doesn't necessarily need to be work that blocks the user from being able to use the app. If we don't have all of our templates installed before the app is completely launched, then, you know, maybe we don't have those available to the user. Same with, like, the external library. Since it's for image processing, maybe that functionality isn't online until it becomes available.

These tasks also represent things that are paralyzable. The output from the external library doesn't depend on the payload installation, which doesn't depend on if the databases get created correctly or in time. So these are things that can happen concurrently while your app is launching. And finally, they may actually be dependent on the network, and we mean that in two different ways.

And the first being, the external library, since it's a static library that we don't have the source for, we're not really quite sure what it does in its initialization. So if it calls home to, like, check for an update, we don't want to pay that -- we don't pay the price for that during our app launch.

And the second meaning is that our database may need to pull down a file to properly initialize from one of our servers. So it may need to wait until that data is available before it can actually start itself up. So if we take a look at how we might be able to process some of this general data, here we have two chunks of code.

And in the first, we have a DBManager object that looks like it's going to start up a database. And it just needs to update itself when it gets running. It has no dependencies on any other part of the application. And the application really doesn't depend on it being ready at any point in time. So it's in this situation that this might actually be a really good use of DispatchAsync. If there is no dependencies, you don't really need to put it in a queue. You just want it to get running at some point in time.

This second chunk of work, though, is going to be slightly more problematic, and for that reason right there. In it with data, and we got that data from our server. It's problematic because now all of our data is coming down asynchronously and concurrently in a background queue. So how do we make sure that the XML parser gets initialized correctly, and how do we get it started the minute that data is available to us? Let's take a look.

So let's give ourselves an operation queue, give it a name, and we're going to have some network operations on it already. So we need to create an operation that encompasses the work that parsing that XML file is going to do. And so we create that operation, but before we put it into the processing queue, we need to create the dependency between it and the network operation that's going to be or is downloading that data. And we do that through the add dependency method. And once we've added that dependency, we add our operation to our operation queue. RQ is both synchronous and concurrent, so all these operations could happen in any order at any time.

And so we can keep adding in objects and other operations, and they can get processed as the queue gets to them. But what happens when this one becomes slow? Doesn't matter. We can still add other operations. They get processed in time. And when that operation -- that operation that's slow finally completes, the operation queue will tell our parsing XML operation to go ahead and run itself, and then it finishes.

There's a lot of tasks that the demo application does that sound a lot like the general processing you're describing. There's a lot of things that we're doing that I think I need to make asynchronous to try to keep getting the application launched. When I launch the demo application, we need to create a couple of databases. One of those databases is for local photos, and it doesn't have any dependencies. It just needs to be created, and when we get around to finishing it, that's great.

The second database, however, does rely on networking, kind of like what Jim was talking about. If you remember when I was creating my networking operations, there's two JSON files I download. One of those JSON files is used to initialize that database, and I have to have it to get that database up and running. So I'll need to do some dependency work for that.

[Transcript missing]

The next is that shared photos database. And this little bit right here makes it a little bit more complicated because it relies on data coming down from the network. This is a great place to use NSOperation and take advantage of dependencies. Now, for something like my networking traffic that I was using earlier, it's great to create your own subclasses of NSOperation to do the tasks that you need.

But sometimes you need something that's straightforward and you don't want to have a list of, you know, 50 operations that you created. To help you with that, we provide NSBlockOperation. NSBlockOperation is very similar to DispatchAsync, but it gives you the capability to do the same things you do with any other NSOperation. And I'm going to use NSBlockOperation here.

So my Shared Photos database code needs to be loaded with the dependency. I create a block operation, and I call block operation with block. And just like with the dispatch async above, I put my Shared Photos database code in there. However, the second line of code is what makes this work. I can add the dependency on that earlier JSON operation. With the dependency in place, I just add it to my operation queue, and it's taken care of.

The next steps in my application launch process is to copy some assets from my applications bundle into the documents directory. The first of those is this initial plist. It's very small, it's very lightweight, which is fortunate because I have to have it in this launch process. It's always good to think about what you need there and what you don't need in your launch process.

If you have to have it there, by all means, have it there. And in this case, I'm going to leave my plist in place. The next step, however, is copying my default images and templates and other assets into my documents directory, and this takes a long time. This is another place to use NSBlockOperation.

Once again, I create an NSBlock operation and call BlockOperationWithBlock. I pass in, again, the same code I was using to copy those photos. In this case, however, I'm taking advantage of another feature that we have on NSOperation, which is the ability to set a completion block. The completion block is something that we guarantee you we will call after the operation is finished. And in this case, I want to update my application's main user interface once those photos have been copied into the documents directory. As you may or may not know, your application's user interface can only be updated on the main queue.

I need to guarantee that this completion block, or the task in this block, will occur on the main queue. However, if you take a look at the documentation for completion blocks, we say it's highly unlikely that that completion block will be called back on the main queue. So I need to guarantee that it will happen myself. And to do that, I'm using performSelectorOnMainThread. I'm asking my view controller to load the photos on the main thread in my completion block. With the completion block in place, I once again add the operation to my operation queue.

The last thing I need to do in speeding up or improving my application's launch performance is to initialize my external library, which I call ImageStamper. This one has no other dependencies, so this is yet another place to use an NSBlock operation. Like before, I create the block and simply add it to my operation queue.

And now I've taken a lot of those general processing tasks that my application had and I've made them asynchronous. I'm not blocking my application's main queue, and hopefully this will help me get QuickPix up and running much faster. What do those changes look like to the watchdog? Database work is now on the background queue.

Payload, image stamping, external library is now on the processing queue. We still have that little sliver of payload and some other things that we're going to do, but the watchdog isn't going to bother us anymore. But at this point in time, you're probably thinking to yourself, you guys have now put pretty much everything that builds up the infrastructure of my app in a background queue. What exactly do I show that user wellness is all happening? At least three of you thought that. This is a really good question. But it's something you can do if you put a little forethought and planning into creating your application or when you're refactoring your app.

And how we're going to approach this in Quick Pick is, That initial data file that Brandon loads, that P list, it actually has a fake database in it. It shows, you know, 10, 15, 20 entries of what the user would normally see when the app is actually doing what it's supposed to be doing. It has no images downloaded, so we have a very tiny default image with a question mark in it.

But when the user launches this app and they're in a cornfield somewhere and they have no networking, they get to still use the app like we would intend them to do. They can see the functionality and they get to actually experience what we want them to experience instead of showing them a spinner.

Then as assets get downloaded, here maybe one of our databases has finally come online and we've downloaded a couple images, we're going to just seamlessly load those into the UI as they become available. If we're lucky, the user doesn't even realize this is actually happening. And then finally, when the entire application is up and running, it's come online, it's fully realized, we have all of our assets in place. The application is running as we had seen it in our development environment and our users are ecstatic and they give us more money.

But what remains? What are the other things that we do inside Application Did Finish Launching with Options that need to stay there? Core Location. We use Core Location for geotagging our photos. But Core Location needs to be instantiated -- an instance, excuse me, of Core -- see a location manager -- needs to be instantiated before your application says that it's finished, before we return from application did finish launching with options.

And this is important because in some instances, we will launch your app in response to a location event. And when your app says that you're up and going, we will immediately call you back to give you updated location coordinates and information. And if you don't have a CO location manager configured properly, you're going to miss that information.

IAD, we have a single IAD banner view in our app, and we only create one because it doesn't make any sense to make more than one. You're not allowed to show more than one anyway. And if you create one and you create it in your application startup, you can share it amongst all the view controllers that you're going to display in your app.

And by doing it this way, the IAD framework can actually optimize and more efficiently deliver ads to your application. We also look at what other options are passed into us. I mean, the method name is application did finish launching with options, so there are other things that come along for the ride. If you have a local or remote notification, your app is launched in response to one of those, the options dictionary will tell you that.

If you, perchance, have a custom scheme, if you have a URL that your app will be launched in response to, another application on the device could launch that URL, and you're told not only the URL that the app requested, but the app that actually requested it. So in this way, maybe you update your UI to better match the app that called you. Maybe you are a newsstand application. We'll launch your application in response to the user downloading new content that you need to make available to them.

And then as the teaser showed you, there's something new in iOS 6 called State Restoration. And this is a really cool tool. It allows you to save and restore where you're at in your application so that when the user comes back into your app, they can be immediately returned to where they left it at, whether they're 15 view controllers down in a nav controller or they've scrolled 16 pages in a web view that you host. Really interesting tech. It's great stuff, and I think you guys are all going to love it. But to implement it correctly, we needed to create a new delegate method. And we're talking about it here because this entire conversation deals with application did finish launching with options.

But we need, State Restoration needed your application to be up and running before it can actually do what it does. So we needed to tell you that the application was going to be launched, so we've made it application will finish launching with options. Small, subtle change, but what we recommend that you're actually doing in did finish launching with options, you simply move it into will finish launching with options.

There was a talk on this yesterday, but Gordy's doing another talk on it tomorrow in Russian Hill at 3:15. Highly recommend you check it out. It's really great stuff. And if you can get it in your application, your users will love it. Woohoo! It works! Okay. Hi. So, clearly Brandon's got this thing working better, so let's give him another chance. Let's start this whole demo over again and give Brandon a chance to show you what this application can do.

So, hi, everyone. My name is Brandon Newendorp, and I'm an iOS software engineer, a much improved iOS software engineer, after the last 45 minutes. I've taken a lot of -- a bit of time here in our talk, anyway, to make my application's launch process better. I've taken some of my networking code, and I've made that both asynchronous and concurrent, so I don't block my main queue and take full advantage of my device's networking resources.

I've made sure that my databases are not blocking while they load. I've made sure that I'm copying my assets appropriately out of my application bundle into the documents directory. And while I'm doing that, I'm making sure I update my application's interface with interesting data as it loads. So I'd like to show you what QuickPix is supposed to be like. About 45 minutes ago, I tried to show you QuickPix. And this is the application launch experience I was giving my customers.

It was less than a minute long. It was a little bit more than a minute long. Less than ideal, shall we say. Because we were hitting the watchdog. With the changes I've made, I've put together a new version of QuickPix. You can see it launches almost immediately and then starts populating my interface.

I'm making sure that my application takes the time to only load photos when they're requested by the user, when they scroll, as you'll see. I've also made sure that my image post-processing library can initialize itself so that when I want to add a little bit of extra character to my applications or to my photos, it's ready to go. And this is the experience I like to give my customers with Quick Pick, and I hope you enjoyed seeing it as well.

Excellent, Brandon. What was that 45 minutes ago? All right. So now that Brandon's got his application running the way that he wants it, how is he going to prevent regressing? How is he going to prevent going back to what we had when we started this? Well, he's going to be vigilant.

And that's a really simple way of saying is that he's going to look really hard at the work he's trying to put into any method that involves application launch, app coming back from the foreground, even going into the background. Anything that is launch or launch sensitive, he needs to really make sure that what he's putting in there is important and needs to be done then. But vigilance only gets him so far.

He really needs to automate some performance testing. We've had the UI automation framework around for several versions of the OS now. And if you're not familiar with it, it's a framework that allows you to simulate user input into your application, both on the device and in the simulator.

And you can pair UI automation with the UI application. You can pair UI automation with the same time profiler that we used. And maybe every day that you commit code to your repository, at night you can build, run some automation against it, and in the morning see how bad you actually regressed or what kind of progress you actually made. And to help with this, iOS 6 also has another new feature, and that is the Network Link Conditioner. Yeah.

So most of you are clearly familiar with this from the desktop side. But if you're not, the network link conditioner can help you simulate both a slow and a high latency network. And now a slow network we've all been -- we've all experienced. But a high latency network is one that, you know, the data may flow at a normal rate, but it's going to take you a long time to get it started. In both cases, your application really needs to know how to handle those situations. And using the network link conditioner, you can test for this and make sure that your app is doing the best it can when the network's not cooperating.

So to wrap up today's session, if there was a single thing that we wanted you to take away from this, a single question actually, is to ask yourself any time that you're adding code, is do I need this here? If you are in your app's launch routine or if you're trying to add code that's going to get initialized in through a nib, ask yourself, the code I'm adding here, is the result of the data that comes from it, is it really important to have it right now? If it's not, find somewhere else to put it. Maybe you can defer it until a later time. Maybe you can make it asynchronous. Whatever it takes to make sure that that information gets available when it's required, but won't block the user from doing what they want to do with your app.

The network doesn't like you or your app or your users or anybody. And it's really something that you need to be aware of because we're becoming more and more connected to servers and services in our applications. Becoming defensive with the network and trying to plan for the best that you can in worse situations is the best you can do and really got to test for it.

Dispatch Async and Grand Central Dispatch are wonderful tools, but they can get you in trouble sometimes. They can make really easy to understand code kind of spaghettiish and can cause problems where a better tool may exist through NSOperation. When you're trying to solve a problem through Asynchrony, make sure you're trying to understand what you're trying to solve first.

And finally, when your application is launching and it finds itself in a really horrible situation, none of the content is available, or everything that you thought was going to be there really isn't, what can you provide to the user to show them that this is what my application can do, what it's going to do, and this is just an anomaly, a fluke? If you can give that to your users, they'll stay around until the app actually gets going, and then they'll see what you can actually do with your application. Like we mentioned before, the related session was Saving and Restoring Application State in iOS tomorrow in Russian Hill at 3:15. Highly recommend you check that out.

For more information, Jake Behrens is our UI frameworks evangelist from the future. He likes email, especially ones with cats. And there is lots of documentation and all this stuff, application delegate protocol, storyboards, and its operation and operation queue, and especially on state restoration. And of course, you're already on the forums, but if you're not, go there. Lots of great conversations. And that's the slide that says we're done. Thank you, guys.