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: tech-talks-2013-18
$eventId
ID of event: tech-talks
$eventContentId
ID of session without event part: 2013-18
$eventShortId
Shortened ID of event: tech-talks
$year
Year of session: 2013
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2013] [Tech Talk 18] Hidden iOS...

iOS 7 Tech Talks #18

Hidden iOS 7 Development Gems

2013 • 50:24

With such a wide variety of frameworks and APIs to choose from in Cocoa Touch, it's easy to miss some of the real gems. From tips for how to get the most out of Xcode 5 to harnessing the power of the Objective-C language to entire classes you might not have thought about using, everybody is sure to take away practical and useful tips and tricks to make developing iOS apps easier and more productive.

Speaker: Paul Marcos

Unlisted on Apple Developer site

Transcript

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

All right, welcome back. This is our last session of the day, Hidden iOS 7 Development Gems. I am still Paul Marcos. And obviously, we've been talking all about iOS 7 all day today. So just a ton of new technology, and I'm not here to talk about any of it, so at least not directly. What this session is about, right, this has been a long day. You've had a lot of information to absorb. And instead of talking about the APIs for new technology, I actually want to focus this session on technique, right? How you use these APIs and things that you can do to improve, you know, what you do in your apps and how you develop them and how you can get the most out of our APIs in your apps.

What this session really is, it's just a list of tips and tricks, is probably the best way of putting it. And these are tips and tricks that actually I've gathered from a lot of my colleagues at Apple. And this is one of the most fun presentations to put together because I can go around to a lot of people around Apple and basically ask, hey, what are the things that you've witnessed when you've been working with either, you know, another colleague at Apple or an external developer, that you've witnessed when someone says, whoa, whoa, wait, stop, how did you do that? What was that thing that you just did? Explain yourself to me. Those are the little gems that I think people often either they don't know about or they just don't find them. And that's what I'm trying to surface here is just kind of inform you about new things and new ways that you can do stuff. So one phrase that I'll use throughout this presentation is, but there's a better way. So those are the types of things that I want to get. So my measure of success for this presentation is if everybody learns at least one new thing, one new tip. These things are broken into four different sections.

So the four areas that I'm going to go through are Xcode and some source code editing, project management tips. Then we'll move into the iOS simulator and a few things A few techniques that I want you to employ to apply a little polish to your app and really kind of raise the bar of detail in your app.

We'll dive into some instruments things. So if you haven't used instruments, hopefully this will give you some insight into how you can use instruments to really look at data and performance data in your app in a different way and get some really important insights in there. And then we'll wrap up with just a couple little pieces of API or things around API in our frameworks. So let's get started with Xcode. And the first tip I have here is method completion. And now you may say that you're already familiar with completion in Xcode, and it's great.

You know, it really helps write code faster. The case, the completion that I'm talking about here is specifically like when you're trying to implement a new method. So you have your class and you want to add a new method to it. You know, here's an example of a piece of API, a delegate callback that I think Dave talked about earlier today. And you know, if you've worked with our APIs, you know that we are very verbose, right? We love a verbose API. But we actually think that's important, right? It's very self-documenting. But in a case like this, you know, animation controller for presenting, presented controller, presenting controller, I have to read this, source controller, right? These are really long methods. The return type is an ID with a protocol there.

So you're never gonna remember this by heart. So when I type something like this, before we have method completion, you might go to the header file and copy and paste it, or you'd go to the documentation and copy and paste it. But there's a better way. So what I wanna show you is the better way.

So here's the example. And most of these tips have kind of a scenario to them that I'll introduce. So the scenario is you want to add this method to your code. So what you would do is start typing, you know, the hyphen for the instance method, and then the name of the method, note I'm omitting the return value here, and that's the key to this, is if you just start typing the name of the method, when you do that, Xcode will pop up a little completion menu here like you're familiar with, but it's filtered the results, and you're only seeing methods that make sense here. Right, it's not every method in the world, it's methods that make sense for this class to implement, either because it's inherited from a superclass, or maybe it's one of the methods in a protocol that this class conforms to. So you get that, you choose it off the menu, and now you get the full selector in there and you didn't have to type more than just a couple characters.

So that's a really handy thing, makes adding new methods very, very efficient. So it works with both instance and class methods, so hyphen versus a plus, obviously. And again, the key is you omit the return value and just start typing the name of the method, 'cause that's usually something that you remember, you know, "Oh, it was like animation controller "for something," you know, so you get the first couple characters. Now, one place that you would type the return value is if you want to add a default action method. For example, if you're connecting a button to a class and you wanna add a new selector that that button is going to call. In that case, you might do what's shown here, where you type, you know, dash, open parentheses, IBA, as if you were typing IB action, and then Xcode will complete that to the default action selector. So, that's our first step. You know, that's kind of about the size of each of these.

