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: wwdc2011-123
$eventId
ID of event: wwdc2011
$eventContentId
ID of session without event part: 123
$eventShortId
Shortened ID of event: wwdc11
$year
Year of session: 2011
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2011] [Session 123] Improving t...

WWDC11 • Session 123

Improving the Stability of Your Apps

App Frameworks • iOS • 57:45

Learn how to properly test and debug your application to ensure a great first impression. From edge cases to common pitfalls, learn tips and tricks on how to ensure that your application runs on multiple devices and multiple versions of iOS.

Speakers: Jake Behrens, Jim Turner

Unlisted on Apple Developer site

Downloads from Apple

HD Video (149.9 MB)

Transcript

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

Good morning, and welcome to Improving the Stability of Your Apps. My name is Jim Turner, and I'm an iOS binary compatibility engineer. And we're going to have a conversation today about why we want to have your apps be more stable. Why do we have a presentation about this? Well, we understand that your time as a developer is incredibly important and very short usually.

And we want you to be using that time to Creating better features in your applications or creating great new applications and not spending so much time fighting bugs or generally fighting the iOS development ecosystem. We want the applications and the code that you write today to run really well, not only on the devices we ship now and in the future, but on older devices and users that are running older versions of iOS.

Now, we're not going to be able to discuss absolutely everything that you can do to increase the stability of your application. So for the next hour, we're going to cover four things. First, binary compatibility. What is it and how do you ensure that your app remains compatible? Second, coding for tomorrow, how to make good choices when you use the API to ensure that your app remains stable in the future.

Anticipating the user. Thinking about some of the issues that your users are going to encounter when they use your application. and finally, testing. What to test, how to test, and if you've never created a test plan before, we'll go over creating a real basic one. So let's get started with binary compatibility.

The term usually means that an application, if it's adhering to the rules of the API, should not be negatively or adversely affected when the operating system that it runs on changes. Now for your users, though, this means something slightly different. It means that while your application is being updated and the user is updating their version of iOS, or maybe they're updating their device hardware, their experience should not be negatively or adversely affected.

Now, my group is the Binary Compatibility Group, and what we do is we test binary compatibility for you guys. While we're creating new features in iOS, we use your applications to see that the changes we're making are not breaking features that you expect. So literally what we do is we put your applications on-- The Next Great Thing.

And if you guys are doing your part right, it just works. This is Carl. He's in The Next Great Thing. So what are some of the things that you can actually do to ensure compatibility? Well, one of the first things that most developers have an issue with when they first come to iOS is what kind of device am I actually on? Am I on an iPhone or an iPad? So when I was developing this presentation, I went and searched the internet for how do I know which device I'm on? Yeah, I'm going to show you the code I found. And I want you to know before I even show it to you, this is not what we want you doing.

What you're seeing here is-- raise your hand if you have this in your app. How many of you-- what you're seeing here is that someone's using syscontrol, my name, and they're looking for the hardware machine type. And they're looking at the first seven characters to see if it equals iPad 1, 1. And if it matches, we're on an iPad, right? Not exactly. And let me show you why this is a really bad idea.

And remember, the My Group likes to put your applications on the next great thing. Carl. So we took some of your applications, we put it on the iPad 2, comma, 1, and this is what some of you guys did. You rendered in one fourth the size because you thought you were on an iPhone.

This is what binary compatibility doesn't look like. So the issue here is that you need to realize that you don't necessarily care which kind of device you're actually on. You care more about are you on an iPhone-sized device or an iPad-sized device? And to that end, we've made a function available, UI user interface idiom, that will tell you that information. If you use this, you will always know, and we will always be correctly knowing, which device you're actually on.

So another thing that a lot of developers want to do is use the great new features that we throw into every version of iOS. Lots of new and shiny stuff. But you have users who aren't going to be upgrading to iOS 5. Or they can't. Or they're running old hardware.

But you still want to support them in the same application. So take, for example, Core Image. Our incredibly powerful image manipulation framework is now available in iOS 5. And you want to use this framework, but you still want to support your users that are running 4.1 or 4.2.

How do you go about doing this? Well, the answer is weak linking your frameworks and checking for class availability at runtime. And this is a fairly simple process, but it's two steps, so some people get tripped up by it. First, to do weak linking, I'm going to show you a screenshot of what Xcode looks like to do weak linking.

And here I have a project. Here I have a project that has the Core Image framework already linked, already added into it. And if you look right here, I changed the required pop-up to optional. That's it. At runtime, the linker now knows that if Core Image is not available, it just doesn't worry about it.

