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
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
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.
Paul Marcos So 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 or 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.
[ Transcript missing ]
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. Paul Marcos 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. Paul Marcos 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. Paul Marcos It's like, all right, I can deal with that.
Paul Marcos 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 like forever since the last major version of your app. So what the heck broke? Paul Marcos 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. Paul Marcos And ultimately, you'd get way back to your 2.0 version, which, you know, I'm showing eight convenient little bullets. Paul Marcos 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 that's using Git.
Paul Marcos The idea would be, well, this thing that broke, this arbitrary thing that broke, well, you know, 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. Paul Marcos 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. We have the bug on the right-hand side. We know we tested 2.0. It doesn't exist there.
[ Transcript missing ]
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 the view that's not marked as opaque. 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, right, 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 a lot of data in there, we're going to 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, you know, 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.
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, you know, sometimes you want to 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. 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, you know, a couple things for identifying problems in your app and adding. And then you can add 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 nondeterministic. It's driven by iOS based on a couple different things and things that you can't, as an app developer, necessarily drive. So you can't directly control.
So that raises the question of how do you actually, like, 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 want to optimize that work that you're doing and make sure that you're only doing the necessary work for doing a background fetch.
[ Transcript missing ]
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 go click next again, now the app is suspended once again. So we transitioned into running, but always in the background.
Paul Marcos 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. Paul Marcos 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.
Paul Marcos And so that brings me to call tree. Paul Marcos And so that brings me to call tree. 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, very fast frequency, stopping your app and capturing the backtraces of all the threads. Paul Marcos 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 tree. 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 0.7 seconds, or sorry, 0.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 kind 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.
Paul Marcos 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.
Paul Marcos So if you think about, depending how you think about which direction a tree is, it just turns it around and shows you the number of times it's been called. So if you think about how you think about which direction a tree is, it just turns it around and shows you the number of times it's been called. So if you think about how you think about which direction a tree is, it just turns it around and shows you the number of times it's been called.
So if you think about how you think about which direction a tree is, it just turns it around and shows you the number of times it's been called. Now then you look at this and say, OK, well, what bubbled up to the top here is Objective-C message send. 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. Paul Marcos So I kind of want to get rid of 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.
Paul Marcos 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, right? Just the code that exists in the application. Paul Marcos So now I get this.
Paul Marcos 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. Paul Marcos 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, right? Paul Marcos 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. Paul Marcos All right, what am I doing in that method, and how can I make it faster? Paul Marcos So call trees are just a great way to get a different view of your data.
[ Transcript missing ]
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. Paul Marcos 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.
Paul Marcos 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. Paul Marcos The first is this Apple Languages. Paul Marcos 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.
Paul Marcos 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. Paul Marcos These are separate toggles that you have. So when you set these, now you can just go through and duplicate the scheme numerous times. Paul Marcos And set it up in a way that it's not going to be a problem. So you can do that.
Paul Marcos And set one for French, or you can add Chinese, or Japanese, and Russian, whatever languages that you're currently targeting. Paul Marcos 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. Paul Marcos So that's the last tip that I had for the frameworks, and actually is the last tip that I have for this session.
Paul Marcos So hopefully, you'll be able to use it in your own apps. Paul Marcos Hopefully, did everybody learn at least one thing in the session? Yes? Excellent. So I think my job here is done. Paul Marcos And actually, our day is done. So I just want to say thank you so much for spending the day here with us. Paul Marcos Take care.