So that's method completion. Next up is edit in scope. And I should say that there's an embarrassing admission that I have to make. This was one that somebody told me about, and I didn't know about this. And it's right there under your nose. So this is kind of proof that you can teach an old dog a new trick. So here's my trick. The scenario for this one is you have some code.

What this code does doesn't really matter, so don't worry about reading it. The important point here, and if you're a speed reader of code, you might notice this code has two local variables defined at different levels of scope, but they have the same name. I don't know about you, but this drives me nuts. When I see this, I'm like, oh, that's confusing. And in this example, it's compounded by we're returning a field from one of those. And this is like where, do you feel lucky? Do you know which parameter you're getting? So I don't feel lucky. I'm not a lucky person. So when I see code like this, I want to change it. I want to clarify this. So I want to rename one or maybe both of these variables. variables. So Xcode has this great command. It's right there in the menus. And this is why I was embarrassed, because it's like, it's right there in the menus. It's called edit all in scope. And the idea is you select that and -- sorry, you select the first local variable. Here I've highlighted the first occurrence of position. And when you select edit all in scope, Xcode will highlight in gray all of the places where that local variable is used. So you're actually seeing, you know, you're visualizing scope in action here. So you can use this as a training tool. But Xcode will highlight only the places that that local variable is used. So that's handy and interesting. You can make sure it's being used in the right place. But the cool part of this is now when you start typing to rename that variable, Xcode will live replace all of the occurrences of that variable in the appropriate scope. So that's really the cool part. You don't have to use the find and replace panel where you might mistakenly replace the wrong thing. So that's edit and scope. The menu item is right there in Xcode. And now that I know about this, this is one that I actually use like really fairly regularly.

So I'm very happy about that. So the next one is called debug quick clicks. And as I mentioned, my measure of success here is, you know, making sure that everybody leaves this room learning something. This one is kind of my ace in the hole because we haven't even shipped it yet. So actually, I lie a little bit. It's in the Xcode 5.1 beta. But, you know, I'm counting on none of you knowing about this. So what is it? Well, in Xcode 5, we introduced in the visual debugger in Xcode the ability to quick look variables or objects. And when you do this, you'll see an example in a second, you hover the mouse over something, you get the little pop-up window, and there's a little eye in there, and you click it, and you get a quick look. And you could do this for some of the common system types, strings and numbers, images, and you'd actually see that in line. Well, Xcode 5.1, which we have a developer preview out right now, so I encourage you to check it out, we're expanding the number of built-in types that we can support. So you can just see richer things. Like you can actually, for an NSURL, we'll actually quick look the result of the URL, right? We'll resolve it and actually show that to you. So it's really handy. But that's not the very cool part. The really cool part is something that you had asked for, which is the ability to quick look your custom objects, right? So let me show you what I mean. So here's a view controller that I have, right? So view controller in and of itself doesn't really seem very quick lookable. So ordinarily in an Xcode 5, this is what you'd get, just kind of the hex value of it. That's not very interesting. But now in Xcode 5.1, when you click that little eye there, you can actually -- you can provide a custom thing that we will quick look, right? So you can provide a visual meaning to arbitrary objects in your app. Now, here I'm just using a very simple example.

And if I show you the code for how this works. The way it works is you would implement a method on an object that you want to be quick lookable and that the Xcode debugger, excuse me, not GDB, the Xcode debugger LLDB will call into this method when we need to show a quick look for it. So in the example that I showed of that red look at me, all I'm building here is an NS attributed string, right? So I'm setting a foreground color to red, the font size to 48 so you can and I'll read it, and I return that string, and that's what's shown.

But you could return different types, and I think one really interesting thing here is, like, you could return image, right? You could compose and composite an image on the fly that gives you a lot of meaningful information about it. So this is just something new. Look for it in Xcode 5.1. You can try it out now, implementing this method, and one of the reasons that we put out developer previews is actually to get feedback from you. So, you know, try this out, And if you have any trouble with this, file bug reports. So that's debug quick looks. How many of you did not know about that? Don't lie. Okay. All right.