The problem though is that if you still try to use a Core Image class, your application is going to crash. So to check for class availability, you need to use NSObject's class method. Here we're trying to see if CI Image is available. We send it the class method. And if we get back a non-nil answer, CI Image is available. And this is safe going forward and backwards.

Another issue users, developers, excuse me, have a problem with, we see it in binary compatibility a lot, and developer technical support sees it too, is orientation. And there's one specific problem that seems to crop up over and over again, and that is, how do you start your application in landscape, and how do you only support landscape? This isn't a terribly difficult problem to solve, but a lot of people try stabbing at the status bar first to make this happen, and it's not the right answer. You need to start off by editing your project's Info.plist. And the first key you're going to look for is supported interface orientations. Now, this is an array. And in this array, you're going to enter in each orientation that you want your application to support.

The second key you're looking for is initial interface orientation, and this is the orientation you want your application to be initially launched in. Now, it's important that you actually make the initial orientation interface orientation match the first elements in the supported interface orientations. But once you do that, your application should launch in the correct orientation. But we're going to ask your application at runtime what orientations it supports, so we need to go a little bit further and edit some of your view controllers.

Now, every view controller in your application needs to know how to answer the question, should auto rotate to interface orientation, especially the one designated as your root view controller. So continuing with our example, if we pass in the interface orientation parameter we give you to UI interface orientation is landscape, and if you return the answer, your application now does the right thing.

It'll launch in landscape, and it will only rotate to landscape. And finally, if you're an OpenGL user, you should really check out GLKit. It's a new framework in iOS 5. It has a whole bunch of fun stuff in it, but it also should help you with some of the orientation issues you might be having.

Finally, there's some other features that you can have your application configure that have nothing to do with your source code. Things like Wi-Fi needs to remain active while my application is running, or I want to share documents with my application and with iTunes on the host. Or I absolutely have to have a gyroscope. My app doesn't work without it. These are all done by editing your project's Info.plist. So going right back to it, to support continuous Wi-Fi, you add the key application uses Wi-Fi, and set its value to a Boolean yes.

To do file sharing, the key is application supports iTunes file sharing, and it's a Boolean value again. And finally, to restrict your application to a specific device capability, you need to add the key required device capabilities. And this is a dictionary. And for each capability you want to restrict on, you add another Boolean value for yes or no.

There's an important thing to note about required device capabilities here. And that is, if you don't have a preference about which capability, like you don't really care if the device has a Javascript or not, don't set any value at all. Because a value of no here does not mean the same thing as a value that doesn't exist.

There's quite a few other things that you can actually do by editing your project's Info.plist, so I encourage you to check out the documentation to see if any of the other items apply to your application. So with that, I'd like to bring up Jake Behrens from Developer Technical Support to talk about coding your application for tomorrow.

Thanks, Jim. So what if I told you that I've been sent from the future And in this future, I've seen your app crash. I've seen you overburdened with stacks of crash logs. And I've seen your app become quite unstable and incompatible with future software and hardware. Over the next 25 minutes, I'm going to show you some techniques and some things to keep in mind so that you can ensure that this future is destroyed and will probably cease to exist. So my name is Jake Behrens, and I'm an engineer in Developer Technical Support. And I work with developers like yourselves every day, troubleshooting issues within your code and applications. And this is coding for tomorrow.

So I'm going to talk about three things today. First, custom functionality. How do we implement custom functionality properly in a supported way so that you can get assistance when something goes wrong? Then I'm going to talk about some things that you can do that take a small amount of time but make a huge impact on your project going forward. And then finally, we're going to cover crash reports. How to make them more informational, how to read them, and how to get the most out of them so that you can ensure that your application updates are going to be compatible. So let's start.

With custom functionality, this is achieved typically by extending existing classes that we ship in our public frameworks. But when you create this custom functionality, you want to make sure that you're not only thinking about the future of your software, but the future of our software. What's that user experience going to look like when your user runs your application for the first time on iOS 5? Is it going to crash? Is it going to hang? And even further down the line, when they run it on new hardware, what's that experience going to be? Maybe you haven't had time to issue an update for the new hardware. Is it going to be stable and compatible? So really, this comes down to your users. You want to ensure that they've got the best possible user experience. So let's look at an example.

Here I've got a list of products, and maybe we want to customize the UI navigation bar. Here's a hint, it's the bar at the top. You're in a meeting with a client or your manager comes to you and they say, you know, look, the custom look and feel of iOS is great. But this application is for our company. And we really want to mesh the branding with our company. So couldn't we make it a little more orange? Well, yeah, you can.

