iOS 6 Application Development For Dummies (2013)

Part III. Getting Your Feet Wet - Basic Functionality

image

In this part . . .

After you have the foundation in place, you need to make all those controls do something, such as when a user taps a button.

In this part, you start the actual coding process to turn a good idea (an app that you use when you take a road trip) into a real working app.

Because your app will be using the Internet — a lot! — you find out how to determine whether the Internet is available and how to alert the user when it is not.

You also discover how to customize the appearance of the controls provided by the framework to make your application a thing of beauty.

Next, you find out how to connect all those views and controls that you’ve added in Interface Builder to your code to make them do something — such as have a ’59 pink Cadillac Eldorado Biarritz convertible drive up and down the screen.

Finally, you add animation and sound to the opening screen of your app so that when the user taps the Test Drive button, the ’59 Cadillac Eldorado convertible takes off with a screech and then drives up and down the screen with a satisfying engine purr.

You also begin to see how to account for the differences between an iPad and an iPhone, and make sure that the app can run flawlessly on whatever device the user has handy.

Chapter 8. It’s (Finally) Time to Code

In This Chapter

arrow Determining Internet availability

arrow Changing the appearance of controls

arrow Using the debugger

arrow Using breakpoints to examine runtime behavior

Yes, it’s finally time to start coding, although this chapter doesn’t get you going on the RoadTrip app functionality itself yet (the example app developed in this book). In this chapter, I show you some code you have to include to make sure that your app isn’t rejected out of hand by Apple.

Next, you’ll spruce up the Main view by customizing how controls appear, and finally, I give you an introduction to your new friend, the debugger. While some of you out there (but not me) may code perfectly, most developers make some mistakes as they develop an application. Fortunately, the debugger in Xcode starts helping you right from the start — so you want to understand how to use it as soon as you start coding.

Checking for Network Availability

One of the easiest ways to get your app rejected by Apple is to fail to make sure that you have an Internet connection when your app needs it, and therefore fail to notify the user that the functionality that requires the connection will be unavailable (or even worse, have your app just hang there).

Downloading the Reachability sample

Apple provides a sample application called Reachability that shows how to determine whether you have an Internet connection (as well as quite a bit of additional network information I won’t be going into), and you’ll be using that code in the RoadTrip app developed in this book. Here’s how to use code from that valuable sample program:

1. Download the Reachability sample from Apple by clicking Sample Code at http://developer.apple.com/devcenter/ios.

2. Type Reachability in the Search field.

3. Click the Reachability project in the search results, and in the iOS Developer Library window that appears, click the Download Sample Code button.

4. In Safari downloads, double-click the Reachability folder to open it.

5. Open the disclosure triangle next to the Classes folder (or open the folder) and drag the Reachability.m and Reachability.h files into your project.

(I put them in my Supporting Files group just to keep them out of the way.)

6. Select the check box in front of Road Trip in the Add to Targets section.

Be sure to select the Copy Items into Destination Group’s Folder option (if it isn’t already selected).

In order for you to be able to use this code, you need to add the SystemConfiguration framework. To do so, follow these steps:

1. In the Project navigator, select the project icon (in this case, RoadTrip) at the top of the Project navigator content area to display the Project editor.

2. In the Targets section in the Project editor, select RoadTrip.

3. On the Summary tab, scroll down to the Linked Frameworks and Libraries section.

4. Expand the Linked Frameworks and Libraries section if it isn’t already expanded (see Figure 8-1) by clicking the disclosure triangle.

5. Click the + (plus sign) button underneath the list of current project frameworks.

A list of frameworks appears.

6. Scroll down and select SystemConfiguration.frameworkas shown in Figure 8-2.

7. Click the Add button.

You’ll see the framework added to the Linked Frameworks and Libraries section.

8. Close the Linked Frameworks and Libraries section.

image

Figure 8-1: Adding a framework.

image

Figure 8-2: Select SystemConfig-uration.framework.

9. In the Project navigator (don’t do this from the Linked Frameworks and Libraries section!), drag the SystemConfiguration.framework file (you can see it at the top of the Project Navigator in Figure 8-3) to the Frameworks group, as shown in Figure 8-3.

imageYou can also add a framework by clicking the Build Phases tab in the Project editor, expanding the Link Binary with Libraries section, and following the same procedure.

When I wrote this chapter, the Reachability sample application hadn’t yet been updated to use ARC, which is okay, because it gives me a chance to show you how to use both ARC and non-ARC files in your project.

In order to have both ARC and non-ARC code in a single project, the following steps allow you to remove a file from ARC:

1. In the Project navigator, select the project icon at the top of the Project navigator area (RoadTrip, in this case) to display the Project editor.

2. In the Targets section, select RoadTrip.