Fair enough. Okay. The next thing is renaming IB objects. And this one is all about interface builder inside of Xcode. And if you have an interface that has any level of complexity to it, you probably have an outline view that maybe looks something like this. And well, that's handy. It expresses hierarchy, but it doesn't really express meaning. You look at this and, well, there's a view that has two more views inside of it. Who knows what they are? It has a couple buttons. Who knows what they do? There's an image view.

You look at the outline view and it just doesn't mean anything to you. So this tip is all about adding meaning to this. And the way it works is, well, this outline view behaves like the outline view in the finder where if you select one of these things and then you pause for a second and you click it again, that actually starts editing. And now you can change the name that it's showing in the hierarchy. So you could actually change that. This is our adventure sample project. I changed that button to the Archer button. It's the button that you click when you want an Archer. Now you can go through this outline view and kind of rename things to make them human readable. And the important point here is that these names really are just for presentation purposes. This doesn't change the way your zip files get compiled or the ultimate output. This is really just to make it easier for you to find things in your interface. So that's renaming IBObjects. And I think it's really handy, especially if you work on a team where maybe you're really intimately familiar with a view hierarchy, but the new guy that just started, he has no idea how this thing was put together. So it's useful for making your code and your interface more easily understandable.

Now, the last one actually isn't inside of Xcode, but it has to do with source control. It's actually a Git command. And if you haven't used Git, it's a really great source control management system that I encourage you to look at. It's very, very powerful. And what I'm talking about here is just my favorite command in Git, which has to do with searching a revision history.

So the scenario for this is you've got your app. You released 1.0. You did really well. you released 1.1 and so on, and you're going on, and you're chugging along, making great progress and implementing things, and you get up to some point, and you release a new test build of that latest version, and you get a bug report, and somebody says, oh, hey, something just broke.

It's like, all right, I can deal with that. The problem is, what they just report was not the thing that you've been working on, right? It's something that you haven't touched forever since the last major version of your app, so what the heck broke? Well, now your challenge becomes finding where you introduce this regression. And one approach to doing that is just simply, you know, the brute force linear search. Like, okay, well, maybe it was my last check-in.

Nope, maybe it was the one before that. Nope, and on and on. And ultimately you'd get back to, way back to your 2.0 version, which, you know, I'm showing eight convenient little bullets because they fit on a slide, but in reality it's probably more like 800 commits, right? Especially if you have a team. So there's a better way of doing this than using Git. The idea would be, well, This thing that broke, this arbitrary thing that broke, well, jump back to a major revision. Go back to your 2.0 version. Was it broken there? And I'll say no. So that's the green check. The bug doesn't exist in 2.0. So now Git can help you find in between where that broke.

And you do that by using the git bisect command. And what git bisect does is it performs or it assists, it facilitates in doing a binary search through your revision history. And we all know how great binary searches are. They cut times down in orders of magnitude. So this is the type of thing that you probably don't need very often, but when you do, it will save you a ton of time and frustration. So it works something like this. So here's our scenario. So we have the bug on the right-hand side. We know we tested 2.0. It doesn't exist there. We now start a git bisect session. This is a session-based operation.

So you say git bisect start, and we After you start the session, you need to tell Git a few pieces of information, kind of give it some context. And the first thing is you tell it where the bug exists, right? So you say git bisect bad. And by default, it's going to use the head revision, the most recent revision, the one on the right.

And that's going to mark that commit as bad. And then the next thing that you're going to do is, say, give it the other end of the spectrum. You say git bisect good. And here you provide a tag that you have in your history or a commit revision, a commit hash. Here I'll say 2.0. That's just the tag that we marked that one. And now Git will say, OK, that's the good and the bad. So now we have our extremes of the bounds that we want to check. And the next thing Git will do is check out the midpoint. So it'll calculate what was the midpoint revision of that. And it'll move the head to that. And now you've got that checked out. And then you run your tests. You build your app and run your tests again. And that's the process that you're going to go through kind of time and time again is telling Git for this midpoint revision what was the result.

So here I'll say, okay, at that midpoint the bug still existed, so now it's bad. And now Git will say, okay, everything after this is also bad. So now we've cut down our range to a half, right? And this is where you visualize the binary searching. And then you run your tests again on that new midpoint revision, and then you give Git the answer of what the results were. Okay, here it was good. So now it takes all the commits on the right and then checks out the new midpoint revision. So you're just kind of honing in on this, and pretty quickly you get to the point where, okay, it looks like that bug was introduced at this revision. So that's really the power of Git bisect, and it gets you down to finding the source of a bug really quickly. So one of my favorite commands, but it is second to the next command that you obviously would do, which is git blame, and then you next activity is to go yell at the person that introduced the bug. So that's git bisect, and that's the set of kind of editing and source control tips that I have for Xcode. So next up is the iOS simulator. And the first one that I want to talk about here is the a double high status bar. And if you're not sure what I mean by that, it's this. It's in an iOS app, I'm showing an example of being in a call where the status bar actually grows in height to twice its size. And what you're seeing is this is good, right? This is a well-behaved app. The status bar grows, the content shrinks down to accommodate it, right?