But there are some ways you could do this, and I'm going to go through two ways that you probably don't want to do this, and then two ways that are probably going to be a better way. So how can we achieve this functionality? Well, we could start with... Maybe a category. But a category can be dangerous in this. So for those of you in the audience new to Objective-C, a category is a great way to extend an existing class.

So, I'm going to talk about the implementation of the StableTools application. So, the StableTools application is a very common application that you don't have the implementation for. But the issue here is that we've implemented our own DrawRect, and a lot of you may think, okay, I'm overriding DrawRect, but you're not overriding, you're replacing DrawRect.

So, any performance tuning that we've done within the project is what you do, and so you could run into issues here. The other problem here is that, well, since this is a category, this applies to every instance of UI navigation bar within your project. So, every single bar is going to look like this, which may not be the exact user experience you want.

So, what's another way we could do this? Oh, and also, with categories, since you've implemented this via a category, if the original implementation was done with a category, which one runs at runtime? It's undefined. So, what's another way that we could do this? Well, I've seen some people do this.

Yeah, this is really bad. So not only have you, what this is is method swizzling. And so you're digging into the classes method list and you're saying, yeah, that method, yeah, don't do your own implementation, do mine. I know what I'm doing, right? So what this does is suddenly, like within the category, well, you've got it at runtime now, but you've killed any performance tuning that we did in ours.

And you have no way to get back to the original implementation. And really, at the end of the day, this is just a big hack. You really don't want this. Because when you write into me and you say, hey, I've got this issue with my UI navigation bar on the new OS, I don't know. It's just not being good.

It's just not being called or, you know, it's just not working. Well, I can't really help you because, you know, this is really not a good thing to do. You're just hacking the system. So you want to make sure that you do it in a supported way. So what does that look like? Funny enough, a subclass. So we can subclass UI navigation bar and implement our draw rect. And you'll notice here that I've set up the image in init with coder.

So we won't run into the issue where, you know, possibly we're having to recreate the image when draw rect gets called multiple times. So now I know what you're saying. You're saying, look, Jake. I've read the documentation, and I know that the navigation bar property on UI Navigation Controller is read-only. So it's great that you wrote this subclass, but how do I actually implement this? Well, the secret here is by utilizing the nib.

So you can select the UI navigation bar within your nib, and then you can set the class of that UI navigation bar to your custom subclass. So now, like I said, you've got it only in specific instances that you wanted. So this is great. So now going forward into the future, it's even better.

In iOS 5, we have the Appearance Proxy API. So this is one line of code now, totally supported. No categories, no hacking, no subclassing, it just works. And so you can do this with setBackgroundImage for bar metrics. And I would also suggest that if you didn't catch polishing your app or customizing the appearance of UIKit control sessions this week, catch the repeats. Polishing your app will be today, and then customizing the appearance of UIKit controls will be tomorrow. Great sessions that dive a little bit deeper into this.

[Transcript missing]

In this kind of situation, if a certain framework is very essential to the core functionality of your application, you want to make sure that you at least take five minutes and go through the headers for all those classes in that framework. Jake Behrens, Jim Turner Because in some specific instances, there are additional insights.

You know, a lot of this is in the class reference, but every once in a while, you get something with, well, if it returns this value, this is kind of the situation that we were thinking of, or, well, we kind of anticipated that it would be used this way, when maybe you're trying to use it another way. So this can kind of give you a little bit more insight.

And now, there's a great amount of awesome code out there on the internet. And man, before I came to Apple, if a client was breathing down my neck, it was awesome to be like, "Yeah, I need that functionality. Yes, that does it. Okay, ship it. Awesome!" Well, before you do that, you should probably read through the code.

Because in some cases, if it's an older framework, you could run into an issue where they're calling a private API and you didn't even know. So then you get rejected and you're like, "Oh, man, I waited seven days and now I just got to fix this." Another issue is you don't want to add in a 5-meg framework to do a photo gallery.

Or a thousand lines of code to reimplement UI button with some faux CSS styling. So you really want to take some ownership of this code. Anything that ships within your application is your responsibility. When your users come back and say, "It's crashing," you really can't say, yeah, use this framework.

probably shouldn't use it, so sorry about that. They just know that the application that they know and love and use every day suddenly doesn't work quite the way they intended it to. And so you want to make sure that you're analyzing the code, you're profiling it, you're putting it through all the ringers that you would put your own code through.

