This workshop will be retired on May 31, 2020.
Debugging in Xcode: Part 216:03 with Gabe Nadel
Now that we are getting comfortable with what Xcode can do, let's try to hunt down some trickier bugs! We'll even take crack at debugging our User Interface.
In our last video, we got ourselves oriented with Xcode's tools, 0:00 step through some code and even tracked down a bug. 0:04 Let's hop back into that project and try our hand at hunting down exceptions and 0:06 debugging the user interface. 0:10 >> Here we are again inside our TableSearch app. 0:13 As I mentioned, it's a free code sample from the Apple developer site 0:16 which I've changed just a hair. 0:19 You can download both the original and my tweaked code below. 0:21 If you wanna follow along, be sure to use my version. 0:25 When we left off, our app seemed to be running fine. 0:28 We fixed our missing price error, at least for the moment and everything seemed okay. 0:31 Well, there was a feature that we didn't test yet. 0:35 You see the marketing guys thought it would be fun to have a feature 0:38 where users could select a device to buy, shake their phone, and 0:41 if there were any applicable coupons they would be displayed. 0:45 Let's give that feature a test drive, okay? 0:49 We can run our app. 0:52 We can pick iPod. 0:56 And now we gotta shake the simulator. 0:59 Now it sounds like something we have to do on a real device. 1:03 Maybe this will work. 1:06 No that doesn't work. 1:08 What about picking up the whole monitor and giving it a good shake? 1:10 Nope, I'm not gonna try that and neither should you. 1:12 We can simply go up here and select Shake Gesture. 1:15 And when we do, we see some ugly looking stuff everywhere. 1:20 In our console, it looks like we have a fatal error, that can't be good. 1:25 And we see that it's Array Index out of range. 1:30 That might be useful. 1:34 We also see this line of code here highlighted and 1:36 it has an exception code, you can tell that by the EXC prefix here. 1:39 Now, I'm not gonna go in depth about errors versus exceptions. 1:45 But in simple terms, an exception is the kind of issue you don't expect and 1:48 could cause your app to crash. 1:52 An error on the other hand, you may check for specifically and may either inform 1:54 the user that something happened or handle it gracefully some other way. 1:59 For instance, if internet connectivity drops in your news app, you might 2:03 tell your user what happened and that they should check their settings and reload. 2:08 An exception would be more like having your news app crash 2:12 because it received its data in an unexpected format and now has lots of nil 2:15 values where it expected critical information it needed to proceed. 2:20 If you'd like to read more on this topic as it relates to Objective C, 2:24 I've linked an article below. 2:27 Before we go into fixing this, 2:29 I just want to point out a few other debugging features. 2:31 The first is over here in the debug navigator, we've got the call stack. 2:34 Now a call stack is a record of all the sub routines, aka the functions and 2:41 methods, which are active at the time. 2:45 You'll see here, we have the method we're in right now. 2:47 That's check all products for coupons. 2:50 And then we also have this method here, motion ended and 2:53 that's what detected the shake and then called the method that we're now inside. 2:58 Now these two methods happen to be located right here on my screen next to each 3:04 other, but that's really just a coincidence. 3:08 Very often your call stack will show that your code is hopping all over the place 3:11 from one file or class to another as they make use of each other's methods. 3:14 And in those cases, especially, 3:18 it's very useful to have it all listed out in your call stack. 3:20 Now in our case, the exception brought us right here to where we probably 3:25 need to be to fix the issue, which is admittedly a simple one. 3:29 In real life that may not be the case, an exception will be thrown, but 3:34 you won't have much to go on. 3:38 When that happens, it's always a good idea to add an exception breakpoint and 3:40 you can do that down here in the debug navigator, Add Exception Breakpoint. 3:44 In fact, Exception Breakpoints are so good to have, 3:50 it's probably a good idea to just create one in your project at the very beginning, 3:53 and leave it there just in case. 3:57 That way it'll catch all your exceptions for you. 3:59 Similarly, you can create Symbolic Breakpoints, 4:02 which can trigger whenever your code executes a particular method. 4:05 The last point to mention here is that our call stack is highlighted and 4:09 marked to be more readable. 4:14 The calls with the little blank heads, they point to code that we the developers 4:16 wrote and that's generally where you'll find your root cause. 4:21 The mug icon points to something in app kit or UI kit. 4:25 The gear icon is gonna indicate system code. 4:30 But again, you can most often ignore those and 4:34 spend your time looking into developer written code. 4:36 If you'd like a more thorough explanation of this display, 4:39 I've linked below to that section of the docs. 4:42 Okay, now that we have some context for 4:45 what we're looking at, let's see if we can figure out what's wrong. 4:47 Now just from looking at the code on the screen here and 4:50 our console output down here, can you tell what's wrong? 4:54 Pause the video and take a minute to figure it out. 4:59 Well, we see our console says Array Index out of range. 5:02 This probably means we're trying to access an element in the array that isn't there. 5:07 Do we see any suspects? 5:12 Well, right here we're using a for 5:14 loop to iterate through an array, but usually that should be fine. 5:17 Let's go in for a closer look. 5:21 It says, set a variable i equal to the count of products, 5:23 that's 9, then decrement with each iteration 5:29 until i is greater than or equal to 0. 5:35 Inside the loop, we create a temporary product constant and 5:39 set it equal to the element at position i. 5:44 So for our first iteration, we'd be looking for the element at index 9. 5:47 See the problem? 5:52 Remember, indices and arrays start at 0. 5:54 So when you wanna access the last element in an array, 5:57 you don't want the value of the array count but rather the count minus 1. 6:00 Let's see if that's the problem. 6:05 We can add in a minus 1 here. 6:07 And that will set our initial value for i to 8 instead of 9. 6:11 Let's stop our code and try it again. 6:17 Okay, we can pick a product and we can shake our device. 6:28 Hey, there we go. 6:34 We got a coupon. 6:35 Let's close that for now. 6:37 Before we move any further, does anyone have a funny feeling in their stomach? 6:40 Yeah, our solution was pretty hacky. 6:44 In fact, the code we stumbled on for this for loop was pretty shoddy to begin with. 6:46 What's a better way to do this? 6:51 Well, we definitely want to get rid of this -1 here and 6:53 we could do that pretty easily just by changing our for loop to start at 0. 6:57 And count up until i is less than but 7:06 not equal to products.count, so it would stop at 8. 7:11 We'd of course have to change that to ++ so we're incrementing up rather than down. 7:18 Now, is there an even better way to do this? 7:24 Well, yeah, there is. 7:26 A for in loop is perfect for the job 7:30 as it's designed specifically to iterate through all elements in a collection but 7:33 won't go out of bounds like our initial loop did. 7:37 To implement that, we can simply say for product in the array Products. 7:40 So that's for each product in Products, we want to iterate through. 7:47 And we can even get rid of this line, 7:52 because we've already declared our product here. 7:54 Now I think that works, but just to be sure, let's give it another run. 7:59 Okay, we can open up iPod. 8:04 We'll shake our hardware, and we have our coupon and 8:07 let's make sure everything's hunky dory. 8:11 Come on, are you serious? 8:14 I was just about to head home for the day. 8:16 Well, perhaps this will be what they call a teachable moment. 8:18 So why isn't the button working? 8:22 First, let's make sure we remembered to hook it up to our IB action. 8:25 We can cruise over here, into our storyboard, and let's look for our button. 8:29 Okay, our Redeem button, right? 8:36 And the reason that's not showing up is because our CouponView here is 8:39 Hidden cuz we unhide it with our shake, we'll hide that again. 8:44 Let's look at our Redeem button. 8:49 Okay, it is hooked up to the action just like it should be. 8:50 Okay, so maybe our method just isn't doing what we expect. 8:57 So let's drop a breakpoint in there. 9:01 Action sheet button, that's the name of it. 9:04 So we'll go to DetailViewController. 9:06 Let's see what we have. 9:09 Okay, so this is the IB action that we're gonna call. 9:10 Let's plop in a breakpoint there. 9:13 Go back to our SIM. 9:15 Click again. 9:16 Nope. 9:19 Now we see when we tap our button, we aren't hitting our breakpoint at all. 9:21 Let's go back to the main storyboard, pause the video, 9:26 now take 30 seconds and see if you can figure out what the issue is. 9:30 Well, some of you may have noticed what's wrong already. 9:35 But let's take this opportunity to use the view debugger I mentioned earlier. 9:38 To do that, we need to make sure the app is running. 9:42 Which it is, we can see in our SIM and 9:46 we can also see up here that it's running because the stop button is active. 9:48 And we're gonna wanna make sure that we're on the screen we wanna debug. 9:51 So now that we're here on the screen, and I'll make that a little smaller. 9:58 We can use this button down here to debug our views. 10:04 This doesn't look at very impressive but how about now, and 10:12 how about when we explode the views so they're really separate? 10:16 Okay, does anyone see what the problem is here? 10:23 See, it looks like our Redeem button, that's right here, 10:27 is actually behind this gradient image. 10:31 So when we're trying to tap, we're tapping the transparent part of this image, and 10:35 that's catching our tap instead of the Redeem button. 10:40 Now you really can't tell as a user because the bottom of the gradient's 10:44 transparent. 10:47 But in this view, it's clear as day. 10:48 Well, there's a clear fix. 10:52 Actually, there's two clear fixes. 10:54 One, is that we could take our Redeem button and 10:57 simply move it in front of the gradient. 10:59 The other is that if you look at the gradient, 11:02 you'll notice that it's User Interaction Enabled. 11:06 If we turn that off then it won't grab the taps. 11:10 Now just to be clear, if you had an image and you lowered the alpha so that it was 11:13 totally transparent, the button, even if it was behind it would get clicked. 11:18 But this gradient is a PNG image that has a transparent portion baked in, 11:24 the image itself is being displayed at full opacity. 11:29 One other thing worth mentioning is that this view debugger we just used is, 11:33 well, buggy. 11:37 That's just a fact of working with Xcode. 11:38 When you have lots of views and one storyboard, it can often crash. 11:41 If you find that you really need this feature and Xcode isn't working well 11:45 enough for you, it may be worth investing in a software called Reveal. 11:48 Which essentially does the same thing, but more reliably. 11:52 Just in case that's useful to you, I've linked to their product page below, and 11:56 last I checked they had a 30 day free trial available. 12:00 Okay, we should check our work, so let's run it again. 12:04 We'll make this a little bit bigger. 12:10 We'll try a device, we'll give it a Shake and there we go, Coupon Discovered. 12:12 When we click Redeem, oop, we hit our breakpoint, that's fine, get rid of that. 12:19 And then we see we get a message, oops, 12:25 you better try redeeming from a real iPhone. 12:26 I guess it knows that we're on a SIM. 12:28 All right, we're almost done, but before we go, 12:31 did you notice anything else funky about this whole coupon code business? 12:35 Well, let's take a quick walk through what we're doing. 12:39 This is a great habit to form, by the way. 12:42 So when a user shakes the device, we're gonna check all products for coupons and 12:46 that's this right here. 12:51 So we take a for loop and we loop through all the products in the products array. 12:54 And then we're gonna check the current coupon for that product. 13:02 So with each iteration, we're gonna run through this method and 13:06 see if it satisfies any of these cases. 13:10 For example, iPad, but that's probably not what we really want. 13:13 After all, we've already selected iPad or iMac or whatever. 13:18 So we really should only be presenting a coupon, if there is one for that product. 13:24 How would we change this so it only displays the coupon if we're 13:30 viewing a product with a coupon like the iPad indicated here? 13:35 Well, instead of bothering to loop through the whole list of products, 13:40 why don't we just feed our switch the current product we're viewing. 13:44 That's actually pretty easy, we can just change a few lines. 13:49 We can comment out this call to this method that 13:52 contains the loop and we can replace it with this. 13:57 Doing that will call this method initially. 14:06 And it will bypass this method containing our loop. 14:10 Also, just to be clear, this product we're saying right here, 14:15 is actually the property product of the DetailViewController class. 14:21 It's just a coincidence that we happen to also declare a variable or 14:29 rather a constant called product right here inside this method, so 14:35 we could conveniently paste it in. 14:40 But this and this are actually referring to two different things, 14:42 as you might guess, as their text is highlighted in different colors. 14:47 Before we sign off, let's run our app again and test the change we just made. 14:51 We click on iPhone and Shake, no coupon. 14:57 If we go down to iPad, which was the only one listed in that switch statement, 15:02 and Shake, there we go. 15:08 Our coupon, fully tappable, and everything seems to be working. 15:10 Now in the real world, of course, I'd want you to test all nine of these options. 15:15 But for now, I think we're good to go. 15:19 >> We've covered a lot in this workshop so far. 15:22 And you should now feel comfortable using Xcode's basic debugging tools. 15:24 Though as I mentioned earlier, debugging is a big topic and 15:27 there are certain things we won't cover for fear of overwhelming you. 15:30 A few debugging related topics which we haven't yet gotten into our debugging for 15:33 performance, memory and multithreading issues. 15:38 I've provided some additional resources on those in the links below. 15:41 Over time, 15:44 the Treehouse library will grow to cover more advanced topics in debugging. 15:45 So I encourage you to be on the lookout for those as well. 15:49 All right, we're almost done. 15:52 Join me in the next video where we'll wrap up by talking through some of the most 15:53 common types of bugs. 15:57 And some useful strategies for staying productive, efficient, and 15:58 most importantly sane while debugging. 16:01
You need to sign up for Treehouse in order to download course files.Sign up