You look at and think, great, that's terrific. That, a lot of the time, is not what we see, and that's what I want to change. So what we see a lot of the time is actually this, where the status bar just changes height, but the content doesn't change. And we can do better than this. This is a scenario where when you do it right, no one notices. It's like, that's the thankless engineering that we do. When you do it wrong, everybody notices, and it stands out like a sore thumb. So there's a really easy way to find out whether you're doing it right or wrong, and that's using the simulator. So in the simulator, you can just run your app and then under the hardware menu, toggle, choose this toggle in call status bar. And all that does is, you know, toggles it from single height to double height. And that's kind of that movie that I showed the first time was just me kind of hitting the command shortcut for toggling this. And all you have to do is just verify that you update correctly, right? Just see, look at the screen. Does it look horrible? If it does, then you're doing it wrong. And you wanna test in both portrait and landscape. The height only changes in portrait, but when you add code to handle this, which I'll show you in a second, you just wanna make sure that you do the right thing in both orientations.

So how you determine this is happening in code is there's a couple ways. You can pick up a notification. There's a will change status bar frame did change status bar frame notification. So you can pick that up in view controllers, and then you just adjust your layout and accommodate that extra height.

The app delegate is also told directly when this happens. So you can pick it up from there as well. There's application will and did change status bar frame. So just an example where you can use the simulator to really identify a problem and then apply some polish to it. So that's the double high status bar.

The next one is memory warnings. So as Dave talked about, you know, here's kind of a how we represent memory on a device, and we've got a lot of things that are competing for memory. And over time, we end up getting into situations where we have what we call memory pressure, right? We're running out of memory and we need to relieve this pressure. And we'll do so by iOS, as you hopefully are aware.

We'll call into view controllers and basically give you a memory notification warning, and that's our, you know, the first time around, it's a very polite request. Hey, we're kind of low on memory. Can you help us out? And what we want you to do there is, you know, throw away your static caches, unload any images or textures that you have that can be reloaded from disk very quickly, right? Things that are read-only, not edited in memory. Like, get rid of that stuff and reduce the memory pressure. So we do that very politely the first time, And then after we go through that process, if we still are under memory pressure, we're gonna come around a second time and we're gonna be a lot less polite and we're gonna start killing apps. And that's really what you wanna avoid. You don't wanna be in the crosshairs of iOS looking for memory.

So the best way to do that is just verify that you're reducing memory when we ask you to. And you can test this very easily again in the simulator where under the hardware menu, there's simulate memory warning. And all that will do is just invoke the memory warning notifications on your objects. And what you want to do in response to that is two things. One, reduce your memory. That's what it's for. You can use the Instruments tool for monitoring your memory usage. You want to see the graph go down. That's the right direction. And then two, and this is maybe more important, is you want to make sure that your app continues to behave correctly. You don't want to trade saving memory for a crash. That would be a bad tradeoff. So this just lets you test these, have these APIs get called, and then you can validate that you're doing the right thing.

So that's memory warnings. And then the last simulator topic that I want to talk about is color-blended layers. And what color-blended layers is, it enables a way to visualize views in your app that are using transparency. And when you enable this, you can do so in the debug menu in the simulator. You just choose color blended layers. And when you choose that, here's an example. This is just a very stock master detail example project in Xcode. So I just made a new project, ran it, added one row in the table view. And when I turn that on, you'll see these green and red overlays in your app. And what this is representing is green views are fully opaque, and red views have some amount of transparency to them. So another way of thinking about this is green is good, red is bad. And the reason red is bad is because when a view has transparency, we have to actually render and composite the thing behind it. And that takes extra cycles.

And the place that this really can impact apps, and we see it very regularly, is in scrolling lists. So as new cells are coming on screen, the more compositing of views that we have to do, the longer it takes. And so you may end up dropping frames. So what you want to focus on is getting rid of the red. And this is, as I said, shows where views are using that. It's a common performance problem.