So now you've made your code more compatible, you've made it more stable, now you've got crash logs. So I hate to be the bearer of bad news here, but your application's probably going to crash. My applications crashed before I came to Apple. Now they don't. No, no, no, I'm just kidding. They still crash. So you've got all these crash logs.

Well, let's start at the beginning. How did we prepare for getting these crash logs? Well, when you finally got the build and you're like, all right, I'm ready to go, we're going to throw this out, before you give it to your beta testers, before you submit it to the app store, do the build and archive from within Xcode. What this does is it sets aside, it does that build and it sets aside the binary and the DSIM file.

And what happens here is that the binary is your application and the DSIM is kind of like a mapping of these addresses to these symbols. Or your code. And so get this, back it up, co-locate that back up, do everything possible to make sure that you have that. Because this is going to help you make your crash log much more informative going forward. And we'll get to that in a moment. So you've done your preparation.

Now, where do I find them? Well, you can get them off your test device because I know you're not running the newest seed on your personal device, right? Well, maybe. So, get them off your test device. You can also get them in iTunes Connect when your application is in there.

Also, if you have a user contact you directly, maybe they say, you know, I've got your app, it changed my life before I was lonely, now I've got a kid, a dog, and kids, it's great. Well, you can point them to this directory and say, okay, pass me up these crash logs and I'll take a look. You can also read technical note TN2151 on the reference library to get the other directories from the other operating systems.

So now you've got them, and your user has emailed you crash reports, and you're like, "All right, let's go, let's get this done." And it looks like this. I don't read hex. But remember when we did the build and archive before? Well now we're gonna use that. So what you can do is you can take this crash log and you can drop it into the device log section of the organizer in Xcode. And it goes, okay, you did the build and archive, so I've got the DSIM, I've got the binary, and now I can make this look like this.

So much more informative. Now this is fully symbolicated. And realize sometimes you might see them, especially from app review or maybe iTunes Connect, where our code is symbolicated, but your code isn't symbolicated. So you wanna make sure that you do this just to be sure. And I'm gonna show you how you can make sure.

So you've got this and you're like, okay, well this is a lot of information, so how do I kind of sift through this? Well, the first thing you wanna look at is exception codes and exception types. This is gonna give you kind of a general idea of what you're dealing with. And the more you read crash logs, the more you're gonna say, oh, okay, it's that kind of thing, okay, I got that. I know kind of where this is going.

Another thing to look at is which thread crashed. So in this example, it's thread zero. And if you've ever heard, you know, don't block the main thread, that's thread zero. So now we know which thread crashed, and we can scroll down and get to that thread. So we're there. No.

Since we're here, we want to start looking through the backtrace. So you want to follow down the backtrace until you get to the first instance of your application. So you can see here at frame 10, on the left-hand side is my application. And on the right-hand side, it's mapped the addresses to symbols. And so it says, okay, in my application view controller's viewed load method, something happened. And it even has more information for you, which is in the implementation file, it was at line 36.

So, great, now you know almost right where things went wrong. You know what line of code was last executed before everything went south, but we can still gain a little bit more insight if we start following up the backtrace. So, on frame 9, you'll see objected index was called on an immutable NSArray. That's what the I after the NSArray stands for. And so, okay, now we're at line 36, objected index, NSArray, we've got a lot more info, and then at frame 8, you can see the exception was thrown. So, alright, let's look at the code.

So if we go to Xcode, we're in my application view controller's view to load method, and we look at line 36. Ugh, I'm so embarrassed. So what I did was I created an NSArray with nil values, and then I'm calling object at index zero, which doesn't exist. So it crashed.

There's another potential crash that could have happened here, which is I released it and then I was calling it again, which is a problem. So I could have hit that, but since this was all I was doing, the memory hadn't been overwritten yet. And then I wasn't even setting it to anything.

So yeah, I just really didn't. I must have been sleeping. So now I know exactly where I needed to fix my code from looking at the crash report. I was able to symbolicate it. I was able to find out exactly what needed to happen and what the problem was.

So as we're reading through, there are some common exceptions that we can find. So on Mac and iOS, you can see exe crash, which was our example. And this is kind of your standard crash exception. Hey, something in the logic went a little weird, so something happened. An exe bad access usually refers to some memory mismanagement.

Something happened there. And then on iOS specifically, we have ate bad food, which means that the watchdog timer killed your application. Maybe it didn't launch in time, or maybe it didn't terminate in time. And then we also have deadfall, which means that it just was so unresponsive that the user had to force quit the application.