3. Click the Build Phases tab.

You’re witnessing here one of the few reasons you would ever have for using any of the other tabs in the Project editor.

4. Expand the Compile Sources section, as shown in Figure 8-4.

image

Figure 8-3: Drag in the Project navigator!

image

Figure 8-4: Don’t use ARC for this file.

5. Double-click the Reachability.m file.

A window appears.

6. In the window’s text box, enter -fno-objc-arc, as shown in Figure 8-4; then click outside the text box to close it.

You may also have to choose Product⇒Clean if you tried to compile the project without doing Steps 1–6 because you wanted to see how many errors it would generate.

You have now told the compiler not to use ARC in Reachability.h and .m and that memory management is done by the Reachability code (which is what it would have been doing all along pre-ARC).

You also need to add the bolded code in Listing 8-1 to Reachability.h.

Listing 8-1: Update the Reachability Interface

#import <Foundation/Foundation.h>

#import <SystemConfiguration/SystemConfiguration.h>

#import <netinet/in.h>

Adding the code to check for reachability

The place to check for whether you have access to the Internet is right when you start up. The method for doing that is the application delegate protocol method application:didFinishLaunchingWithOptions:.

You also need to include the Reachability.h file to be able to use Reachability, so add the bolded code in Listing 8-2 to the beginning of both the RTAppDelegate.m file and the application:didFinishLaunchingWithOptions: method.

Listing 8-2: Updating the RTAppDelegate Implementation and application:didFinishLaunchingWithOptions:

#import “RTAppDelegate.h”

#import “Reachability.h”

@implementation RTAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application

         didFinishLaunchingWithOptions:

                            (NSDictionary *)launchOptions

{

  if ([[UIDevice currentDevice] userInterfaceIdiom] ==

                                UIUserInterfaceIdiomPad) {

      UISplitViewController *splitViewController = (UISplitViewController *)

                           self.window.rootViewController;

      UINavigationController *navigationController =

         [splitViewController.viewControllers lastObject];

      splitViewController.delegate =  

               (id)navigationController.topViewController;

  }

NetworkStatus networkStatus =

  [[Reachability reachabilityForInternetConnection]

                              currentReachabilityStatus];

  if (networkStatus == NotReachable) {

    UIAlertView *alert = [[UIAlertView alloc]

       initWithTitle:@”Network Unavailable”

       message:@”RoadTrip requires an Internet connection”

       delegate:nil

       cancelButtonTitle:@”OK”

       otherButtonTitles:nil];

    [alert show];

  }

  return YES;

}

I’ll have you ignore the code not in bold for the time being. I explain it in detail in Chapter 13.

In the main bolded section of Listing 8-2, you start by creating a Reachability object and then send it the currentReachabilityStatus message:

NetworkStatus networkStatus =

  [[Reachability reachabilityForInternetConnection]

                               currentReachabilityStatus];

reachabilityForInternetConnection is an initializer that creates a Reachability object that checks for the availability of an Internet connection. As I said, Reachability has a lot of functionality, but all you really care about right now is whether you can reach the Internet.

Next, check to see whether you have network access:

if (networkStatus == NotReachable) {

If you don’t have network access, you post an alert:

UIAlertView *alert = [[UIAlertView alloc]

      initWithTitle:@”Network Unavailable”

      message:@”RoadTrip requires an Internet connection”

      delegate:nil

      cancelButtonTitle:@”OK”

      otherButtonTitles:nil];

[alert show];

This is the standard way to configure and then show an alert. You have filled in the various (self-explanatory) parameters required by the initialization method. Configured this way, the alert will have a single button.

The show message to the alert object causes the alert to be displayed in the window, and when the user taps Cancel, the alert is dismissed.

imageIf you had added other buttons to give the user a choice of responses, you would have had to make the object posting the alert (the RTAppDelegate, in this case) a UIAlertViewDelegate, assigned the delegate parameter to self, and added the title of the other buttons using a nilterminated list. You would then have needed to implement the alertView:clickedButtonAtIndex: method in the delegate.

Explaining how Reachability works is beyond the scope of this book, but by examining the code, you can easily figure out how to get any other network status information you want.

If you run the application now, and either turn off your Internet connection on the computer (if you’re running the Simulator) or turn on Airplane mode or turn off your Wi-Fi connection on the iPad, you see the message shown in Figure 8-5.

image

Figure 8-5: Checking the network connection.

You can also temporarily change the

if (networkStatus == NotReachable) {

to

if (networkStatus != NotReachable) {

so that the test works in reverse. Just be sure to change it back!

Of course, in a real app, you would want to do something further here, such as give the user options and so on. I’ll leave that up to you.

Congratulations! It’s time for your first (real) adventure in coding land.

Sprucing Up the Main View

Now that you have some rudimentary code written, it’s time to pick up the pace. If you look underneath the alert in Figure 8-5, or if you dismiss the alert, you can see that you are close to the user interface I showed you in Chapter 4, but not quite there. While some of what you’ll need to do to get to this user interface could be accomplished in Interface Builder, some can’t, and I want to explain how to set the appearance of controls programmatically in your app.

Users appreciate consistency of the user interface on their iPad. If you just use the interface objects “as is,” however, your app looks sort of generic.

To allow developers to spice up the look of its apps, Apple provides three basic ways to customize a control’s appearance:

image By setting properties in Interface Builder: You use this approach when you create the custom button in Chapter 5, in the section about adding user interface objects.

image By setting properties in your program: These include some of the same properties that you could have set in Interface Builder, and some that can only be set programmatically.

image By customizing the appearance of an entire class: An example is a UIButton, which you can customize by using the UIAppearance protocol to get the appearance proxy for a class and then customize the appearance of instances of a class by sending appearance modification messages to the class’s appearance proxy.

In this section, I show you how to use the last two approaches.

You start by adding the bolded code in Listing 8-3 to the application:didFinishLaunchingWithOptions: method and deleting the commented-out code in bold, underline, and italic. In this method, you work on changing the appearance of the Navigation bar and the bar’s button items.

Listing 8-3: Updating application:didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application

          didFinishLaunchingWithOptions:

                             (NSDictionary *)launchOptions

{

  UINavigationController *navigationController;

  if ([[UIDevice currentDevice] userInterfaceIdiom] ==

                               UIUserInterfaceIdiomPad) {

    UISplitViewController *splitViewController =

      (UISplitViewController *)

                           self.window.rootViewController;

  //UINavigationController *navigationController =

        [splitViewController.viewControllers lastObject];

  //splitViewController.delegate = (id)navigationController.topViewController;

    splitViewController.presentsWithGesture = NO;

    UINavigationController *detailNavigationController =

         [splitViewController.viewControllers lastObject];

    splitViewController.delegate =

         (id)detailNavigationController.topViewController;

    navigationController =

    [splitViewController.viewControllers objectAtIndex:0];

  }

  else {

    navigationController = (UINavigationController *)

                           self.window.rootViewController;

  }

NetworkStatus networkStatus = [[Reachability reachabilityForInternetConnection] currentReachabilityStatus];

  if (networkStatus == NotReachable) {

    UIAlertView *alert = [[UIAlertView alloc]

       initWithTitle:@”Network Unavailable”

       message:@”RoadTrip requires an Internet connection”

       delegate:nil

       cancelButtonTitle:@”OK”

       otherButtonTitles:nil];

    [alert show];

  }

  [[UIApplication sharedApplication]

      setStatusBarStyle:UIStatusBarStyleBlackOpaque   

                                             animated:NO];

  navigationController.navigationBar.barStyle =

                                          UIBarStyleBlack;

  [navigationController.navigationBar  

    setTitleTextAttributes:

       [NSDictionary dictionaryWithObject:

          [UIColor yellowColor]

                        forKey:UITextAttributeTextColor]];

  [[UIButton appearance] setTitleColor:

      [UIColor greenColor] forState:UIControlStateNormal];

  [[UIBarButtonItem appearanceWhenContainedIn:

    [UINavigationBar class], nil]

    setTitleTextAttributes:

      [NSDictionary dictionaryWithObject:

                                 [UIColor yellowColor]

      forKey:UITextAttributeTextColor]

      forState:UIControlStateNormal];

  [[UIButton appearanceWhenContainedIn:

     [UIAlertView class], nil] setTitleColor:

      [UIColor whiteColor] forState:UIControlStateNormal];

  [[UIToolbar appearance] setBarStyle:UIBarStyleBlack];

  return YES;

}

These changes are for the Navigation bar elements, and other application-wide classes. The fact that they’re application wide makes it convenient to set them all up here. In addition, we want to disable the Split View gesture recognizer (which slides the Master View controller in from the left) because we’ll be using a popover, as you’ll see in Chapter 13.

Taking a closer look at Listing 8-3, you see that you start by taking into account the fact that this is a universal app, and the way you access the Navigation bar on the iPhone is different from the iPad when you use a split view. That may not make much sense now, but it will when I get into the detailed explanation of split views in Chapter 13 — so I’ll ask you to just enter the bolded code into the application:didFinishLaunchingWithOptions: method as described earlier, and wait until Chapter 13 for more details. I normally don’t like to defer explanations like this (and I generally don’t), but the explanation is rather long and detailed.

UINavigationController *navigationController;

  if ([[UIDevice currentDevice] userInterfaceIdiom] ==

                               UIUserInterfaceIdiomPad) {

    UISplitViewController *splitViewController =

      (UISplitViewController *)

                           self.window.rootViewController;

    UINavigationController *detailNavigationController =

         [splitViewController.viewControllers lastObject];

    splitViewController.delegate =

         (id)detailNavigationController.topViewController;

    navigationController =

    [splitViewController.viewControllers objectAtIndex:0];

  }

Starting with the next block of (bolded) code, you begin customizing a control’s appearance by setting properties in your program. To start with, you set the status bar at the very top of the window to black:

[[UIApplication sharedApplication]

    setStatusBarStyle:UIStatusBarStyleBlackOpaque

                                             animated:NO];

You send the setStatusBarStyle:animated message to the UIApplication object, which “owns” the status bar. As you may recall from Chapter 6, you only have one UIApplication object. You get a reference to it by sending the class message sharedApplication (also explained in Chapter 6, in the section on UIApplicationMain).

imageIn Objective-C, every class is also an object, so you can define methods that are responded to by the class.

setStausBarStyle:animated allows you to set the status bar to the default (white), black translucent, and black opaque, either with an animated action or immediately. I chose “black opaque” and “immediately” (animated:No) here.

Next, you set some of the properties of the Navigation bar, which means that you’ll need a reference to the Navigation bar object. The Navigation bar is a property of the Navigation controller, which means that you’ll of course need a reference to the Navigation controller. (I learned all this by going through the Xcode documentation for each of the classes, which I did by pressing Option and clicking the class name in the Source editor. I recommend that you explore those classes as well.)

You set the Navigation bar style to black:

navigationController.navigationBar.barStyle =

                                          UIBarStyleBlack;

This code sets not only the Navigation bar style but also the bar button elements (such as the Back button) to black.

Now that you have a nice black Navigation bar, set the title text to yellow. Setting the title text requires creating a dictionary to pass the parameters for the title text. Each key in the dictionary corresponds to a different text style attribute. (You can discover such facts by pressing Option while double-clicking the setTitleTextAttributes symbol in the Source editor to read the method documentation.) Here’s the code that sets the title text to yellow:

[navigationController.navigationBar

    setTitleTextAttributes:

      [NSDictionary dictionaryWithObject:

        [UIColor yellowColor]

                        forKey:UITextAttributeTextColor]];

Several different title text attributes are available for you to set, including font, text shadow color, and a text shadow offset, each with its own key. In this case, you specify a text color.

The UITextAttributeTextColor key specifies a text color and requires a UIColor object as its value.

A UIColor object represents color and can also represent opacity (alpha value). yellowColor (along with a number of similar methods) is a class method that returns a color object whose RGB values are 1.0, 1.0, and 0.0 and whose alpha value is 1.0. You find similar class methods for other colors, such as redColor, clearColor, and so on, as I discovered by pressing Option while double-clicking UIColor and reading the class reference documentation. (You should be getting the picture by now about reading the documentation, so I won’t belabor the point.)

If you don’t like the boring black background for the Navigation bar that you get when you set the bar style to black, you have the option of adding a background image. setBackgroundImage:forBarMetrics:, as you might guess, enables you to set a background image. You have to provide the image using UIImage (as you do in Chapter 5 in the section about adding user interface objects):

[navigationController.navigationBar setBackgroundImage:

    [UIImage imageNamed:@”NavBarImage.png”]

                       forBarMetrics:UIBarMetricsDefault];

You use the imageNamed: method to find an image in your program’s bundle (which contains the application executable and any resources used by the application, such as the application icon and aforementioned image). The imageNamed: method first looks to see whether the image has already been loaded (because you’ve used it previously) and cached. If not, it loads the image data, caches it, and then returns the resulting object. Of course, you’ll have to add that image to the project.

imageOn a device running iOS 4 or later, the behavior is identical if the device’s screen has a scale of 1.0. If the screen has a scale of 2.0 (which means you’re using the Retina display on either the iPhone or iPad), this method first searches for an image file with the same filename with an @2x suffix appended to it. For example, if the file’s name is button, the method first searches for button@2x. If it finds an image with 2x in the filename, it loads that image and sets the scale property of the returned UIImage object to 2.0. Otherwise, it loads the unmodified filename and sets the scale property to 1.0.

UIBarMetricsDefault says use this image for both Landscape and Portrait orientation. You can set a different image for Landscape orientation if you’d like.

After you have your background image installed (if you want one), you can customize the appearance of your controls using the following code. Until now, you’ve been customizing a control’s appearance by setting properties in your program. In this next chunk of code, I show you how to customize a control’s appearance by customizing the appearance of an entire class:

[[UIButton appearance] setTitleColor:

      [UIColor greenColor] forState:UIControlStateNormal];

This code takes advantage of a feature in UIKit called Custom Appearance for UIKit Controls. It enables you to customize the appearance of many UIKit views and controls to give your application a unique look and feel. You can set the tint color, background image, and title position properties (among others) on a number of objects, including toolbars, navigation bars, search bars, buttons, sliders, and some other controls.

You set the default attributes to use for a class by using an appearance proxy — an object supplied by UIKit that you can use to modify the default appearance of views and controls in classes that adopt the UIAppearance protocol. (You can see whether a class conforms to the UIAppearanceprotocol by looking in the class reference for the class.) If you were to look up the UIButton class, you would see, as shown in Figure 8-6, that it does.

image

Figure 8-6: The UIButton class conforms to the UIAppear-ance protocol.

To modify the default appearance of such a class, you need to first get its appearance proxy object by sending the appearance class message to the class:

[UIButton appearance]

Next, you send a message to the appearance proxy object to set new default values. For example, you can use a proxy object to change the default title color of the UIButton class to green by using setTitleColor, as follows:

[[UIButton appearance] setTitleColor:

      [UIColor greenColor] forState:UIControlStateNormal];

This statement modifies the titleColor for all buttons. This approach works well if you want all buttons to have this title color. It also means that every button title — including the Back button in the Navigation bar and the OK button in the alert you post when you don’t have an Internet connection — will be green. But you can fix that situation if you don’t want to include those items.

If you want the text for all Bar Button items (such as the Back button) in the alert to be yellow and not green, or to be set to any other color you want instead of the one you’re using for the appearance object, you can use the appearanceWhenContainedIn: object to set the tint color, background, image, and title position properties (among others) for all Bar Button items that are subviews (not subclasses) contained in a Navigation bar to yellow:

[[UIBarButtonItem appearanceWhenContainedIn:

    [UINavigationBar class], nil] setTitleTextAttributes:

      [NSDictionary dictionaryWithObject:

                                    [UIColor yellowColor]

         forKey:UITextAttributeTextColor]

         forState:UIControlStateNormal];

To make the title of the OK button in an alert another color (white, in this example), you can use the appearanceWhenContainedIn: object again:

[[UIButton appearanceWhenContainedIn:

  [UIAlertView class], nil] setTitleColor:

  [UIColor whiteColor] forState:UIControlStateNormal];

This code sets the font color for all Bar Button items that are subviews of the UIAlertView.

Finally, you set the appearance of all toolbars to black. (I know you don’t have a toolbar yet, but you will have one when Chapter 13 rolls around.)

[[UIToolbar appearance] setBarStyle:UIBarStyleBlack];

Figure 8-7 shows the before and after results on the iPad of the code you just added. Because this book is in black and white, you’ll have to take my word for it that the Navigation bar is black, the Navigation bar title is yellow, the Test Drive button title is green, and the Back button title is yellow.

image

Figure 8-7: Before and after control customization on the iPad.

Notice that the Navigation bar for the Detail view has stayed the same; I get around to fixing that in Chapter 13, when I replace the Navigation bar with a toolbar sporting the black tint you specified earlier in Listing 8-3.

Finally, you’ll also want to be able to set the title in the Navigation bar for the view. You could have done that in Interface Builder, but I’m showing you how to do it programmatically because you want to be able to set the title of the view based on where the user is going.

To set the RTMasterViewController title, add the bolded code in Listing 8-4 to viewDidLoad in RTMasterViewController.m.

Listing 8-4: Updating viewDidLoad

- (void)viewDidLoad

{

  [super viewDidLoad];

  self.title = @”Road Trip”;

}

How did I know to put this code here, rather than in some other section of the code? As I explain in Chapter 7, viewDidLoad is the message sent when the view is loaded for the first time, but before it’s displayed. If you want a title, you want to see it here, before the view is displayed.

Understanding Autorotation

In Chapter 5, I said I would explain more about view rotation when the orientation changes.

One of the responsibilities of the UIViewController class is to work with the application’s window to handle device rotation (also known as device orientation changes). Although the UIViewController class itself already includes the functionality to animate the transition from the current orientation to the new one (which you can override if you need to lay out the view again for the new orientation), it must also communicate to the application window whether it in fact wants to support a particular orientation.

In earlier versions of iOS, you had to override certain UIViewController methods to control autorotation, but you can manage this now by simply choosing your desired Supported Interface Orientations in the Target’s Summary Deployment Info sections, as shown in Figure 8-8.

image

Figure 8-8: The Target’s Supported Interface Orientations.

Writing Bug-Free Code

Although some developers think that writing code is where they spend the vast majority of their time when they’re developing an app, debugging is actually right up there as a very close second. (And yes, I know that the title of this section is wishful thinking.)

Because debugging plays such a crucial role in writing workable code, I want to use this section to emphasize two points:

image App developers should strive to write code with as few bugs as possible (duh!).

image App developers need to know how to use the debugger so they can track down the inevitable bugs they do introduce into their code as efficiently as possible.

With the release of Xcode 4, Apple has made it easier to write code with fewer bugs, as well as use the debugger to track down bugs you do have.

Because the best defense is a good offense, I want to start with the tools that Xcode provides that help you to write less buggy code. Xcode has figured out that the best way to make sure your code has as few bugs as possible is by giving you the opportunity to fix the code as you write it. Such opportunities come in the form of Xcode’s various compiler warnings. More specifically, by taking advantage of the Live Issues and Fix-it features, which I explain in Chapter 7, you’ll catch many of your errors before you even run your program, and fixing them will be easy. (Well, some of them, at least.) Live Issues continuously evaluates your code in the background and alerts you to coding mistakes, and Fix-it will also offer to fix the problem for you. I suggest that unless you are crystal clear about what you’re doing, don’t run your app without first resolving any outstanding compiler warnings.

Of course, Live Issues and Fix-it are really only good at fixing syntax errors — they’re usually not much help in detecting logic errors or coding mistakes that cause runtime errors (such as dividing by zero). For those errors, you need to become facile at using the debugger — or, more precisely, the Debug area and the Debug navigator.

Working in the Debug area and Debug navigator

The Debug area consists of the Debug bar, partnered with the Variables pane and the Console pane, each of which has a Scope bar fitted out with a pop-up menu. You usually use the Debug area in conjunction with the Debug navigator.

You access the Debug area by selecting it in the Xcode toolbar’s View selector (as shown in Figure 8-9). You select the Debug navigator by showing the Navigator area and then selecting the Debug navigator in the Navigator selector bar. Truth be told, though, there’s nothing much to see in the Debug area or Debug navigator unless your application is actually running. And although the Debug area’s Variables and Console panes will retain the results from your last program execution, the Debug navigator shows content only when your application is paused.

If you get a runtime error (or if you click the Pause button or a breakpoint is triggered), the Debug area and the Debug navigator open automatically.

Figure 8-10 show what happens when you hit a breakpoint (which I explain shortly) in your program.

image

Figure 8-9: Accessing the Debug area.

image

Figure 8-10: Hitting a breakpoint displays the Debug area and Debug navigator.

What you see in the Debug area is controlled by using the Debug area Scope bar, shown in Figure 8-11. You use this bar to toggle between the Variables pane only (left button), both Variables and Console panes (center button), and Console pane only (right button).

image

Figure 8-11: Use the Debug area Scope bar to control what pane you see in the Debug area.

The Variables pane and the Console pane have their very own Scope bars as well. The pop-up menu in the Variables pane Scope bar lets you display

image Auto: Recently accessed variables

image Local: Local variables

image All: All variables and registers

The pop-up menu in the Console pane Scope bar lets you display

image All Output: Target and debugger output

image Debugger Output: Debugger output only

image Target Output: Target output (program logging to the debugger, for example) only

imageXcode offers other controls and filters for what gets displayed that I encourage you to explore on your own.

Managing breakpoints

You can use the debugger to pause execution of your program at any time and view the state of the running code.

As mentioned previously, you won’t find much to see in the Debug area and Debug navigator unless your program is stopped at a breakpoint or paused (and not much at those points, either). The debugger is more useful to you if you set breakpoints to stop at known points and then view the values of the variables in your source code. Given that fact, it’s probably time to show you how to set a breakpoint and explain what a breakpoint is.

breakpoint is an instruction to the debugger to stop execution at a particular program instruction. By setting breakpoints at various methods in your program, you can step through its execution — at the instruction level — to see exactly what it’s doing. You can also examine the variables that the program is setting and using. If you’re stymied by a logic error, setting breakpoints is a great way to break that logjam.

To set breakpoints, open a file in the Source editor and click in the Gutter — the column between the Navigator area and the Focus ribbon that is adjacent to the Editor area in Figure 7-6 — next to the spot where you want execution to stop. You can toggle the state (on or off) of all the breakpoints in your program at any time by clicking the Breakpoints button in the Xcode toolbar (to the left of the Activity viewer).

imageTo disable an individual breakpoint, click its icon in the gutter. To get rid of a breakpoint completely, simply drag it off to the side. You can also right-click (or Control-click) the breakpoint and choose Remove Breakpoint from the pop-up menu that appears.

image  In Figure 8-12, I’ve added a breakpoint to the statement after I check network status. You’ll also notice that I’ve displayed the Breakpoint navigator by selecting the appropriate icon in the Navigator selector bar. The Breakpoint navigator lets you see all breakpoints at once; if you select a given breakpoint in the Breakpoint navigator, it displays in the Source editor (where you can also edit it).

image

Figure 8-12: Setting a breakpoint and displaying the Breakpoint navigator.

You can set several options for each breakpoint by Option-clicking the breakpoint and choosing Edit Breakpoint from the shortcut menu that appears, as shown in Figure 8-13.

Doing so opens the Edit Breakpoint window, where you can set the actions and options you want for breakpoints added in the Breakpoint editor. As shown in Figure 8-14, you can set a condition for a breakpoint, ignore it a set number of times before stopping, add an action, and automatically continue after evaluating actions.

In Figure 8-15, I selected the Click to Add an Action check box and then chose to add a sound. I also set a condition that I want the breakpoint to be triggered only if the networkStatus isn’t equal to notReachable. In this case, as you can see in Figure 8-15, I had to specify

networkStatus != 0

This is because networkStatus is not a symbol the debugger has access to, but rather an enumerated type (a set of named values that behave as constants). If you examine the Reachability.h file you’ll find

typedef enum {

          NotReachable = 0,

          ReachableViaWiFi,

          ReachableViaWWAN

} NetworkStatus;

image

Figure 8-13: Editing a breakpoint.

image

Figure 8-14: Some breakpoint options.

image

Figure 8-15: Fine-tuning the breakpoint.

The “normal” condition, of course, would be to set the breakpoint to stop when the condition is something you don’t expect, like the networkStatus equal to NotReachable. But I want to keep my Mac connected to my network (which connects the Simulator as well), so I set it this way so it would stop at the breakpoint every time (unless of course my network unexpectedly goes down).

Set this breakpoint and run your program in Xcode. As you can see in Figure 8-16, you’ll be stopped at the breakpoint.

image

Figure 8-16: The compiler stops at the breakpoint I set.

As you can see in the figure, when the breakpoint is reached, the Debug area is displayed and the Debug navigator opened automatically. (As I mention in Chapter 3, you can change that response in the Behaviors tab of Xcode Preferences.) It stopped because the condition I set (networkStatus != 0) evaluated YES.

What you’ll find in the Debug area

As you can see in Figure 8-16, on one side of the Debug area you have the Variables pane (which displays the values of variables), and on the other side, you have the Console pane. That’s the configuration I’ve selected in the Debug area Scope bar (the middle button).

The Variables pane

The Variables pane displays the variables you’re interested in. Click the disclosure triangle next to self to see the instance variables in the object. You’ll see the local variables to a method as well. As mentioned earlier, you can specify which items to display by using the pop-up menu in the top-left corner of the Variables pane:

image Auto: Displays only the variables you’re most likely to be interested in, given the current context.

image Local: Displays local variables.

image All: Displays all variables and registers.

You can also use the Search field on the toolbar to filter the items displayed in the Variables pane.

The Console pane

The Console pane displays program output. Again, as mentioned earlier, you can specify the type of output you see in the console by using the pop-up menu in the top-left corner of the Console pane:

image All Output: Displays target and debugger output.

image Debugger Output: Displays debugger output only.

image Target Output: Displays target output only.

When you are stopped at the breakpoint you set earlier, what you see is a lot of boilerplate and then this:

2012-02-19 12:02:12.486 RoadTrip[26123:f803] Reachability Flag Status: -R -----l- networkStatusForFlags

(lldb)

Not much of interest here, but in Chapter 11, you’ll find out how to print (really display) the contents of a variable to the Console pane — and in Chapter 15, you’ll have the opportunity to examine some runtime error messages.

The (rather boring) stuff you see here is the result of an NSLog statement in Reachability:

2012-02-19 12:02:12.486 RoadTrip[26123:f803] Reachability  

       Flag Status: -R -----l- networkStatusForFlags

NSLog allows you to display information in the Console pane during execution. For example, if you wanted to know how many points of interest your app had to display (the number of elements in the poiData array — you find out about poiData in Chapter 17), instead of using a breakpoint and displaying a variable, you could add the following NSLog statement:

NSLog(@”Number of points of interest %i”,

                                         [poiData count]);

which would display

2012-02-19 12:06:52.688 RoadTrip[26145:f803]

                            Number of points of interest 1

in the Console pane.

NSLog is pretty useful and uses the same formatting as NSString’s stringWithFormat and other formatting methods.

What you’ll find in the Debug navigator

imageSelecting an item in the Debug navigator causes information about the item to be displayed in the Source editor. For example, selecting a method displays the source code for that function in the Source editor.

Each application within iOS is made up of one or more threads, each of which represents a single path of execution through the application’s code. Every application starts with a single thread, which runs the application’s main function. The main thread encompasses the application’s mainrun loop, and it’s where the NSApplication object receives events. Applications can add (spawn) additional threads, each of which executes the code of a specific method.

imageThreads per se are way beyond the scope of this book, but that’s okay: Here you’ll be concerned with only the main thread.

Every time you send a message (or make a function call), the debugger stores information about it in a stack frame and then it stores all such frames in the call stack. When you’re thrown into the debugger because of an error (or if you pause the application by clicking the Pause button on the toolbar), Xcode displays the thread list, and within each thread the call stack for that thread, putting the most recent call at the top. The call stack shows a trace of the objects and methods that got you to where you are now.

You can do a lot more as far as threads are concerned, but again, that’s outside of the scope of this book. (If you don’t know whether to be disappointed or relieved, hold that thought.)

imageAlthough the trace isn’t really all that useful in this particular context, it can be very useful in a more complex application — it can help you to understand the path that you took to get where you are. Seeing how one object sent a message to another object — which sent a message to a third object — can be really helpful, especially if you didn’t expect the program flow to work that way.

Getting a look at the call stack can also be useful if you’re trying to understand how the framework does its job, and in what order messages are sent. As you’ll soon see, you can stop the execution of your program at a breakpoint and trace the messages sent up to that point.

Displaying variables in the Source editor

In the Debugger window, you can move your pointer over an object or variable in the Source editor to show its contents and move your pointer over disclosure triangles to see even more information.

In Figure 8-17, for example, I moved the pointer over networkStatus to see its value (information about the current status of the Internet connection).

imageWhen you move your pointer over a variable, its contents are revealed — and if more disclosure triangles appear, you can move your pointer over them to see even more information (which I explain in more detail in Chapter 11).

You see the value of the variable in the Variables pane as well.

In the next section, I show you how to step through your program after it’s stopped at a breakpoint.

image

Figure 8-17: Showing the networkStatus.

Tiptoeing through your program

When you build and run the program with breakpoints, the Debug bar appears in the Workspace window as the program runs in the Simulator. The program stops executing at the first breakpoint (if you have set a condition, it stops executing if that condition is met).

To control the execution, you use the Debug bar (located at the top of the Debug area that you see in Figure 8-18). The Debug bar includes buttons to

image Open or close the Debug area. As mentioned previously, you can hide the Debug area if you don’t need it for what you’re doing right now.

image Pause or resume execution of your code. Click this button to stop your program from executing or continue execution after it stopped when it entered the debugger.

image Step over. Click this button to make the process counter (PC), which is identified by the green arrow in the gutter, move to the next line of code to be executed. If that line of code sends a message, it will send the message (and run the method) — but then, from your perspective, it just moves to the next line of code.

image Step in. Click this button to move the process counter to the next line of code to be executed. If the line of code sends a message to a method in your source code, the debugger will step to the method and then return to the next line of code after the line that sends the message.

image Step out. Click this button to step out of the current function or method. The Source editor then displays either the method that sent the message or the function’s caller.

image Simulate location. You can have the debugger simulate the location of the iPad for you. I explain this in Chapter 17.

Because I set a condition that I want the breakpoint to be triggered only if the networkStatus isn’t equal to NotReachable, when I build and run RoadTrip in Figure 8-18, you can see that the program has stopped executing at the breakpoint. The networkStatus is reachableViaWiFi as you can (barely) see in Figure 8-18 in the Variables pane. If I then want to watch what happens as RoadTrip executes step-by-step, I would select Step In, and the screen shown in Figure 8-19 appears.

image

Figure 8-18: The Debug area and Debug bar.

image

Figure 8-19: Step in.

You can see that the instruction

if (networkStatus == NotReachable) {

caused the debugger to move to the line following the code block that would have been executed if the if statement evaluated to YES and the debugger has paused at the blank line.

That’s because the if statement evaluated to NO — the networkStatus was reachableViaWiFi) — and the code block was skipped:

  if (networkStatus == NotReachable) {

    UIAlertView *alert = [[UIAlertView alloc]

       initWithTitle:@”Network Unavailable”

       message:@”RoadTrip requires an Internet connection”

       delegate:nil

       cancelButtonTitle:@”OK”

       otherButtonTitles:nil];

    [alert show];

  }

Of course you knew that, having displayed the value of the networkStatus as reachable in Figure 8-17.

This concludes your introduction to the debugger and Debug navigator. I do want to show you a couple more things, but I need to have you add more code to have them make sense. In Chapter 11, I show you how to print (display) the contents of a variable in the Console pane, and then inChapter 15, I show you a couple of my favorite runtime errors.