And if I peel back this red and green overlay and actually lay them side by side, we can see in this example, and the reason I picked this one, is that that table cell, which is red, is sitting on a solid white background. So there's really nothing interesting behind it. It's using transparency but not in a meaningful way. So we're taking a performance penalty for no good reason. So this is a really common case that we see. And it turns out it's really easy to fix, too.

If we go to see why this is here, we go to Xcode, and in this example, and you can absolutely do this on your own, select the label that's in that table cell, and then look at the attributes. And what we see are there's a background which is just set to the default color, which by default is clear, meaning it's going to have transparency. And also the view is not marked as opaque. So the fix in this case, and in a lot of cases that I see, is really just as simple as changing those two values to give the cell a solid background color and tell iOS that it's an opaque view. And if you do that, in this case, you go from running this, you make those changes, you rerun, and now the view looks like this. And so you want to see things turn green over time. Now this is a really common case, these non-opaque views. Another very common case is images that have alpha in them. And the thing that gets people is you look at your UI image view, and it's marked as opaque. And you're like, that's going to be a green view. I know it. And then you run your app, and you turn this on, and it's bright red. And you wonder, why the heck is that happening?

Well, if you go look at the artwork file, the JPEG or the PNG, if the image file has alpha in it, if it has an alpha channel, we will respect that. And even if there's no meaningful data in it, and it's not being used in a meaningful way, we will enable transparency in the view. So that's a really easy fix, where you just go back to your artwork person and say, I need this image as an asset with no alpha channel. And then you can magically have these red images turn green. And then the last place is if you're doing custom drawing directly, if you use the clear color, that's going to enable alpha in your views. So most of these are really low-hanging fruit. and the challenge is identifying where the problem is, and that's what the simulator is good for, and you can turn on color blended layers. I'll also add, you can do this on a device as well. For the color blended layers, you generally get the same results on both, but sometimes you wanna do it on a device where you can actually see the performance as well simultaneously. So you enable that in instruments in the core animation template in instruments, there's a set of debug options there, which overlap with the ones in the simulator. The side benefit of doing it in the simulator is you can ask a coworker, "Hey, can I borrow your device?" You plug it into your computer, you go turn on color blended layers, you disconnect it, the red and green stays there. It's a persistent switch. Then you give it back to them and then you know nothing about it, but hey, your device looks like it's broken. Okay, so that's color blended layers. I'm not condoning that whatsoever. And those are the simulator tips that I have. Just a couple things for identifying problems in your app and adding some polish to them. So next up on the topic of performance, I want to switch over to instruments. And the first thing I want to talk about there is profiling background fetch. And this is actually kind of a two for one tip where I talked about background fetch a couple sessions ago. And one of the important aspects of background fetch is that it's somewhat non-deterministic. It's driven by iOS based on a couple different things, and things that you can't, as an app developer, necessarily directly control. So that raises the question of, how do you actually test that? How do you invoke background fetch? Well, it turns out there's two ways you can do it.

One is in Xcode, and then the second one, which I think is the more interesting case, is to use the time profile trace in instruments, and under the instrument menu, there's simulate background fetch down at the bottom. And the reason I think that's more interesting is, well, one, it triggers a background fetch when you're running your app, so that's really helpful. You can test it. But it triggers it in the context of time profiling. So you can actually see the work that is going on while you're fetching. And because you have a small amount of time, you wanna optimize that work that you're doing and make sure that you're only doing the necessary work for doing a background fetch.

Now, when you do that, you'll see something like this. And if you've worked with instruments, you might look at that and say, well, OK, I can see there's the spike of activity, then kind of a long, flat thing. And then there's a whole bunch of activity at the tail end. But it's a little hard to see, like, well, where was the background fetch? So in instruments, you can tell that by using the time bar, which I've highlighted here. And as an app transitions states, as it goes from running in the foreground to being in the background to being suspended, Instruments is dropping flags. You see these flags appear in the time bar there. And you can use those to really understand like, okay, what state was the app in over a particular range? So here, if you click on one of those flags, you get this little pop-up that will tell you Tech Talks is the name of this app, not to be confused. So in the Tech Talks app that I was profiling, at the very beginning, it was foreground and the running, because it was running in the foreground. That's when the app launched.

And now if I click the right arrow there to go to the next flag, now I see it was running in the background, and that was when I hit the home button. So now the app has transitioned into the background, but it's still running. And now if I go to the next one, which happens to be just milliseconds after that, we see it's background tasks suspended. So now the app is suspended in the background.