So, these exceptions are great, but how do we kind of avoid these? Well, here's some quick tips. On exe crash, like I said, these are usually logic issues. So, follow back through and see, you know, where something went wrong. In my case, I was setting an array to nil and then trying to call an index that didn't exist.

With exe bad access, make sure your ownership is in place if you're manually managing your memory. So, you can basically remember this as narc. If you're calling new, alloc, retain, or copy, you own that now. So, you need to dispose of it properly. But going forward, I would really, really suggest that you check out our automatic reference counting, which our teams have done an amazing job on. And this will help you.

Get past these trivial memory management issues. Another thing to look at is thread access. So, you know, in my example, we looked at thread zero had crashed. Well, if we've done a multi-threaded application and we see a crash on another thread, that may clue us in as to, oh, okay, maybe I should check out some of my threading code because I create one here and this is the kind of issue that has happened. And you also want to make sure that you're not, you know, trying to mutate an array.

On one thread while you're trying to release it on another. That can lead to sadness. So, then with ate bad food, I've seen some over-eager developers who are like, wow, main window is awesome. I'm just going to put all my view controllers in there. And I'm going to put all my resources in there.

Well, the problem here is that that's loaded when your application launches. And so, all of those objects are being loaded. And it just makes sense. It may take a really long time. So, what the watchdog does is it stands by to the side and it says, okay, we're loading an application. We're loading. We're loading.

Yeah, we're still loading. What is he doing? No, kill it. Just kill it. And we do. Another thing here is to watch all the initialization code that you're doing in applicationDidFinishLaunchingWithOptions. You want to try and return from this method as quickly as possible so that your application is responsive and the system knows that, okay, the application's up.

We're doing stuff. We're going. If you have initialization code in here, make sure you're not doing things like initWithURL. Maybe you're trying to ping a server and it's just not responding and it's just sitting there. Well, the watchdog timer is going to come along and say, "Nah, we're not waiting on that anymore," and it's going to dump your app.

So, The things to take away is that you can change this future that I am from. You can correct your clever code. And for those of you who are new, well, when you see this clever code, there's usually a better way. And for those of you who are veterans in the audience, I know you guys wouldn't put a code like that in, right? Right? Maybe.

So you want to check out if there's a better way to do these. And then make sure that you're taking ownership. Everything that ships within your application is yours. It's your responsibility. If you're pulling in static libraries, you want to make sure that you either have access to that source or you have a support channel so that you can say when you get a crash log back and a crash is happening in there, you can try and resolve that as quickly as possible.

And then finally, if you do incur a crash, knowing your crash reports and knowing how to symbolicate those, how to prepare for these, you know, issues is really, really important. I mean, this is how you're going to issue the next update of your application for your users. And you want to make sure that you can do that quickly and effectively. So with that, thank you. And I'm going to pass back off to Jim.

Thank you, future Jake. Did you leave the lottery numbers up here for next week? No. So let's talk about you taking a step back out of your role as a developer and putting yourself into the role of your users for a little while and thinking about some of the issues that they're going to encounter while they use your application. Specifically, four things here. The first is upgrades to your application.

Not only to your application, actually, but to the device and to the operating system as well. Second, the network. The network has a mind of its own. Some people think it looks like this green thing. We need to help the user understand what's going wrong when the network doesn't cooperate. Third, targeting devices. How to ensure that the user experiences exactly what you intended them to. And finally, knowing your customers. Thinking about the properties of the user themselves and not just basically what features they would like to see.

Now, upgrades to your application are normally a very benign process. The user goes to the store, they download it to the device, they launch it, and it works just fine. It's during this relaunch process that your application needs to be mindful of upgrading the data that it created on behalf of the user that it still manages. And specifically, there's three areas.

User defaults. If you're saving a lot of user data in preferences, which is fine, you need to make sure that the keys that you were using before are still supported in the next version. And if they're not, or for some reason you can't use them, that they need to be upgraded or migrated. You don't want your user to have to reconfigure your application every time they upgrade it.

Second is Core Data. Core Data is our persistence framework, and honestly, it expects to be upgraded. And to that end, it works very hard to make what could be a very painful process as painless as possible. Usually, if your data models aren't very complicated or you don't have a lot of significant changes from version to version, providing the data models from each version of your application is enough for us to infer a migration map on your behalf, and we'll just do the migration for you.

However, if you have a really complicated data model, or you've made a large number of changes from one version to the next, you can still do the upgrade, but it's going to be more on you. You're going to have to provide your own migration map. You may actually have to interact with an NS Migration Manager a little bit, but it's totally possible to do really complicated migrations.