And then it's at this point that I went and selected the menu item for simulate background fetch. And the next flag that I see is now the app has been woken up in the background, but it hasn't come to the foreground. So I'm running in the background. And between here and the next flag, which if I click next again, now the app is suspended once again. So we transitioned into running, but always in the background. And between those two flags is where the background fetch is being performed. And that would be the area you want to zoom in on to really identify what work is being done and is it only the minimum amount of work that I need. So that's how you can profile background fetch, invoke it, and actually see what work is being done. And then the next step that you commonly would want to use is inspecting what it's doing. And so that brings me to call trees. And I want to show you how, well, first what a call tree is, and that's this area down here in the bottom right-hand corner of the time profile template.

And what it's showing you is over a period of time, instruments is stopping, you know, at very fast frequency, stopping your app and capturing the backtraces of all the threads. And then we unify them, we kind of lay them on top of each other, and we get this tree representation where you see main at the top and then kind of the call trees of what your app is doing over time.

So now this is a different example than the previous one that I was showing. And this is actually an example where I was working on the WWDC app, which is one thing that the evangelism team works on. And I'd been working on the new iPad version, where we have a new grid view that you can see the schedule for the conference. And in particular, I was working on this one method that I was concerned about the performance, right? In the back of my mind, I was like, hmm, this code might actually be slow. So I wanted to find out. And I used the time profiler to take this trace, And down here at the bottom, I can see this method that I was concerned about, this apply layout attributes.

And if I just looked at this initial view, I'd see, well, okay, it was taking about.7 seconds, or sorry,.7% of the time that I profiled. And you think, well, okay, that seems like a really small amount. But I had a hunch that this code was being called from multiple different places, actually more than a hunch I knew that it was. And what I wanted to know is, What is the total amount of time that this method is taking? And that's what you can do using call trees. And you can do it by using these view options on the left-hand side. Now, this is what you get by default. And I'm going to go through a series of checking and unchecking items in here and explain what's happening to the call tree along the way.

The first thing that I want to do is I'm going to uncheck this separate by thread checkbox because I want to know the total amount of time regardless of where it's being called from. So I don't care about what thread we're in. So uncheck separate by thread. And now you see the outline view. This isn't a particularly good example of it, but the outline view is no longer separating threads out at the top level, and everything is in a single branch of the tree. So the next thing I want to do is I want to invert the call tree. And this is where really the heavy lifting of this operation comes in. And what that will do is instead of showing the call tree organized by the caller of a method, it will turn the tree upside down and show the cumulative amount of time regardless of who called it. So if you think about, depending how you think about which direction a tree is, It just turns it around and shows you total cumulative time as opposed to single invocation of a method.

Now then you look at this and say, okay, well, what bubbled up to the top here is objc_message_send, right? That's the inner function that does the Objective-C messaging. And that's not really helpful because that's not my code. That's not what I'm looking for. So I kind of want to get rid of, like, all the system stuff here because I don't care about that. It's almost noise to me at this point in my hunt for finding how much time this method is taking. So next thing I'll check is this hide system libraries, which all that's going to do is remove all the system libraries out of the call tree. And it reduces the call tree down to just your code, just the code that exists in the application.

So now I get this code where I recognize all of this. It's meaningful to me. I can act on it. And if I actually now look at what bubbled up to the top, it's actually that method that I'm concerned about. So now I look at it. and we have apply layout attributes. And when I compare this total amount of cumulative time that method took versus the first single instance of it, I see it's by a factor of eight. So now I'm looking at 6% of the time spent in my app. And that's a sizable number. That's a number I can go and say, all right, what am I doing in that method and how can I make it faster? So call trees are just a great way to get a different view of your data.

And another really interesting way of getting a different view of your data is using what's called CPU strategy views. And if you're not familiar with what a strategy view is or how you select one, you do it using this little widget in the top left corner of the time profile trace document. There's three segments, and by default you see the middle one, which just shows you total CPU time.

And that's generally really helpful and very meaningful. you can understand that's what your app is doing on the CPU over time. But if you click on the left button there, that shows you the CPU trace. Actually, I'm sorry, I need to go back just for a second. If we look at this example, you know, I look at this time trace, and I see there is this region of, you know, a whole bunch of activity, and then the app kind of, you know, it quiesced and went relatively idle and then a little blip at the end. But you look at this and you think, well, okay, that looks reasonable, right? We were busy for a little while. And the scenario that this one is, is again in the WWDC app. And what the app is doing here is that period of activity is we've downloaded a JSON file which contains the session data, so all the sessions. And we're updating our database. So we're iterating through this list of a couple hundred sessions, including labs, and updating the database. And you look at that and think, OK, we were busy for a bunch of time. And then we got quiet. Looks good. What's the problem? Well, so if you go choose the CPU strategy view, that leftmost thing, you'll get a different view of that same data. And the view you get here is actually each individual core of the CPU. So you can see what the hardware is doing under the covers. But again, you look at this and think, well, okay, same period of activity, each core looks pretty busy and we're quiet. So what is the problem?