We mentioned this because, unfortunately, a lot of developers now seem to think the route that forcing their users to upgrade the application or reinstall the application is the preferred method to upgrade it. This is a really bad user experience, and it makes the users lose all the data they have created beforehand. And finally, the third thing, custom databases. If you're using SQLite directly, maybe you have a XML store or a flat text file even, or maybe your company has a proprietary binary format database, ensure that those get upgraded as well.

Upgrades on the devices and for the operating system, again, your applications probably don't necessarily care when the device upgrades. Again, you'll get relaunched and you'll be fine. But if you think about it, your application could actually be shut down on an original iPhone running iOS 3.1.3 and through the restore process be brought back up on an iOS 5 device or iPhone 4 running iOS 5.

That is a huge difference in functionality from not only just the device, what the device can do, and what the OS can actually do. So your application finds itself in a very strange but brand new world. The environment is totally different. And this becomes important for you guys when you're restoring your application's last known state.

Now, state saving is something that you're doing as necessary and every time that we're calling application did enter background. And it's the information that you need to know. To bring your application back up to the same reasonable point that the user left it in. But if you're saving transient data, information like about which networks are available or what type of network the user was last on or URL resources that may or may not be there anymore. You need to verify that this information is still saying when you relaunch. Sometimes we see it that you just blindly restore the state. It's totally wrong and the application crashes.

A final note about upgrades to the devices, new devices out of the box have no content. If your application is expecting address book records and you start blindly indexing into them, what actually happens? Be prepared to handle the state where there are no records or no content available to your application.

The network. The network, like I said, it has a mind of its own. And it usually chooses to foist its anger upon your customer at the exact point in time that they really need to get the data that's in it. And what we advise is that you hope for the best here, but you really need to expect the worst. And when you're in that absolute worst case scenario, you need to handle errors as gracefully as you possibly can. Now, the error handlers in our protocols are optional, or usually marked as optional, but they're really not.

It's the last chance that you have to undo any network state or clean up any problems that you may have encountered during your network transmission. So, the error handlers are optional, or usually marked as optional, but they're really not. It's the last chance that you have to undo any network state or clean up any problems that you may have encountered during your network transmission.

Something we suggest that you do before you even send your network request though, is to test for reachability. Reachability is the knowledge to know if your request is actually going to leave the physical device before you even make it and in which network it's going to take. Is it going to be on Wi-Fi or is it going to be on cellular? We have some great sample code that shows you how to use the Reachability API, but there are two things that you need to remember about it. Is that the first, it does not indicate availability. Just because we say that your request is going to leave the device, doesn't mean it's actually going to get anywhere. If your server's not up and running and providing data, it's still not gonna work.

Second is that domain name, excuse me, that reachability requires domain name resolution. And DNS is not exactly the fastest thing on the planet. So the first time you make a request to reachability, it's probably going to give you the answer of, I don't know. You need to wait until we call you back with the real state of reachability before you decide how to proceed.

And finally, disabling UI that's bound to a network function. This is fine, but you want to be very careful about how you go about doing it. A lot of times, we'll see an application start up, and it immediately blitz this loading view on top of all of its UI. And reachability fails, or the network isn't present, or some other transient error happens.

And now I'm stuck with an application that's a loading screen. Disabling UI, just do it for the controls and components that are bound to the network itself. And let the user try to use as much of your application as they can that doesn't require the network. Targeting devices.

Users do not buy simulators, guys. They buy devices. They buy iPhones. They buy iPads. I looked for a guy holding his laptop to his ear. I could not find an image of it. If you're not testing your application, if you're not building your application for a physical device and you're only testing in the simulator, this is a real problem. The simulator does not emulate the actual experience that the user is going to encounter when they run your application on their actual device. There is no substitute for testing and developing on real hardware.

But while you're developing for the current hardware and the current version of iOS, we want you to understand that there are users that are not going to be able to upgrade with you for one reason or another. So while you're developing for the new hotness, we want you to have a plan in place to figure out how to handle those users that are going to be not coming along for the ride.

As the gap between the oldest version of iOS that you're going to support widens from the version that you're targeting, it becomes increasingly difficult to satisfy both of these ends. And eventually what's going to happen is that the end that has the most of your customers is going to start going to have a degraded performance because you're still trying to support this end of the long tail where there's only a few users left.

You need to take a hard look at your application when you get in this situation and see if these users over here really are still worth supporting. Now, I'm not telling you you should go and dump all your old customers. That's not what I'm saying. But what I'm saying is if you're going to develop an application that targets older devices and older versions of iOS, you need to key specifically on responsiveness when you come to this. When you're thinking about features, if that feature works awesome on an iPhone 4 but runs for junk on an iPhone 3G or an iPhone.

You need to make sure that they get the same experience as if you were actually developing that application for them and not just as an afterthought. If you can't do that, consider dropping features or maybe toning them back a little bit so they get a really good user experience as well.

Finally, knowing your customer. And this is more than just knowing which device they're on or which features they're asking for. It's realizing that some users, they're not as technically savvy as everyone in this room. Kind of like my father-in-law. Or maybe they don't speak the same language. Maybe like your father-in-law or my wife some days.

You need to think about these things. Things that have nothing really to do with the technical aspect of your application, but they still are a part of application development. And specifically, there is one thing that you can actually do and implement in your application that will actually pay you a dividend the more you actually add it. And that is accessibility.

Accessibility is enabling your application to be usable by those with special needs or physical disabilities. And I'll tell you that we are incredibly proud of how accessible iOS has become. I mean, if you think about it, we make a device that literally is two pieces of glass and three buttons. And yet people who have never seen the thing can actually still use it every single day. I mean, even future Jake and his future whatever iPhone that he has still has to think that's pretty cool.

We give you the power to enable your application to be just as assistive as iOS is. If you're using standard UI kit controls, you get most of this for free. When the UI becomes a little ambiguous, you can help us with some hints. But for the most part, it just works. Now, if you're providing an entirely custom view and custom controls, you need to support the UI accessibility protocols.

Now, what's the payoff for this? Why, besides other having more customers for your application, The payoff here is that the more accessible you make your application, the more automatable it becomes. The automation system that we have in iOS is completely dependent upon accessibility. So the more accessible you are, the more automatable you become and the more easy it is to test your UI. There is a great talk on accessibility going on right now about 20 yards that way. I would highly recommend that you check it out on the videos. They have done fantastic things with accessibility in iOS.

Now we're at the part of the talk where you're probably sitting in the audience and you're thinking that, oh my god, I had that iPad 1.1 check in there. I should probably get rid of that. And you listened to what Jake had to say and you probably have all these crash reports that you probably should go and gather up and actually fix what's actually happening.

And you maybe think, well, actually, accessibility would be kind of cool to add to my app. So you go home, and you bundle up an update, and you put it off to the store, and we approve it. And you get to see those three words that everyone loves to see, right? Ready for sale. Woo-hoo! Right? It's a party. You guys actually let go of balloons, right? Users start downloading your update, they start posting reviews, and you start seeing these reviews, like, "Oh, this app was awesome. Now with the update, it doesn't work anymore." Okay, that's not good.

Oh, here we go. I love this app, but since I updated it, it won't start. It keeps crashing. Well, that doesn't happen for me in the simulator. Okay. Oh, great. "Every update makes this game worse. They want their money back." Fabulous. It can't get any worse, right? That's nice. OK, great. I'm not even quite sure what this actually-- I think it means I need to make a salad with eggplants. I have no idea. It's not usable. It's not useful.

How did we get to this point? If you've done everything we've told you to do up to what we said so far, what happened here? What component did we miss that makes your application stand the best chance of being truly stable and a really great experience for your users? And we'll get you reviews that will instead look like this.

A component is testing. Every application you ship that you deliver to the App Store needs to be tested. I don't care if it has a single button and makes fart noises, or it has a million buttons and connects to every social network on the planet. It needs to be tested.

Why does it need to be tested? Because you don't want to see reviews that have eggplants in them, for one. But if you ship bugs, that means you have to fix them. Your users are going to find them, and they're going to submit bug reports, and they're going to demand that you fix this stuff.

So you're fixing bugs instead of adding features to your application. Most importantly, testing finds regressions. Regressions are changes to your code that break existing functionality. This is something internally that we focus on a lot. When we make a change, we want to know that we haven't broken something that already exists.

So you may be saying, testing is hard. I don't want to do testing. Well, I understand that. Testing can be a little difficult, but a little bit goes a long ways. And if you've never actually done testing, if you've never really tested your application, you may have no idea where to even start. So let's walk through a very simple test plan, but it should give you some ideas of how you can go about getting started with testing your application.

First off, you start with the tools. Maybe. And the first tool that we're gonna start off with is Xcode. It's the tool that you guys all know already. And not only does Xcode run your unit tests, which, You're all running unit tests, right? It runs the Static Analyzer. And we've talked a lot about the Static Analyzer this week. And if you haven't been in any of the sessions, it's basically a tool that will walk through every single code path in your application, and it finds things that the compiler just isn't gonna see.