Well, in order to see the problem and get this different view, you need to zoom in on the CPU strategy view. And you zoom using that little control in the bottom left-hand corner, and that will stretch out the time scale. And when you do that, now you look at the CPU cores, and this is where the different story is. Now you look at this, and at any given point in time, only one core is doing something.

So we're sitting here idle on a core for half the time. And that goes back to what we've been talking about earlier today of making the most of the hardware. So this is not utilizing the hardware to its fullest extent. So again, a lot of these tips really are focused around helping you identify a problem. But in this one, actually, I want to talk about an interesting piece of API that will let you solve that problem or be a part of a solution to that problem. So what we want to try to do is get both cores going at the same time. That's where we'll get good hardware utilization. So in order to do that, as I said, this was an iteration over an array of session objects. And you normally would do that as like a for loop with an array, you know, for object in my array. There's this other piece of API which is really interesting. And this was another one that I didn't know about. Actually, Dave DeLong, who's been speaking here earlier, he told me about this. Credit goes to him. This API on NSArray enumerate objects with options using block. What that will do is take that block and iterate over the objects in the array, invoking the block once for each object in the array. And here, I'm not passing any options, which is going to give the default behavior just a linear enumeration over that array. The cool option to pass here is called NSEnumerationConcurrent. And what that will do is it will take that block and it will enumerate over the objects in the array concurrently using Grand Central Dispatch. So we can actually scale this to the capabilities of the hardware really well. And in this case, I can use this safely because I know that each of these objects in the array is self-contained and I can do atomic updates to our database safely. So this is applicable in this scenario because I know I can do concurrent operations with these objects in the array. That is not always the case, right? So you need to be aware of where you might use this and if you can concurrently operate on these objects. But in this case, I switch to using this instead of just a simple for loop. And now if I go back and I rerun this time profile and I zoom into this CPU strategy view, I see it go from this, where we see these gaps of time, to this. And this is what hardware utilization looks like, right? This is using hardware to its fullest extent because both cores are busy for the vast majority of time. And when I measured this, the operations iterating over this array actually takes a measurable number of seconds. This cut the time nearly in half, right? Not fully in half because there's some additional stuff that's going on, but huge time savings. So if you have these serialized operations that you can do concurrently, you can find out where you're not doing using the hardware to its fullest extent and then think about using an API like this to get to that point. So just a couple performance tips and instruments. Really use these to identify performance problems, and a lot of the time that is 90% of the battle, just finding where the problem is, and then you can figure out how you're going to fix it. So that's instruments, and that brings me to the last section, which is just a couple things about, a couple pieces of API that you may not know about, and a final tip. The first one is URL utilities, And chances are you know about this already, right? Foundation NSURL has a bunch of utilities for giving a URL, you know, what's the scheme, what's the host. You can get the path. You can get the query parameters at the end. So just some really easy ways to pull that information out. We've got stuff for username, password, fragment, things like that. So you can use these on URLs. Also, since paths are so similar to URLs, you know, we have very similar things in NSPath utilities as well. These are just really categories on these objects for getting these pieces of information. You can break up a URL or path into its components very easily, ask for the last path component, the file extension, stuff like that. And the main reason why I point this part of it out is use these convenience methods.

Like if you or one of your coworkers has the impulse to, oh, I'm going to write an NSURL parsing algorithm. It's like that's not interesting anymore. Your users don't care about that. And more importantly, they probably want consistent behavior with Mobile Safari or other apps that are parsing URLs or dealing with things. So the day of interesting parsing of URLs is kind of gone. Just use the built-in systems and you'll be good. Now, there's a new class in iOS 7.

Those utilities existed for quite a while in iOS. But there's a new class in iOS 7 called NSURLComponents, which you can use for a couple things. It's really useful for constructing URLs programmatically. So if you have code that's doing, you know, HTTP colon as a string, string by appending string, slash, slash, string by appending string, host name, whatever, you can do away with that by doing something like this, where you create an NSURL components object, you set some very well-defined, clear properties, and then once you've done that, you just ask the components, can I have a URL for that, please? And you'll get back a spec-compliant URL with percent completion or percent escaping and everything. So it's useful for constructing URLs from scratch. It's also useful for modifying URLs. So you might have -- here's a URL where let's say I have my password in there and it's kind of a very easy to break password there. So I have some code that's going to update my password. I can create an NSURL components object from that string and then you can just selectively modify one property of the URL. So here I'm going to update my password with my super secret password that includes the pig face emoji.

Because it's very important to use emoji and passwords. Also it's really -- that's really actually very seriously -- that's a great way to test your server. Like, does your server handle emoji and passwords? I'm willing to bet no. Because most people don't put emoji and passwords. Although I started doing that. Anyway, I digress. So you make that change, and then you ask the components, can I have a URL now? And internally, all it's doing is breaking up the components and putting it back together, but you don't have to write all that code. And you can see you get the emoji, which is this percent escaped string. So those are URL utilities. Just makes your code a little bit cleaner, removes a little burden for you. The next thing is formatting byte counts. And what I'm talking about here is let's say you're doing some kind of a progress UI where you have an indication to a user a number of bytes. And most of our users, they aren't real savvy with bytes. That's not a concept that's really familiar to them. But nonetheless, you want to present it in as easily understandable form as possible. So here, when you're at the beginning of the transfer, it's saying 865 kilobytes. And then as it progresses, you want that to change into megabytes. These are kind of as user friendly as we can get. So when you want to build a UI like that, we actually have an object that will help format those bytes correctly. And again, along the lines of getting rid of code that's not interesting anymore, the debate over whether it's divide by 1,000 or 1024, let's not have this debate anymore. We'll pick one, and that'll be the right one. So when you want to do this, you can create-- we have this NSByteCountFormatter object, and it'll just format these byte counts.

It'll do it sensitive to the user's locale. So here in the UK, this would end up coming out with a comma instead of a period, and it'll do that automatically. You don't have to worry about that. You can control the units that are used. So you can say, I want to lock this into kilobytes only, because that, for whatever reason, is meaningful. Or it can switch to be that kind of flexible, user-friendly thing. So the API is super simple. You just create an NSByteCount formatter. If you've used any of our other formatter objects, these are really familiar. You create the object. You give it the value that you want. And you just get that string back. And then you put that in your UI. So that's formatting byte counts. And that brings me to the last topic, which is localization. And this is one that's really important to us, right? We think the best apps are ones that are localized. And localizing your app really just expands your user base. So who doesn't want to expand their user base? And if you want to do it, localize your app. Similarly, make your app accessible, similar thing. Now, if you've tried to do localization, you might be familiar with a scenario like this that I show and you might have a little bit of frustration because changing the language on a device or even in the simulator is a heavy operation, right? You go through here, you pick a different language and then the home screen restarts. And along with that, it kills all the apps and they all have to relaunch. So if you're just trying to like fix a bug, somebody reported, hey, you know, in Russian, this string is clipped. It's like, this is a heavyweight operation. So there's an easier way to do that, and that's what I want to show you.

Here's an example of an app that's been localized. And if you just want to switch from one localization to another, it can be as simple as just having some different schemes in your project. And then you can easily switch from one scheme to another, which will switch you from one language to another.

So from run to run of your app, you can jump between languages, and it makes it a really lightweight process. So let me show you how you configure these. You do so in Xcode Scheme Editor, and you would probably pick just your default scheme that you have, and then you would duplicate it. So down here in the bottom left of this panel, you hit Duplicate Scheme. That'll give you a new scheme with all the same configurations. But then you would come in, and you would add some arguments that are passed on launch. And there's two arguments that you would want to set. The first is this Apple Languages, which is an array, but you would specify just a single language code, and that's the language that the app will be run in. And then the second one is Apple Locale, and that actually, in the UI of settings, that's actually going to represent the region format. So affecting things like number formatting, date formatting, stuff like that. These are separate toggles that you have. So when you set these, now you can just go through and duplicate the scheme numerous times and set one for French, or you can add Chinese or Japanese and Russian, kind of whatever languages that you're currently targeting. So it just makes it really, really easy to get this pop-up that you can jump back and forth. So ease the development process for doing localized apps. So that's the last tip that I had for the frameworks, and actually is the last tip that I have for this session. So hopefully, did everybody learn at least one thing in the session? Yes, excellent. So I think my job here is done. And actually, our day is done. So I just want to say thank you so much for spending the day here with us. Take care.