Strange return values, dead code stores, things that are dependent on branches in your application. It's a fabulous tool that you should run early and often, but you also need to understand that when the, the analyzer will return a certain number of errors, and if you get like 20 of them, fix just the first one and rerun it again. It has kind of a cascade effect, that you'll find that if you fix one and rerun it, a lot of the other errors will most likely go away.

Second is Instruments. Now, Instruments really is about the biggest gun that you have in your arsenal to shipping a really great application. It does dozens of different things. But for the sake of this test plan, we're only going to discuss three of them. And the first one is allocations.

Allocations analyzes the memory lifecycle of blocks in your code. Basically, it watches as objects come and go. But allocations usually isn't run by itself. It's ran with leaks, which checks for memory, abandoned memory in the heap. Jake Behrens, Jim Turner Allocations analyzes the memory lifecycle of blocks in your code. Basically, it watches as objects come and go. But allocations usually isn't run by itself.

It's ran with leaks, which checks for memory, abandoned memory in the heap. And finally, it's Time Profiler. Time Profiler is a great tool to let you see what your application is doing while it's actually doing it. And so your application becomes slow or unresponsive, if you fire it up in Time Profiler, you can see where those cycles are going.

The next component is automation. And like I mentioned before, automation is our ability to simulate user input into your application, either via an actual device or on the simulator. And it allows you to do basic UI interaction and UI regression testing. If you have a really complicated navigation controller layout and you are meant to make changes to it, if you record an automation first through it, you can make sure that changes to it aren't going to break how it works.

Automation is part of Instruments, and it's part of the Automation plugin. And both of these have actually received feature upgrades for iOS 5. And starting with Automation, we can now record scripts for you. And this is really nice because previously you've had to-- Everyone clapping in here realizes before you've had to type all this stuff in by hand and try to figure out where you're at in the hierarchy. Now you just plug in a device and tap out where you want to go and we'll figure it out for you. Second is the instrument supports a command line interface.

Meaning that you can run any saved trace, or that doesn't even need to be automation, and you can integrate it with any existing shell scripts or any existing automation or build plans that you have. There was a great talk on what's new in automation, if I can click the button. That was yesterday in Marina. Check it out on the videos. They did some awesome stuff with iOS 5 as well.

[Transcript missing]

With the iPhone, it's the same exact test, but I want you to stay on the cellular network. And again, you do this because you want to see how your application responds to being on an entirely different network. Once you've got these tests complete, you can actually be reasonably assured that your new application runs well on the old version of iOS that you support.

You may still have issues, but for the most part, you've got it covered. Now, moving on to the next version, the current version of iOS, what I want you to do is install the oldest version of your application that you can. Now, this may not actually be possible, because sometimes it just won't go.

But a new feature in Xcode with 4.2 is that you can actually save off the application's sandbox and then restore it onto a device to perform this very test, and that test is to upgrade your application. So if you have an old device learning 4.1, you can plug in into Xcode, pull off the sandbox, and stick it on the iOS 5 device, and then you can perform your upgrade.

And you're upgrading to see if core data gets upgraded, your user defaults stay the same. And then the test from there is exactly the same. You're going somewhere else. you're testing on Wi-Fi. Test like a user. And as you can guess, wash, rinse, and repeat with the iPhone, it's the same exact test.

And like I said in the beginning, this isn't really all that much, but it tests a lot of functionality in your application. Functionality that your users, that's the first thing they're going to see. They're going to use your application in the coffee shop, or they're going to use it traveling down the interstate. So doing these tests gives you a reasonably good assumption of what the user is going to see in your app, and will find all those bugs before they do.

So wrapping up here today, what's the things we talked about? We talked about not caring so much about which device you're on, but querying the API to know what you should be doing with what OS that you're running on. Jake talked about making good choices with the API that you're using, and that when the machines take over in the future and enslave all of humanity, your app still runs pretty well.

Stepping back out of your role as a developer and stepping into the role as a user and trying to think of the issues that they're going to encounter, because you guys are users too. And you really should be making applications that are really great for yourselves. And finally, testing.

Test plans aren't incredibly sexy, but they are a required part of application development and really are the best bet to getting really great reviews and providing a really great user experience. I'd like to thank you guys for your time. If you need to talk about us anymore, send Bill an email or read the documentation. Or if you have any questions, come up here and ask us, and we'd be happy to help you out.