Anatomy of an Xcode Project - IDE - iOS 7 Programming Fundamentals: Objective-C, Xcode, and Cocoa Basics (2014)

iOS 7 Programming Fundamentals: Objective-C, Xcode, and Cocoa Basics (2014)

Part II. IDE

By now, you’re doubtless anxious to jump in and start writing an app. To do that, you need a solid grounding in the tools you’ll be using. The heart and soul of those tools can be summed up in one word: Xcode. In this part of the book we explore Xcode, the IDE (integrated development environment) in which you’ll be programming iOS. Xcode is a big program, and writing an app involves coordinating a lot of pieces; this part of the book will help you become comfortable with Xcode. Along the way, we’ll generate a simple working app through some hands-on tutorials.

§ Chapter 6 tours Xcode and explains the architecture of the project, the collection of files from which an app is generated.

§ Chapter 7 is about nibs. A nib is a file containing a drawing of your interface. Understanding nibs — knowing how they work and how they relate to your code — is crucial to your use of Xcode and to proper development of just about any app.

§ Chapter 8 pauses to discuss the Xcode documentation and other sources of information on the API.

§ Chapter 9 explains editing your code, testing and debugging your code, and the various steps you’ll take on the way to submitting your app to the App Store. You’ll probably want to skim this chapter quickly at first, returning to it as a detailed reference later while developing and submitting an actual app.

Chapter 6. Anatomy of an Xcode Project

Xcode is the application used to develop an iOS app. An Xcode project is the source for an app; it’s the entire collection of files and settings used to construct the app. To create, develop, and maintain an app, you must know how to manipulate and navigate an Xcode project. So you must know something about Xcode, and you must know something about the nature and structure of Xcode projects and how Xcode shows them to you. That’s the subject of this chapter.

NOTE

The term “Xcode” is used in two ways. It’s the name of the application in which you edit and build your app, and it’s the name of an entire suite of utilities that accompanies it — in the latter sense, for example, Instruments and the Simulator are part of Xcode. This ambiguity should generally present little difficulty.

Xcode is a powerful, complex, and extremely large program. My approach in introducing Xcode is to suggest that you adopt a kind of deliberate tunnel vision: if you don’t understand something, don’t worry about it — don’t even look at it, and don’t touch it, because you might change something important. Our survey of Xcode will chart a safe, restricted, and essential path, focusing on aspects of Xcode that you most need to understand immediately, and resolutely ignoring everything else.

For full information, study Apple’s own documentation (choose Help → Xcode User Guide); it may seem overwhelming at first, but what you need to know is probably in there somewhere. There are also entire books devoted to describing and explaining Xcode.

NOTE

The structure of the Xcode installation changed starting with Xcode 4.3. The Developer folder in Xcode 4.2 and before was a top-level install folder containing Xcode and a lot of other files and folders. In Xcode 4.3 and later, the Developer folder is inside the Xcode application bundle itself, Xcode.app/Contents/Developer.

New Project

Even before you’ve written any code, an Xcode project is quite elaborate. To see this, let’s make a new, essentially “empty” project; you’ll find that it isn’t empty at all.

1. Start up Xcode and choose File → New → Project.

2. The “Choose a template” dialog appears. The template is your project’s initial set of files and settings. When you pick a template, you’re really picking an existing folder full of files; basically, it will be one of the folders insideXcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/Application. This template folder will essentially be copied, and a few values will be filled in, in order to create your project.

So, in this case, on the left, under iOS (not OS X!), choose Application. On the right, select Single View Application. Click Next.

3. You are now asked to provide a name for your project (Product Name). Let’s call our new project Empty Window.

In a real project, you should give some thought to the project’s name, as you’re going to be living in close quarters with it. As Xcode copies the template folder, it’s going to use the project’s name to “fill in the blank” in several places, including some filenames and some settings, such as the name of the app. Thus, whatever you type at this moment is something you’ll be seeing throughout your project. You are not locked into the name of your project forever, though, and there’s a separate setting allowing you to change at any time the name of the app that it produces. I’ll talk at the end of this chapter about name changes.

It’s fine to use spaces in a project name. Spaces are legal in the folder name, the project name, the app name, and the various names of files that Xcode will generate automatically; and in the few places where spaces are problematic (such as the bundle identifier, discussed in the next paragraph), the name you type as the Product Name will have its spaces converted to hyphens.

4. Note the Company Identifier field. The first time you create a project, this field will be blank, and you should fill it in. The goal here is to create a unique string identifying you or your company. The convention is to start the company identifier with com. and to follow it with a string (possibly with multiple dot-components) that no one else is likely to use. For example, I use com.neuburg.matt. Every app on a device or submitted to the App Store needs a unique bundle identifier. Your app’s bundle identifier, which is shown in gray below the company identifier, will consist of the company identifier plus a version of the project’s name; if you give every project a unique name within your personal world, the bundle identifier will uniquely identify this project and the app that it produces (or you can change the bundle identifier manually later if necessary).

5. Make sure the Devices pop-up menu is set to iPhone. (Ignore the Class Prefix field for now; it should be empty, with its default value “XYZ” shown in gray. For its purpose, see The Global Namespace.) Click Next.

6. You’ve now told Xcode how to construct your project. Basically, it’s going to copy the Single View Application.xctemplate folder from within the Project Templates/Application folder I mentioned earlier. But you need to tell it where to copy this template folder to. That’s why Xcode is now presenting a Save dialog. You are to specify the location of a folder that is about to be created — a folder that will be the project folder for this project.

The project folder can go just about anywhere, and you can move it after creating it. I usually create new projects on the Desktop.

7. Xcode also offers to create a git repository for your project. In real life, this can be a great convenience (see Chapter 9), but for now, uncheck that checkbox. Click Create.

8. The Empty Window project folder is created on disk (on the Desktop, if that’s the location you just specified), and the project window for the Empty Window project opens in Xcode.

The project we’ve just created is a working project; it really does build an iOS app called Empty Window. To see this, make sure that the scheme and destination in the project window’s toolbar are listed as Empty Window → iPhone Retina (3.5-inch). (The scheme and destination are actually pop-up menus, so you can click on them to change their values if needed.) Choose Product → Run. After some delay, the iOS Simulator application eventually opens and displays your app running — an empty white screen.

NOTE

To build a project is to compile its code and assemble the compiled code, together with various resources, into the actual app. Typically, if you want to know whether your code compiles and your project is consistently and correctly constructed, you’ll build the project (Product → Build). New in Xcode 5, you can compile an individual file (choose Product → Perform Action → Compile [Filename]). To run a project is to launch the built app, in the Simulator or on a connected device; if you want to know whether your code works as expected, you’ll run the project (Product → Run), which automatically builds first if necessary.

The Project Window

An Xcode project embodies a lot of information about what files constitute the project and how they are to be used when building the app, such as:

§ The source files (your code) that are to be compiled

§ Any .storyboard or .xib files, graphically expressing interface objects to be instantiated as your app runs

§ Any resources, such as icons, images, or sound files, that are to be part of the app

§ All settings (instructions to the compiler, to the linker, and so on) that are to be obeyed as the app is built

§ Any frameworks that the code will need when it runs

A single Xcode project window presents all of this information, as well as letting you access, edit, and navigate your code, plus reporting the progress and results of such procedures as building or debugging an app and more. This window displays a lot of information and embodies a lot of functionality! A project window is powerful and elaborate; learning to navigate and understand it takes time. Let’s pause to explore this window and see how it is constructed.

A project window has four main parts (Figure 6-1):

The project window

Figure 6-1. The project window

1. On the left is the Navigator pane. Show and hide it with View → Navigators → Show/Hide Navigator (Command-0) or with the first View button at the right end of the toolbar.

2. In the middle is the Editor pane (or simply “editor”). This is the main area of a project window. A project window nearly always displays an Editor pane, and can display multiple Editor panes simultaneously.

3. On the right is the Utilities pane. Show and hide it with View → Utilities → Show/Hide Utilities (Command-Option-0) or with the third View button at the right end of the toolbar.

4. At the bottom is the Debugger pane. Show and hide it with View → Show/Hide Debug Area (Command-Shift-Y) or with the second View button at the right end of the toolbar.

NOTE

All Xcode keyboard shortcuts can be customized; see the Key Bindings pane of the Preferences window. Keyboard shortcuts that I cite are the defaults.

The Navigator Pane

The Navigator pane is the column of information at the left of the project window. Among other things, it’s your primary mechanism for controlling what you see in the main area of the project window (the editor). An important use pattern for Xcode is: you select something in the Navigator pane, and that thing is displayed in the editor.

It is possible to toggle the visibility of the Navigator pane (View → Navigators → Hide/Show Navigator, or Command-0); for example, once you’ve used the Navigator pane to reach the item you want to see or work on in the editor, you might hide the Navigator pane temporarily to maximize your screen real estate (especially on a smaller monitor). You can change the Navigator pane’s width by dragging the vertical line at its right edge.

The Navigator pane itself can display eight different sets of information; thus, there are actually eight navigators. These are represented by the eight icons across its top; to switch among them, use these icons or their keyboard shortcuts (Command-1, Command-2, and so on). You will quickly become adept at switching to the navigator you want; their keyboard shortcuts will become second nature. If the Navigator pane is hidden, pressing a navigator’s keyboard shortcut both shows the Navigator pane and switches to that navigator.

Depending on your settings in the Behaviors pane of Xcode’s preferences, a navigator might show itself automatically when you perform a certain action. For example, by default, when you build your project, if warning messages or error messages are generated, the Issue navigator will appear. This automatic behavior will not prove troublesome, because it is generally precisely the behavior you want, and if it isn’t, you can change it; plus you can easily switch to a different navigator at any time.

Let’s begin experimenting immediately with the various navigators:

Project navigator (Command-1)

Click here for basic navigation through the files that constitute your project. For example, in the Empty Window folder (these folder-like things in the Project navigator are actually called groups), click AppDelegate.m to view its code in the editor (Figure 6-2).

At the top level of the Project navigator, with a blue Xcode icon, is the Empty Window project itself; click it to view the settings associated with your project and its targets. Don’t change anything here without knowing what you’re doing! I’ll talk later in this chapter about what these settings are for.

The filter bar at the bottom of the Project navigator lets you limit what files are shown; when there are many files, this is great for quickly reaching a file with a known name. For example, try typing “delegate” in the filter bar search field. Don’t forget to remove your filter when you’re done experimenting.

WARNING

Once you’ve filtered a navigator, it stays filtered until you remove the filter — even if you close the project! A common mistake is to filter a navigator, forget that you’ve done so, fail to notice the filter (because you’re looking at the navigator itself, not down at the bottom where the filter bar is), and wonder, “Hey, where did all my files go?”

The Project navigator

Figure 6-2. The Project navigator

Symbol navigator (Command-2)

A symbol is a name, typically the name of a class or method. Depending on which of the three icons in the filter bar at the bottom you highlight, you can view Cocoa’s built-in symbols or the symbols defined in your project. The former can be a useful form of documentation; the latter can be helpful for navigating your code. For example, highlight the first two icons in the filter bar (the first two are blue, the third is dark), and see how quickly you can reach the definition of AppDelegate’s applicationDidBecomeActive: method.

Try highlighting the filter bar icons in various ways to see how the contents of the Symbol navigator change. Type in the search field in the filter bar to limit what appears in the Symbol navigator; for example, try typing “active” in the search field, and see what happens.

Search navigator (Command-3)

This is a powerful search facility for finding text globally in your project, and even in the headers of Cocoa frameworks. You can also summon the Search navigator with Find → Find in Project (Command-Shift-F). The words above the search field show what options are currently in force; they are pop-up menus, so click one to change the options. Try searching for “delegate” (Figure 6-3). Click a search result to jump to it in your code.

You can type in the other search field, the one in the filter bar at the bottom, to limit further which search results are displayed. (I’m going to stop calling your attention to the filter bar now, but every navigator has it in some form.)

The Search navigator

Figure 6-3. The Search navigator

Issue navigator (Command-4)

You’ll need this navigator primarily when your code has issues. This doesn’t refer to emotional instability; it’s Xcode’s term for warning and error messages emitted when you build your project.

To see the Issue navigator in action, you’ll need to give your code an issue. For example, navigate (as you already know how to do, in at least three different ways) to the file AppDelegate.m, and in the blank line after the last comment at the top of the file’s contents, above the #importline, type howdy. Build the project (Command-B). The Issue navigator will display some error messages, showing that the compiler is unable to cope with this illegal word appearing in an illegal place. Click an issue to see it within its file. In your code, issue “balloons” may appear to the right of lines containing issues; if you’re distracted or hampered by these, toggle their visibility with Editor → Issues → Hide/Show All Issues (Command-Control-M).

Now that you’ve made Xcode miserable, select “howdy” and delete it; build again, and your issues will be gone. If only real life were this easy!

Test navigator (Command-5)

This navigator, new in Xcode 5, lists test files and individual test methods and permits you to run your tests and see whether they succeeded or failed. A test is code that isn’t part of your app; rather, it calls a bit of your app’s code to see whether it behaves as expected.

By default, a new Xcode 5 project has one test file containing one test method, to get you started. I’ll talk more about tests in Chapter 9.

Debug navigator (Command-6)

By default, this navigator will appear when your code is paused while you’re debugging it. There is not a strong distinction in Xcode between running and debugging; the milieu is the same. The difference is mostly a matter of whether breakpoints are obeyed (more about that, and about debugging in general, in Chapter 9).

To see the Debug navigator in action, you’ll need to give your code a breakpoint. Navigate once more to the file AppDelegate.m, select in the line that says return YES, and choose Debug → Breakpoints → Add Breakpoint at Current Line to make a blue breakpoint arrow appear on that line. Run the project. By default, as the breakpoint is encountered, the Navigator pane switches to the Debug navigator, and the Debug pane appears at the bottom of the window. This overall layout (Figure 6-4) will rapidly become familiar as you debug your projects.

New in Xcode 5, the Debug navigator starts with two numeric and graphical displays of profiling information (CPU and Memory); click one to see extensive graphical information in the editor. This information allows you to track possible misbehavior of your app as you run it, without the added complexity of running the Instruments utility (discussed in Chapter 9). To toggle the visibility of the profiling information at the top of the Debug navigator, click the “gauge” icon (to the right of the process’s name).

The Debug navigator also displays the call stack, with the names of the nested methods in which a pause occurs; as you would expect, you can click on a method name to navigate to it. You can shorten or lengthen the list with the slider at the bottom of the navigator. The second icon to the right of the process’s name lets you toggle between display by thread and display by queue.

The Debug pane, which can be shown or hidden at will (View → Debug Area → Hide/Show Debug Area, or Command-Shift-Y), consists of two subpanes — the variables list and the console. Either of these can be hidden using the two buttons at the bottom right of the pane. The console can also be summoned by choosing View → Debug Area → Activate Console.

The variables list (on the left)

It is populated with the variables in scope for the selected method in the call stack.

The console (on the right)

Here the debugger displays text messages; that’s how you learn of exceptions thrown by your running app, plus you can have your code deliberately send you log messages describing your app’s progress and behavior. Such messages are important, so keep an eye on the console as your app runs. You can also use the console to enter commands to the debugger. This can often be a better way to explore values during a pause than the variables list.

The Debug layout

Figure 6-4. The Debug layout

Breakpoint navigator (Command-7)

This navigator lists all your breakpoints. At the moment you’ve only one, but when you’re actively debugging a large project with many breakpoints, you’ll be glad of this navigator. Also, this is where you create special breakpoints (such as symbolic breakpoints), and in general it’s your center for managing existing breakpoints. We’ll return to this topic in Chapter 9.

Log navigator (Command-8)

This navigator lists your recent major actions, such as building or running (debugging) your project. Click a listing to see (in the editor) the log file generated when you performed that action. The log file might contain information that isn’t displayed in any other way, and also it lets you dredge up console messages from the recent past (“What was that exception I got while debugging a moment ago?”).

For example, by clicking on the listing for a successful build, and by choosing to display All and All Messages using the filter switches at the top of the log, we can see the steps by which a build takes place (Figure 6-5). To reveal the full text of a step, click on that step and then click the Expand Transcript button that appears at the far right (and see also the menu items in the Editor menu).

Viewing a log

Figure 6-5. Viewing a log

When navigating by clicking in the Navigator pane, modifications to your click can determine where navigation takes place. By default, Option-click navigates in an assistant pane (discussed later in this chapter), double-click navigates by opening a new window, and Option-Shift-click summons the navigation window, a little heads-up pane where you can specify where to navigate (a new window, a new tab, or a new assistant pane). For the settings that govern these click modifications, see the Navigation pane of Xcode’s preferences.

The Utilities Pane

The Utilities pane is the column at the right of the project window. It contains inspectors that provide information about the current selection or its settings; if those settings can be changed, this is where you change them. It also contains libraries that function as a source of objects you may need while editing your project. The Utilities pane’s importance emerges mostly when you’re editing a .storyboard or .xib file (Chapter 7). But it can be useful also while editing code, because Quick Help, a form of documentation (Chapter 8), is displayed here as well, plus the Utilities pane is the source of code snippets (Chapter 9). To toggle the visibility of the Utilities pane, choose View → Utilities → Hide/Show Utilities (Command-Option-0). You can change the Utilities pane’s width by dragging the vertical line at its left edge.

The Utilities pane consists of numerous palettes, which are clumped into multiple sets, which are themselves divided into two major groups: the top half of the pane and the bottom half of the pane. You can change the relative heights of these two halves by dragging the horizontal line that separates them.

The top half

What appears in the top half of the Utilities pane depends on what’s selected in the current editor. There are three main cases:

A code file is being edited

The top half of the Utilities pane shows either the File inspector or Quick Help. Toggle between them with the icons at the top of this half of the Utilities pane, or with their keyboard shortcuts (Command-Option-1, Command-Option-2). The File inspector is rarely needed, but Quick Help can be useful as documentation. The File inspector consists of multiple sections, each of which can be expanded or collapsed by clicking its header.

A .storyboard or .xib file is being edited

The top half of the Utilities pane shows, in addition to the File inspector and Quick Help, the Identity inspector (Command-Option-3), the Attributes inspector (Command-Option-4), the Size inspector (Command-Option-5), and the Connections inspector (Command-Option-6). These can consist of multiple sections, each of which can be expanded or collapsed by clicking its header.

An asset catalog is being edited

In addition to the File inspector and Quick Help, the Attributes inspector (Command-Option-4) lets you determine which variants of an image are listed.

The bottom half

The bottom half of the Utilities pane shows one of four libraries. Toggle between them with the icons at the top of this half of the Utilities pane, or with their keyboard shortcuts. They are the File Template library (Command-Option-Control-1), the Code Snippet library (Command-Option-Control-2), the Object library (Command-Option-Control-3), and the Media library (Command-Option-Control-4). The Object library is the most important; you’ll use it heavily when editing a .storyboard or .xib file.

To see a help pop-up describing the currently selected item in a library, press Spacebar.

The Editor

In the middle of the project window is the editor. This is where you get actual work done, reading and writing your code (Chapter 9), or designing your interface in a .storyboard or .xib file (Chapter 7). The editor is the core of the project window. You can hide the Navigator pane, the Utilities pane, and the Debug pane, but there is no such thing as a project window without an editor (though you can cover the editor completely with the Debug pane).

The editor provides its own form of navigation, the jump bar across the top. Not only does the jump bar show you hierarchically what file is currently being edited, but also it allows you to switch to a different file. In particular, each path component in the jump bar is also a pop-up menu. These pop-up menus can be summoned by clicking on a path component, or by using keyboard shortcuts (shown in the second section of the View → Standard Editor submenu). For example, Control-4 summons a hierarchical pop-up menu, which can be navigated entirely with the keyboard, allowing you to choose a different file in your project to edit. Moreover, each pop-up menu in the jump bar also has a filter field; to see it, summon a pop-up menu from the jump bar and start typing. Thus you can navigate your project even if the Project navigator isn’t showing.

The symbol at the left end of the jump bar (Control-1) summons a hierarchical menu (the Related Files menu) allowing navigation to files related to the current one. What appears here depends not only on what file is currently being edited but on the current selection within that file. This is an extremely powerful and convenient menu, and you should take time to explore it. If this file is one of a pair of class files (.m or .h), you can switch to the other member of the pair (Counterparts). You can navigate to related class files and header files (Superclasses, Subclasses, and Siblings; siblings are classes with the same superclass). You can navigate to files included by this one, and to files that include this one; you can view methods called by the currently selected method, and that call the currently selected method.

The editor remembers the history of things it has displayed, and you can return to previously viewed content with the Back button in the jump bar, which is also a pop-up menu from which you can choose. Alternatively, choose Navigate → Go Back (Command-Control-Left).

It is extremely likely, as you develop a project, that you’ll want to edit more than one file simultaneously, or obtain multiple views of a single file so that you can edit two areas of it simultaneously. This can be achieved in three ways: assistants, tabs, and secondary windows.

Assistants

You can split the editor into multiple editors by summoning an assistant pane. To do so, click the second Editor button in the toolbar, or choose View → Assistant Editor → Show Assistant Editor (Command-Option-Return). Also, by default, adding the Option key to navigation opens an assistant pane; for example, Option-click in the Navigator pane, or Option-choose in the jump bar, to navigate by opening an assistant pane (or to navigate in an existing assistant pane if there is one). To remove the assistant pane, click the first Editor button in the toolbar, or choose View → Standard Editor → Show Standard Editor (Command-Return), or click the X button at the assistant pane’s top right.

Tabs

You can embody the entire project window interface as a tab. To do so, choose File → New → Tab (Command-T), revealing the tab bar (just below the toolbar) if it wasn’t showing already. Use of a tabbed interface will likely be familiar from applications such as Safari. You can switch between tabs by clicking on a tab, or with Command-Shift-}. At first, your new tab will look largely identical to the original window from which it was spawned. But now you can make changes in a tab — change what panes are showing or what file is being edited, for example — without affecting any other tabs. Thus you can get multiple views of your project. You can assign a descriptive name to a tab: double-click on a tab name to make it editable.

Secondary windows

A secondary project window is similar to a tab, but it appears as a separate window instead of a tab in the same window. To create one, choose File → New → Window (Command-Shift-T). Alternatively, you can promote a tab to be a window by dragging it right out of its current window.

You can determine how assistant panes are to be arranged. To do so, choose from the View → Assistant Editor submenu. I usually prefer All Editors Stacked Vertically, but it’s purely a matter of taste. Once you’ve summoned an assistant pane, you can split it further into additional assistant panes. To do so, click the Plus button at the top right of an assistant pane. To move the contents of the current assistant pane into the main editor, choose Navigate → Open in Primary Editor. To dismiss an assistant pane, click the X button at its top right.

What makes an assistant pane an assistant, and not just a form of split-pane editing, is that it can bear a special relationship to the primary editor pane. The primary editor pane is the one whose contents, by default, are determined by what you click on in the Navigator pane; an assistant pane, meanwhile, can respond to what file is being edited in the primary editor pane by changing intelligently what file it (the assistant pane) is editing. This is called tracking.

To see tracking in action, open a single assistant pane and use the first component in its jump bar (Control-4). This is the Tracking menu. It’s like the Related Files menu that I discussed a moment ago, but you can select a category to determine automatic tracking behavior. For example, choose Counterparts (Figure 6-6). Now use the Project navigator to select AppDelegate.m; the primary editor pane displays this file, and the assistant automatically displays AppDelegate.h. Next, use the Project navigator to select AppDelegate.h; the primary editor pane displays this file, and the assistant automatically displays AppDelegate.m. If a category has multiple files, the assistant navigates to the first one, and a pair of arrow buttons appears at the right end of the assistant’s jump bar, with which you can navigate among the others (or use the second jump bar component, Control-5). There’s a lot of convenience and power lurking here, which you’ll explore as you need it. You can turn off tracking by setting the assistant’s first jump bar component to Manual.

Telling an assistant pane to track counterparts

Figure 6-6. Telling an assistant pane to track counterparts

There isn’t a strong difference between a tab and a secondary window; which you use, and for what, will be a matter of taste and convenience. I find that the advantage of a secondary window is that you can see it at the same time as the main window, and that it can be small. Thus, when I have a file I frequently want to refer to, I often spawn off a secondary window displaying that file, sized fairly small and without any panes other than the editor.

Tabs and windows come in handy in connection with custom behaviors. For example, as I mentioned before, it’s important to be able to view the console while debugging; I like to see it at the full size of the project window, but I also want to be able to switch back to viewing my code. So I’ve created a custom behavior (click the Plus button at the bottom of the Behaviors pane of the Preferences window) that performs two actions: Show tab named Console in active window, and Show debugger with Console View. Moreover, I’ve given that behavior a keyboard shortcut. Thus at any time I can press my keyboard shortcut, and we switch to the Console tab (creating it if it doesn’t exist) displaying nothing but the console. This is just a tab, so I can switch between it and my code with Command-Shift-}.

NOTE

There are many ways to change what appears in an editor, and the navigators don’t automatically stay in sync with those changes. To select in the Project navigator the file displayed in the current editor, choose Navigate → Reveal in Project Navigator. There are also Navigate menu items Reveal in Symbol Navigator and Reveal in Debug Navigator.

The Project File and Its Dependents

The first item in the Project navigator (Command-1) represents the project itself. (In the Empty Window project that we created earlier in this chapter, it is called Empty Window.) Hierarchically dependent upon it are items that contribute to the building of the project. Many of these items, including the project itself, correspond to items on disk in the project folder.

To survey this correspondence, let’s examine the project folder in the Finder simultaneously with the Xcode project window. Select the project listing in the Project navigator and choose File → Show in Finder (Figure 6-7).

The Project navigator and the project folder

Figure 6-7. The Project navigator and the project folder

The Finder displays the contents of your project folder. The most important of these is Empty Window.xcodeproj. This is the project file, corresponding to the project listed in the Project navigator. All Xcode’s knowledge about your project — what files it consists of and how to build the project — is stored in this file. To open a project from the Finder, double-click the project file. Alternatively, you can drag the project folder onto Xcode’s icon (in the Finder, in the Dock, or in the application switcher) and Xcode will locate the project file and open it for you; thus, you might never need to open the project folder at all!

WARNING

Never, never, never touch anything in a project folder by way of the Finder, except for double-clicking the project file to open the project. Don’t put anything directly into a project folder. Don’t remove anything from a project folder. Don’t rename anything in a project folder. Don’t touch anything in a project folder! Do all your interaction with the project through the project window in Xcode. (When you’re an Xcode power user, you’ll know when you can disobey this rule. Until then, just obey it blindly.)

The reason is that the project expects things in the project folder to be a certain way. If you make any alterations to the project folder directly in the Finder, behind the project’s back, you can upset those expectations and break the project. When you work in the project window, it is Xcode itself that makes any necessary changes in the project folder, and all will be well.

Let’s consider how the groups and files displayed hierarchically down from the project in the Project navigator correspond to reality on disk as portrayed in the Finder (Figure 6-7). (Recall that group is the technical term for the folder-like objects shown in the Project navigator.) Groups in the Project navigator don’t necessarily correspond to folders on disk in the Finder, and folders on disk in the Finder don’t necessarily correspond to groups in the Project navigator:

§ The Empty Window group corresponds directly to the Empty Window folder on disk. Files within the Empty Window group, such as AppDelegate.m, correspond to real files on disk that are inside the Empty Window folder. If you were to create additional code files (which, in real life, you would almost certainly do in the course of developing your project), you would likely put them in the Empty Window group in the Project navigator, and they, too, would then be in the Empty Window folder on disk. (That, however, is not a requirement; your files can live anywhere and your project will still work fine.)

Similarly, the Empty Window Tests group corresponds to the Empty Window Tests folder on disk, and the file Empty_WindowTests.m listed inside the Empty Window Tests group lives inside the Empty Window Tests folder.

These two group–folder pairs correspond to the two targets of your project. I’ll talk in the next section about what a target is. There is no law requiring that a target have a corresponding group in the Project navigator and a corresponding folder in the project folder, but the project template sets things up that way as an organizational convenience: it clarifies the project’s structure in the Project navigator, and it prevents a lot of files from appearing at the top level of the project folder.

§ The Supporting Files group inside the Empty Window group, on the other hand, corresponds to nothing on disk; it’s just a way of clumping some items together in the Project navigator, so that they can be located easily and can be shown or hidden together. The things inside this group are real, however; you can see that the four files Empty Window-Info.plist, InfoPlist.strings, main.m, and Empty Window-Prefix.pch do exist on disk — they’re just not inside anything called Supporting Files. (The Supporting Files group inside the Empty WindowTests group is similar.)

§ Two files in the Empty Window group, InfoPlist.strings and Main.storyboard, appear in the Finder inside folders that don’t visibly correspond to anything in the Project navigator: Main.storyboard, on disk, is inside a folder called Base.lproj, and InfoPlist.strings, on disk, is inside a folder called en.lproj. These folders have to do with localization, which I’ll discuss in Chapter 9.

§ The item Images.xcassets in the Project navigator corresponds to a specially structured folder Images.xcassets on disk. This is an asset catalog (new in Xcode 5); you add images to the asset catalog in Xcode, which maintains that folder on disk for you. This makes it easy for you to have multiple related images, such as app icons of different sizes, without having to see them all listed directly in the Project navigator. I’ll talk more about the asset catalog later in this chapter, and in Chapter 9.

You may be tempted to find all this confusing. Don’t! Remember what I said about not involving yourself with the project folder on disk in the Finder. Keep your attention on the Project navigator, make your modifications to the project there, and all will be well.

Feel free, as you develop your project and add files to it, to add further groups. The purpose of groups is to make the Project navigator work well for you. They don’t affect how the app is built, and by default they don’t correspond to any folder on disk; they are just an organizational convenience within the Project navigator. To make a new group, choose File → New → Group. To rename a group, select it in the Project navigator and press Return to make the name editable. For example, if some of your code files have to do with a login screen that your app sometimes presents, you might clump them together in a Login group. If your app is to contain some sound files, you might put them into a Sounds group. And so on.

The things in the Frameworks group and the Products group don’t correspond to anything in the project folder, but they do correspond to real things that the project needs to know about in order to build and run:

Frameworks

This group, by convention, lists frameworks (Cocoa code) on which your code depends. Frameworks exist on disk, but they are not built into your app when it is constructed; they don’t have to be, because they are present also on the target device (an iPhone, iPod touch, or iPad). Instead, the frameworks are linked to the app, meaning that the app knows about them and expects to find them on the device when it runs. Thus, all the framework code is omitted from the app itself, saving considerable space.

(Starting in Xcode 5, the new modules feature permits frameworks to be linked to your code without being listed here. I’ll talk about modules at the end of this chapter.)

Products

This group, by convention, automatically holds a reference to the executable bundle generated by building a target.

The Target

A target is a collection of parts along with rules and settings for how to build a product from them. Whenever you build, what you’re really building is a target.

Select the Empty Window project at the top of the Project navigator, and you’ll see two things on the left side of the editor: the project itself, and a list of your targets. (This list can appear either as a column on the left side of the editor, as in Figure 6-8, or as a pop-up menu at the top left of the editor if that column is collapsed to save space.) Our Empty Window project comes with two targets: the app target, called Empty Window (just like the project itself), and the tests target, called Empty WindowTests. Under certain circumstances, you might add further targets to a project. For example, you might want to write an app that can be built as an iPhone app or as an iPad app — two different apps that share a lot of the same code. So you might want one project that builds both apps through separate targets.

If you select the project in the left column or pop-up menu of the editor, you edit the project. If you select a target in the left column or pop-up menu of the editor, you edit the target. I’ll use those expressions a lot in later instructions.

Let’s concentrate on the app target, Empty Window. This is the target that you use to build and run your app. Its settings are the settings that tell Xcode how your app is to be built; its product is the app itself. (The tests target, Empty WindowTests, creates a special executable whose purpose is to test your app’s code. I’ll talk more about testing in Chapter 9.)

Build Phases

Edit the Empty Window target and click Build Phases at the top of the editor (Figure 6-8). These are the stages by which your app is built. By default, there are three of them with content — Compile Sources, Link Binary With Libraries, and Copy Bundle Resources — and those are the only stages you’ll usually need, though you can add others. The build phases are both a report to you on how the target will be built and a set of instructions to Xcode on how to build the target; if you change the build phases, you change the build process. Click each build phase to see a list of the files in your target to which that build phase will apply.

Editing the app target to show its phases

Figure 6-8. Editing the app target to show its phases

The meanings of the three build phases are pretty straightforward:

Compile Sources

Certain files (your code) are compiled, and the resulting compiled code is copied into the app.

This build phase typically applies to all of the target’s .m files; those are the code files that constitute the target. Sure enough, it currently contains ViewController.m, AppDelegate.m, and main.m. If you add a new class to your project, you’ll specify that it should be part of the app target, and the .m file will automatically be added to the Compile Sources build phase.

Link Binary With Libraries

Certain libraries, usually frameworks, are linked to the compiled code (now referred to, following compilation, as the binary), so that it will expect them to be present on the device when the app runs.

This build phase currently lists three frameworks. I’ll talk later in this chapter about the mechanics of linking the binary with additional frameworks.

Copy Bundle Resources

Certain files are copied into the app, so that your code or the system can find them there when the app runs. For example, if your app had an icon image, it would need to be copied into the app so the device could find and display it.

This build phase currently applies to the asset catalog; any images you add to the asset catalog will be copied into your app as part of the catalog. It also currently lists InfoPlist.strings and your app’s .storyboard file.

Copying doesn’t necessarily mean making an identical copy. Certain types of file are automatically treated in special ways as they are copied into the app bundle. For example, copying the asset catalog means that icons and launch images in the catalog are written out to the top level of the app bundle; copying the .storyboard file means that it is transformed into a .storyboardc file, which is itself a bundle containing nib files.

You can alter these lists manually, and sometimes you may need to do so. For instance, if something in your project, such as a sound file, was not in Copy Bundle Resources and you wanted it copied into the app during the build process, you would drag it from the Project navigator into the Copy Bundle Resources list, or (easier) click the Plus button beneath the Copy Bundle Resources list to get a helpful dialog listing everything in your project. Conversely, if something in your project was in Copy Bundle Resources and you didn’t want it copied into the app, you would delete it from the list; this would not delete it from your project, from the Project navigator, or from the Finder, but only from the list of things to be copied into your app.

A useful trick is to add a Run Script build phase, which runs a custom shell script late in the build process. To do so, choose Editor → Add Build Phase → Add Run Script Build Phase. Open the newly added Run Script build phase to edit the custom shell script. A minimal shell script might read:

echo "Running the Run Script build phase"

The “Show environment variables in build log” checkbox causes the build process’s environment variables and their values to be listed in the build log during the Run Script build phase. This alone can be a good reason for adding a Run Script build phase; you can learn a lot about how the build process works by examining the environment variables.

Build Settings

Build phases are only one aspect of how a target knows how to build the app. The other aspect is build settings. To see them, edit the target and click Build Settings at the top of the editor (Figure 6-9). Here you’ll find a long list of settings, most of which you’ll never touch. But Xcode examines this list in order to know what to do at various stages of the build process. Build settings are the reason your project compiles and builds the way it does.

Target build settings

Figure 6-9. Target build settings

You can determine what build settings are displayed by clicking Basic or All. The settings are combined into categories, and you can close or open each category heading to save room. If you know something about a setting you want to see, such as its name, you can use the search field at the top right to filter what settings are shown.

You can determine how build settings are displayed by clicking Combined or Levels; in Figure 6-9, I’ve clicked Levels, in order to discuss what levels are. It turns out that not only does a target contain values for the build settings, but the project also contains values for the same build settings; furthermore, Xcode has certain built-in default build setting values. The Levels display shows all of these levels at once, so you can understand the derivation of the actual values used for every build setting.

To understand the chart, read from right to left. For example, the iOS default for the Build Active Architecture Only setting’s Debug configuration (far right) is No. But then the project comes along (second column from the right) and sets it to Yes. The target (third column from the right) doesn’t change that setting, so the result (fourth column from the right) is that the setting resolves to Yes.

You will rarely have occasion to manipulate build settings directly, as the defaults are usually acceptable. Nevertheless, you can change build setting values, and this is where you would do so. You can change a value at the project level or at the target level. You can select a build setting and show Quick Help in the Utilities pane to learn more about it. For further details on what the various build settings are, consult Apple’s documentation, especially the Xcode Build Setting Reference.

Configurations

There are actually multiple lists of build setting values — though only one such list applies when a particular build is performed. Each such list is called a configuration. Multiple configurations are needed because you build in different ways at different times for different purposes, and thus you’ll want certain build settings to take on different values under different circumstances.

By default, there are two configurations:

Debug

This configuration is used throughout the development process, as you write and run your app.

Release

This configuration is used for late-stage testing, when you want to check performance on a device.

Configurations exist at all because the project says so. To see where the project says so, edit the project and click Info at the top of the editor (Figure 6-10). Note that these configurations are just names. You can make additional configurations, and when you do, you’re just adding to a list of names. The importance of configurations emerges only when those names are coupled with build setting values. Configurations can affect build setting values both at the project level and at the target level.

image

Figure 6-10. Configurations

For example, return to the target build settings (Figure 6-9) and type “Optim” into the search field. Now you can look at the Optimization Level build setting (Figure 6-11). The Debug configuration value for Optimization Level is None: while you’re developing your app, you build with the Debug configuration, so your code is just compiled line by line in a straightforward way. The Release configuration value for Optimization Level is Fastest, Smallest; when your app is ready to ship, you build it with the Release configuration, so the resulting binary is faster and smaller, which is great for your users installing and running the app on a device, but would be no good while you’re developing the app because breakpoints and stepping in the debugger wouldn’t work properly.

How configurations affect build settings

Figure 6-11. How configurations affect build settings

Schemes and Destinations

So far, I have not said how Xcode knows which configuration to use during a particular build. This is determined by a scheme.

A scheme unites a target (or multiple targets) with a build configuration, with respect to the purpose for which you’re building. A new project comes by default with a single scheme, named after the project. Thus the Empty Window project’s single scheme is currently called Empty Window. To see it, choose Product → Scheme → Edit Scheme. The scheme editor dialog opens.

On the left side of the scheme editor are listed various actions you might perform from the Product menu. Click an action to see its corresponding settings in this scheme.

The first action, the Build action, is different from the other actions, because it is common to all of them — the other actions all implicitly involve building. The Build action merely determines what target(s) will be built when each of the other actions is performed. For our project this means that the app target is always to be built, regardless of the action you perform, but the test target is to be built only if you elect to build in order to run the project’s tests (by choosing Product → Test).

The second action, the Run action, determines the settings that will be used when you build and run (Figure 6-12). The Build Configuration pop-up menu is set to Debug. That explains where the current build configuration comes from: at the moment, whenever you build and run (Product → Run, or click the Run button in the toolbar), you’re using the Debug build configuration and the build setting values that correspond to it, because you’re using this scheme, and that’s what this scheme says to do when you build and run.

The scheme editor

Figure 6-12. The scheme editor

You can edit the scheme, or create additional schemes. For example, suppose you wanted to build and run using the Release build configuration (in order to test the app as closely as possible to how an actual user would experience it). One way would be to edit this scheme, changing the build configuration for its Run action. Xcode makes it convenient to do this on the fly: hold the Option key as you choose Product → Run (or as you click the Run button in the toolbar). The scheme editor appears, containing a Run button. So now you can make alterations to the scheme and then proceed directly to build and run the app by clicking Run.

If you were to find yourself often wanting to switch between building and running with the Debug configuration and building and running with the Release configuration, you might create a distinct, additional scheme that uses the Release debug configuration for the Run action. This is easy to do: in the scheme editor, click Duplicate Scheme. The name of the new scheme is editable; let’s call it Empty Window Release. Change the Build Configuration pop-up menu for the Run action in our new scheme to Release, and dismiss the scheme editor.

Now you have two schemes, Empty Window (whose build configuration for running is Debug) and Empty Window Release (whose build configuration for running is Release). To switch between them easily, you can use the Scheme pop-up menu in the project window toolbar (Figure 6-13) before you build and run.

The Scheme pop-up menu

Figure 6-13. The Scheme pop-up menu

The Scheme pop-up menu lists each scheme, along with each destination on which you might run your built app. A destination is effectively a machine that can run your app. On any given occasion, you might want to run the app on a physical device or in the Simulator — and, if in the Simulator, you might want to specify that a particular type of device should be simulated. To make that choice, pick a destination in the Scheme pop-up menu.

Destinations and schemes have nothing to do with one another; your app is built the same way regardless of your chosen destination. The presence of destinations in the Scheme pop-up menu is intended as a convenience, allowing you to use this one pop-up menu to choose either a scheme or a destination, or both, in a single move. To switch easily among destinations without changing schemes, click the destination name in the Scheme pop-up menu. To switch among schemes, possibly also determining the destination (as shown in Figure 6-13), click the scheme name in the Scheme pop-up menu. You can also switch among schemes or among destinations by using the scheme editor or the Product menu.

If your app can run under more than one system version, you might also see a system version listed in the Scheme pop-up menu after a Simulator destination. For example, if your project’s deployment target (see Chapter 9) is 6.1, the Scheme pop-up menu in the project window toolbar might say “iOS 7.0” or “iOS 6.1” after the destination name. In that case, when you summon the hierarchical pop-up menu for the scheme, as shown in Figure 6-13, it will have three levels. You can switch easily among system versions without changing schemes or destinations: click the version name in the Scheme pop-up menu. This changes nothing about how your app is built; it’s a convenience, letting you determine the system version that the Simulator will use. (These system versions are technically SDKs; I’ll discuss SDKs at the end of this chapter. On installing additional Simulator SDKs, see Additional Simulator SDKs.)

Further management of schemes is through the scheme management dialog (Product → Scheme → Manage Schemes, or use the Scheme pop-up menu). For example, if you created an Empty Window Release scheme and you no longer need it, you can delete it here.

Renaming Parts of a Project

The name assigned to your project at creation time is used in many places throughout the project, leading beginners to worry that they can never rename a project without breaking something. But fear not! To rename a project, select the project listing at the top of the Project navigator, press Return to make its name editable, type the new name, and press Return again. Xcode presents a dialog proposing to change some other names to match, including the target, the built app, the precompiled header file, and the Info.plist — and, by implication, the build settings specifying these. You can check or uncheck any name, and click Rename; your project will continue to work correctly.

You can freely change the target name independently of the project name. It is the target name, not the project name, that is used to derive the name of the product and thus the bundle name, bundle display name, and bundle identifier mentioned earlier in this chapter. Thus, when you settle on a real name for your app, it might be sufficient to change the target name.

Changing the project name (or target name) does not automatically change the scheme name to match. There is no particular need to do so, but you can change a scheme name freely; choose Product → Manage Schemes and click on the scheme name to make it editable.

Changing the project name (or target name) does not automatically change the main group name to match. There is no particular need to do so, but you can freely change the name of a group in the Project navigator, because these names are arbitrary; they have no effect on the build settings or the build process. However, the main group is special, because (as I’ve already said) it corresponds to a real folder on disk, the folder that sits beside your project file at the top level of the project folder. Beginners should not change the name of that folder on disk, as that folder name is hard-coded into several build settings.

You can change the name of the project folder in the Finder at any time, and you can move the project folder in the Finder at will, because all build setting references to file and folder items in the project folder are relative.

From Project to Running App

An app file is really a special kind of folder called a package (and a special kind of package called a bundle). The Finder normally disguises a package as a file and does not dive into it to reveal its contents to the user, but you can bypass this protection and investigate an app bundle with the Show Package Contents command. By doing so, you can study the internal structure of your built app bundle.

We’ll use the Empty Window app that we built earlier as a sample minimal app to investigate. You’ll have to locate it in the Finder; by default, it should be somewhere in your user Library/Developer/Xcode/DerivedData folder, as shown in Figure 6-14. (I presume you know how to reveal the user Library directory.) In theory, you should be able to select the app under Products in Xcode’s Project navigator and choose File → Show in Finder, but there seems to be a long-standing bug preventing this.

The built app, in the Finder

Figure 6-14. The built app, in the Finder

In the Finder, Control-click the Empty Window app, and choose Show Package Contents from the contextual menu. Here you can see the results of the build process (Figure 6-15).

Contents of the app package

Figure 6-15. Contents of the app package

Think of the app bundle as a transformation of the project folder:

Empty Window

Our app’s compiled code. The build process has compiled ViewController.m, AppDelegate.m, and main.m, first performing precompilation so as to obey all #import directives (and also importing the results of precompiling Empty Window-Prefix.pch). The result is this single file, our app’s binary. This is the heart of the app, its actual executable material. When the app is launched, the binary is linked to the various frameworks, and the code begins to run starting with the main function.

Main.storyboardc

Our app’s storyboard file. The project’s Main.storyboard is where our app’s interface comes from — in this case, an empty white view occupying the entire window. The build process has compiled Main.storyboard (using the ibtool command-line tool) into a tighter format, resulting in a .storyboardc file, which is actually a bundle of nib files to be loaded as the app runs. One of these nib files, loaded as our app launches, will be the source of the white view displayed in the interface. Main.storyboardc occupies the same subfolder location (inside Base.lproj) asMain.storyboard does in the project folder; as I said earlier, this folder structure has to do with localization (to be discussed in Chapter 9).

InfoPlist.strings

This file has been copied directly from the project folder, maintaining the same subfolder location (inside en.lproj). This, too, has to do with localization.

Assets.car and LaunchImage-700-568h@2x~iphone.png

An asset catalog and a launch image. The original asset catalog Images.xcassets has been processed (using the actool command-line tool). This has resulted in a compiled asset catalog file (.car) containing any images that have been added to the catalog, along with a default black launch image (to be displayed momentarily during the animation when our app launches). If this were a real app, you’d probably see some additional app icon and launch image files written out from the asset catalog to the top level of the app bundle.

Info.plist

A configuration file in a strict text format (a property list file). It is derived from the project file Empty Window-Info.plist. It contains instructions to the system about how to treat and launch the app. For example, if our app had an icon, Info.plist would tell the system its name, so that the system could retrieve it from the app bundle and display it. It also tells the system things like the name of the binary, so that the system can find it and launch the app.

PkgInfo

A tiny text file reading APPL????, signifying the type and creator codes for this app. The PkgInfo file is something of a dinosaur; it isn’t really necessary for the functioning of an iOS app and is generated automatically. You’ll never need to touch it.

In real life, an app bundle will contain more files, but the difference will be mostly one of degree, not kind. For example, our project might have additional .storyboard or .xib files, icon image files, and image or sound files. All of these would make their way into the app bundle. In addition, an app bundle built to run on a device will contain some security-related files.

You are now in a position to appreciate, in a general sense, how the components of a project are treated and assembled into an app, and what responsibilities accrue to you, the programmer, in order to ensure that the app is built correctly. The rest of this chapter outlines what goes into the building of an app from a project, as well as how the constituents of that app are used at launch time to get the app up and running.

Build Settings

We have already talked about how build settings are determined. Xcode itself, the project, and the target all contribute to the resolved build setting values, some of which may differ depending on the build configuration. Before building, you, the programmer, will have specified a scheme; the scheme determines the build configuration, meaning the specific set of build setting values that will apply as this build proceeds.

Property List Settings

Your project contains a property list file that will be used to generate the built app’s Info.plist file. The app target knows what file it is because it is specified in the Info.plist File build setting. For example, in our project, the value of the app target’s Info.plist File build setting has been set toEmpty Window/Empty Window-Info.plist. (Take a look at the build settings and see!)

NOTE

Because the name of the file in your project from which the built app’s Info.plist file is generated will vary, depending on the name of the project, I’ll refer to it generically as the project’s Info.plist.

The property list file is a collection of key–value pairs. You can edit it, and you may well need to do so. There are three main ways to edit your project’s Info.plist:

§ Select the Info.plist file in the Project navigator and edit in the editor. By default, the key names (and some of the values) are displayed descriptively, in terms of their functionality; for example, it says “Bundle name” instead of the actual key, which is CFBundleName. But you can view the actual keys: click in the editor and then choose Editor → Show Raw Keys & Values, or use the contextual menu. In addition, you can see the Info.plist file in its true XML form: Control-click the Info.plist file in the Project navigator and choose Open As → Source Code from the contextual menu.

§ Edit the target, and click Info at the top of the editor. The Custom iOS Target Properties section shows effectively the same information as editing the Info.plist in the editor.

§ Edit the target, and click General at the top of the editor. Some of the settings here are effectively ways of editing the Info.plist. For example, when you click a Device Orientation checkbox here, you are changing the value of the “Supported interface orientations” key in the Info.plist. (Other settings here are effectively ways of editing build settings. For example, when you change the Deployment Target here, you are changing the value of the iOS Deployment Target build setting.)

Some values in the project’s Info.plist require processing to transform them into their final values in the built app’s Info.plist. This step is performed late in the build process. For example, the “Executable file” key’s value in the project’s Info.plist is ${EXECUTABLE_NAME}; for this must be substituted the value of the EXECUTABLE_NAME build environment variable (which, as I mentioned earlier, you can discover by means of a Run Script build phase). Also, some additional key–value pairs are injected into the Info.plist during processing.

For a complete list of the possible keys and their meanings, see Apple’s document Information Property List Key Reference. I’ll talk more in Chapter 9 about Info.plist settings that you’re particularly likely to edit.

Nib Files

A nib file is a description of a piece of user interface in a compiled format contained in a file with the extension .nib. Every app that you write is likely to contain at least one nib file. A nib file is generated during the build process by compilation (using the ibtool command-line tool) either from a .xib file, which results in a nib file, or from a .storyboard file, which results in a .storyboardc bundle containing multiple nib files. This compilation takes place by virtue of the .storyboard or .xib files being listed in the app target’s Copy Bundle Resources build phase.

You edit the .xib or .storyboard file graphically in Xcode; in effect, you are describing graphically some objects that you want instantiated when the app runs and the nib file loads (Chapter 5). Thanks to this architecture, a nib file loads only when and if it is needed; this streamlines your app’s launch time, when the only nib files loaded are those required to generate your app’s initial interface, as well as your app’s memory usage over time, because objects described in a nib are not instantiated until that nib is loaded, and can then be destroyed when they are no longer needed.

Our Empty Window project generated from the Single View Application template contains a single .storyboard file, called Main.storyboard. This one file is subject to special treatment as the app’s main storyboard, not because of its name, but because it is pointed to in the Info.plist file by the key “Main storyboard file base name” (UIMainStoryboardFile), using its name (“Main”) without the .storyboard extension — edit the Info.plist file and see! The result is that as the app launches, the first nib generated from this .storyboard file is loaded automatically to help create the app’s initial interface.

If we had elected to use the Single View Application template to create a universal app — that is, an app that runs both on the iPad and on the iPhone — it would have had two .storyboard files, one to be used on the iPad (Main_iPad.storyboard) and the other to be used on the iPhone (Main_iPhone.storyboard). Thus the app can have different basic interfaces on the two different types of device. One of these storyboards is to be treated as the main storyboard file, depending on the device type at launch time, to create the app’s initial interface. For this purpose, a secondInfo.plist key is used: the key “Main storyboard file base name” (UIMainStoryboardFile) points to “Main_iPhone”, while the key “Main storyboard file base name (iPad)” (UIMainStoryboardFile~ipad) points to “Main_iPad”.

I’ll talk more about the app launch process and the main storyboard later in this chapter. See Chapter 7 for more about editing .xib and .storyboard files and how they create instances when your code runs.

Additional Resources

Resources are ancillary files embedded in your app bundle, to be fetched as needed when the app runs, such as images you want to display or sounds you want to play. A real-life app is likely to involve many such additional resources. You’ll add these resources to the project, making sure that they appear in the Copy Bundle Resources build phase. Making such resources available when your app runs will usually be up to your code (or to the code implied by the loading of a nib file): basically, the runtime simply reaches into your app bundle and pulls out the desired resource. In effect, your app bundle is being treated as a folder full of extra stuff.

To add a resource to your project, start in the Project navigator and choose File → Add Files to [Project], or drag the resource from the Finder into the Project navigator. A dialog appears (Figure 6-16) in which you make the following settings:

Options when adding a resource to a project

Figure 6-16. Options when adding a resource to a project

Copy items into destination group’s folder (if needed)

You should almost certainly check this checkbox. Doing so causes the resource to be copied into the project folder. If you leave this checkbox unchecked, your project will be relying on a file that’s outside the project folder, where you might delete or change it unintentionally. Keeping everything your project needs inside the project folder is far safer.

Folders

This choice matters only if what you’re adding to the project is a folder; the difference is in how the project references the folder contents:

Create groups for any added folders

The folder name becomes the name of an ordinary group within the Project navigator; the folder contents appear in this group, but they are listed individually in the Copy Bundle Resources build phase, so by default they will all be copied individually into the top level of the app bundle.

Create folder references for any added folders

The folder is shown in blue in the Project navigator (a folder reference); moreover, it is listed as a folder in the Copy Bundle Resources build phase, meaning that the build process will copy the entire folder and its contents into the app bundle. This means that the resources inside the folder won’t be at the top level of the app bundle, but in a subfolder within it. Such an arrangement can be valuable if you have many resources and you want to separate them into categories (rather than clumping them all at the top level of the app bundle) or if the folder hierarchy among resources is meaningful to your app. The downside of this arrangement is that the code you write for accessing a resource will have to be specific about what subfolder of the app bundle contains that resource.

Add to Targets

Checking this checkbox for a target causes the resource to be added to that target’s Copy Bundle Resources build phase. Thus you will almost certainly want to check it for the app target; why else would you be adding this resource to the project? Still, if this checkbox accidentally goes unchecked and you realize later that a resource listed in the Project navigator needs to be added to the Copy Bundle Resources build phase for a particular target, you can add it manually, as I described earlier.

Image resources for an iOS program tend to come in pairs, one for the single-resolution screen and one for the double-resolution screen. In order to work properly with the framework’s image-loading methods, such pairs employ a special naming convention: for example, listen_normal.pngand listen_normal@2x.png, where the @2x suffix in the second file’s name means that it is the double-resolution variant of the first file. This can cause a confusing proliferation of image files in the Project navigator. That is one of the annoyances that asset catalogs, new in Xcode 5, are intended to alleviate.

Instead of adding listen_normal.png to my project in the way I’ve just described, I can decide to let an asset catalog help me manage it. I’ll use the default catalog, Images.xcassets. I edit the default catalog, click the Plus button at the bottom of the first column, and choose New Image Set. The result is a placeholder called Image with a space for the single-resolution image and a space for the double-resolution image. I drag the two images listen_normal.png and listen_normal@2x.png into this space, where each is automatically assigned to its proper spot. Moreover, there’s no dialog (as in Figure 6-16); the images have automatically been copied into the project folder (inside the asset catalog folder), and there is no need for me to specify the target membership of these image files, because they are part of an asset catalog which already has correct target membership. Finally, I can rename the placeholder to something more descriptive than Image and simpler than listen_normal; let’s call it Listen (Figure 6-17).

An image pair added to an asset catalog

Figure 6-17. An image pair added to an asset catalog

The result is that my code can now load the correct image for the current screen resolution by referring to it as @"Listen", without regard to the original name (or extension) of the images. Moreover, these images do not need to appear separately in the built app bundle; for an iOS 7 app, they can remain inside the compiled asset catalog.

Another advantage of asset catalogs is that the naming conventions don’t have to be followed. Suppose I have two images, little.png and big.png, where the latter is the double-resolution version of the former. We’re not using the @2x naming convention, so the asset catalog can’t tell that the two images are related in this way. But I can tell it, by dragging little.png onto my image set’s “1x” space, and big.png onto its “2x” space.

The entries in an asset catalog can be inspected by selecting an image and using the Attributes inspector (Command-Option-4). This shows the name and size of the image. For example, in the case I just described, inspecting the “1x” variant of my image set tells me that this image islittle.png, and gives its dimensions, showing that it is indeed half the size of its “2x” counterpart, big.png.

Code and the App Launch Process

The build process knows what code files to compile to form the app’s binary because they are listed in the app target’s Compile Sources build phase. (In the case of our Empty Window project, these are, as we’ve already seen, ViewController.m, AppDelegate.m, and main.m.) In addition, the project’s precompiled header file is used during compilation; in fact, it is processed before any other code files. It isn’t listed in the Compile Sources build phase; the target knows about it because it is pointed to by the Prefix Header build setting. (In the case of our Empty Window project, this file is called Empty Window-Prefix.pch.)

The precompiled header is a device for making compilation go faster. It’s a header file; it is compiled once (or at least, very infrequently) and the results are cached (in the DerivedData folder) and are implicitly imported by all your code files. So the precompiled header should consist primarily of #import directives for headers that never change (such as the built-in Cocoa headers); it is also a reasonable place to put #defines that will never change and that are to be shared by all your code, as I mentioned in Chapter 4. The default precompiled header file importsFoundation.h (the Core Foundation framework header file) and UIKit.h (the Cocoa framework header file). I’ll talk in the next section about what that means.

When the app launches, the system knows where to find the binary inside the app’s bundle because the app bundle’s Info.plist file has an “Executable file” key (CFBundleExecutable) whose value tells the system the name of the binary; by default, the binary’s name comes from theEXECUTABLE_NAME environment variable (here, “Empty Window”).

The app is an Objective-C program, and Objective-C is C, so the entry point is the main function. This function is defined in the project’s main.m file. Here are the main function’s contents:

int main(int argc, char *argv[])

{

@autoreleasepool {

return UIApplicationMain(argc, argv, nil,

NSStringFromClass([AppDelegate class]));

}

}

The main function does just two things:

§ It sets up a memory management environment — the @autoreleasepool and the curly braces that follow it. I’ll explain more about that in Chapter 12.

§ It calls the UIApplicationMain function, which does the heavy lifting of helping your app pull itself up by its bootstraps and get running.

UIApplicationMain is responsible for solving some tricky problems. Where will your app get its initial instances? What instance methods will initially be called on those instances? Where will your app’s initial interface come from? Here’s what UIApplicationMain does:

1. UIApplicationMain creates your app’s first instance — the shared application instance, which is to be subsequently accessible in code by calling [UIApplication sharedApplication]. The third argument in the call to UIApplicationMain specifies, as a string, what class the shared application instance should be an instance of. If nil, which will usually be the case, the default class is UIApplication. If, for some reason, you need to subclass UIApplication, you’ll specify that subclass here by substituting something like this (depending on what the subclass is called) as the third argument in the call to UIApplicationMain:

NSStringFromClass([MyUIApplicationSubclass class])

2. UIApplicationMain creates your app’s second instance — the application instance’s delegate. Delegation is an important and pervasive Cocoa pattern, described in detail in Chapter 11. It is crucial that every app you write have an app delegate instance. The fourth argument in the call to UIApplicationMain specifies, as a string, what class the app delegate instance should be. In our main.m, that specification is:

NSStringFromClass([AppDelegate class])

This tells UIApplicationMain to create an instance of the AppDelegate class, and to associate that instance with the shared application instance as the latter’s delegate. The code files defining this class, AppDelegate.h and AppDelegate.m, were created for you by the template; of course you can (and probably will) edit that code.

3. If the Info.plist file specifies a main storyboard file, UIApplicationMain loads it and looks inside it to find the view controller that is designated as this storyboard’s initial view controller; it instantiates this view controller, thus creating your app’s third instance. In the case of our Empty Window project, as constructed for us from the Single View Application template, that view controller will be an instance of the class called ViewController; the code files defining this class, ViewController.h and ViewController.m, were also created by the template.

4. If there was a main storyboard file, UIApplicationMain now creates your app’s window by instantiating the UIWindow class — this is your app’s fourth instance. It assigns this window instance as the app delegate’s window property; it also assigns the initial view controller instance as the window instance’s root view controller (rootViewController property).

(I’m simplifying. In actual fact, UIApplicationMain first gives the app delegate instance a chance to create the window instance, by calling its window method. This is your code’s opportunity to cause the window instance to come from a custom subclass of UIWindow.)

5. UIApplicationMain now turns to the app delegate instance and starts calling some of its code — in particular, it calls application:didFinishLaunchingWithOptions:. This is an opportunity for your own code to run! Thus,application:didFinishLaunchingWithOptions: is a good place to put your code that initializes values and performs startup tasks, but you don’t want anything time-consuming to happen here, because your app’s interface still hasn’t appeared.

(I’m simplifying again. Starting in iOS 6, the sequence of calls to the app delegate’s code actually begins with application:willFinishLaunchingWithOptions: if it exists.)

6. If there was a main storyboard, UIApplicationMain now causes your app’s interface to appear. It does this by calling the UIWindow instance method makeKeyAndVisible.

7. The window is now about to appear. This, in turn, causes the window to turn to its root view controller and tell it to obtain its main view, which will occupy and appear in the window. If this view controller gets its view from a .storyboard or .xib file, the corresponding nib is now loaded; its objects are instantiated and initialized, and they become the objects of the initial interface.

The app is now launched and visible to the user. It has an initial set of instances — at a minimum, the shared application instance, the window, the initial view controller, and the initial view controller’s view and whatever interface objects it contains. Some of your code (the app delegate’sapplication:didFinishLaunchingWithOptions:) has run, and we are now off to the races: UIApplicationMain is still running (like Charlie on the M.T.A., UIApplicationMain never returns), and is just sitting there, watching for the user to do something, maintaining the event loop, which will respond to user actions as they occur.

In my description of the app launch process, I used several times the phrase “if there is a main storyboard.” In most of the Xcode 5 app templates, such as the Single View Application template that we used to generate the Empty Window project, there is a main storyboard. It is also possible, though, not to have a main storyboard. In that case, things like creating a window instance, giving it a root view controller, assigning it to the app delegate’s window property, and calling makeKeyAndVisible on the window to show the interface, must be done by your code.

To see what I mean, make a new iPhone project starting with the Empty Application template; let’s call it Truly Empty. It has an App Delegate class, but no storyboard and no initial view controller. The app delegate’s application:didFinishLaunchingWithOptions: inAppDelegate.m has code to create the window, assign it to the app delegate’s window property, and show it:

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// Override point for customization after application launch.

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

If you build and run this app, it works and displays an empty white window; but there’s a complaint in the console from the runtime engine, “Application windows are expected to have a root view controller at the end of application launch.” We can prevent that complaint by creating a view controller and assigning it to the window’s rootViewController property:

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// Override point for customization after application launch.

self.window.rootViewController = [UIViewController new]; // prevent complaint

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

That works: the complaint no longer appears when we build and run the app. However, we have left ourselves with no way customize our app’s behavior meaningfully, as we have no UIViewController subclass. Moreover, we have no way to create the interface graphically; there is neither a.storyboard nor a .xib file. We can solve both problems by making a UIViewController subclass along with a .xib file:

1. Choose File → New → File. In the “Choose a template” dialog, under iOS, click Cocoa Touch on the left and select Objective-C Class. Click Next.

2. Name the class ViewController and specify that it is a subclass of UIViewController. Check the “With XIB for user interface” checkbox. Click Next.

3. The Save dialog appears. Make sure you are saving into the Truly Empty folder, that the Group pop-up menu is set to Truly Empty as well, and that the Truly Empty target is checked — we want these files to be part of the app target. Click Create.

Xcode has created three files for us: ViewController.h and ViewController.m, defining ViewController as a subclass of UIViewController, and ViewController.xib, the source of the nib from which a ViewController instance will automatically obtain its view by default.

4. Now go back to the app delegate’s application:didFinishLaunchingWithOptions:, in AppDelegate.m, and change the root view controller’s class to ViewController; you’ll have to put #import "ViewController.h" at the top of this file as well:

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

// Override point for customization after application launch.

self.window.rootViewController = [ViewController new]; // our subclass

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

We have thus created a perfectly usable minimal app project without a storyboard. Our code does some of the work that is done automatically by UIApplicationMain when there is a main storyboard: we instantiate UIWindow, we set the window instance as the app delegate’s windowproperty, we instantiate an initial view controller, we set that view controller instance as the window’s rootViewController property, and we cause the window to appear. Moreover, the appearance of the window automatically causes the ViewController instance to fetch its view from the ViewController nib, which has been compiled from ViewController.xib; thus, we can use ViewController.xib to customize the app’s initial interface.

Frameworks and SDKs

A framework is a library of compiled code used by your code. Most of the frameworks you are likely to use when programming iOS will be Apple’s built-in frameworks. These frameworks are already part of the system on the device where your app will run; they live in/System/Library/Frameworks on the device, though you can’t tell that on an iPhone or iPad because there’s no way (normally) to view the file hierarchy directly.

Your compiled code also needs to be connected to those frameworks when the project is being built, on your computer. To make this possible, the iOS device’s System/Library/Frameworks is duplicated on your computer, inside Xcode itself. This duplicated subset of the device’s system is called an SDK (for “software development kit”). What SDK is used depends upon what destination you’re building for.

Linking is the process of hooking up your compiled code with the frameworks that it needs, even though those frameworks are in one place at build time and in another place at runtime. Thus, for example:

When you build your code to run on a device:

A copy of any needed frameworks is used. This copy lives in System/Library/Frameworks/ inside the iPhone SDK, which is located at Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk.

When your code runs on a device:

The code, as it starts running, looks in the device’s top-level /System folder, located at /System/Library/Frameworks/, for the frameworks that it needs.

Used in this way, the frameworks are part of an ingenious mechanism whereby Apple’s code is effectively incorporated dynamically into your app when the app runs. The frameworks are the locus of all the stuff that every app might need to do; they are Cocoa. That’s a lot of stuff, and a lot of compiled code. Your app gets to share in the goodness and power of the frameworks because it is linked to them. Your code works as if the framework code were incorporated into it. Yet your app is relatively tiny; it’s the frameworks that are huge.

Linking takes care of connecting your compiled code to any needed frameworks, but it isn’t sufficient to allow your code to compile in the first place. The frameworks are full of classes (such as NSString) and methods (such as uppercaseString) that your code will call. To satisfy the compiler, the frameworks do exactly what any C or Objective-C code would do: they publish their interface in header files (.h), which your code can import. Thus, for example, your code can speak of NSString and can call uppercaseString because it imports NSString.h. Actually, what your code imports is UIKit.h, which in turn imports Foundation.h, which in turn imports NSString.h. And you can see this happening at the top of any of your own code’s header files:

#import <UIKit/UIKit.h>

(See Example 4-1 and the surrounding discussion for more about where framework headers are imported by class files and why.)

Thus, using a framework is a two-part process:

Import the framework’s header

Your code needs this information in order to compile successfully. Your code imports a framework’s header by using the #import directive, specifying either that framework’s header or a header that itself imports the desired header.

Link to the framework

The compiled executable binary needs to be connected to the frameworks it will use while running, effectively incorporating the compiled code constituting those frameworks. As your code is built, it is linked to any needed frameworks, in accordance with the list of frameworks in the target’s Link Binary With Libraries build phase.

By default, the template has already linked three frameworks into your app target. It does this by listing them in the Link Binary With Libraries build phase (the frameworks then also appear in the Project Navigator, conventionally in the Frameworks group, and you can use these listings to navigate and examine their header files):

UIKit

Cocoa classes that are specialized for iOS, whose names begin with “UI,” are part of the UIKit framework. The UIKit framework is imported (<UIKit/UIKit.h>) in the precompiled header file and in the class code header files that constitute the app templates, such as AppDelegate.h, as well as class code files that you create.

Foundation

Many basic Cocoa classes, such as NSString and NSArray and others whose names begin with “NS,” are part of the Foundation framework. The Foundation framework is imported in the precompiled header file, but there is no need for this, because many headers imported by UIKit.himport the Foundation framework (<Foundation/Foundation.h>). In turn, Foundation.h includes the Core Foundation headers (<CoreFoundation/CoreFoundation.h>) and loads the Core Foundation framework as a subframework; thus, there is no need for you to import or link explicitly to the Core Foundation framework (which is full of functions and pointer types whose names begin with “CF,” such as CFStringRef).

Core Graphics

The Core Graphics framework defines many structs and functions connected with drawing, whose names begin with “CG.” It is imported by many UIKit headers, so you don’t need to import it explicitly.

iOS has about 50 more frameworks, but the templates do not import them into your code or link them to your target. That’s because extra frameworks mean extra work, both in order to compile your code and in order to launch your app. To use one of these additional frameworks, therefore,you must perform both the steps I listed a moment ago: you must import the framework’s header wherever your code wants to use symbols published by that header, and you must link your code to the framework itself. This two-part process is rather inconvenient, and if you omit a step, things won’t work properly.

For example, let’s say you’ve just found out about the Address Book and you’re raring to try it in your app. So, in your code, you create an ABNewPersonViewController:

ABNewPersonViewController* ab = [ABNewPersonViewController new];

The next time you try to build your app, the compiler complains that ABNewPersonViewController is an undeclared identifier. That’s when you realize you need to import a framework header. The pair of letters at the start of ABNewPersonViewController’s name might have provided a clue that it’s provided by some additional framework; it doesn’t start with “NS,” “UI,” or “CG”. The ABNewPersonViewController class documentation informs us that it comes from the AddressBookUI framework. You might guess (correctly) that the way to import this framework’s header is to put this line near the start of your implementation file:

#import <AddressBookUI/AddressBookUI.h>

This works to quiet the compiler, but your code still doesn’t build. This time, you get a build error during the link phase of the build process complaining about _OBJC_CLASS_$_ABNewPersonViewController and saying, “Symbol(s) not found.” That mysterious-sounding error merely means that you’ve forgotten the second step: you have to link your target to AddressBookUI.framework.

To link your target to a framework, edit the target, click General at the top of the editor, and scroll down to the Linked Frameworks and Libraries section. (This is the same information that appears when you click Build Phases at the top of the editor and open the Link Binary With Libraries build phase.) Click the Plus button at the left just below the list of frameworks. A dialog appears listing the existing frameworks that are part of the active SDK. Select AddressBookUI.framework and click Add. The AddressBookUI framework is added to the target’s Link Binary With Libraries build phase. (It also appears in the Project navigator.) Now you can build (and run) your app.

Starting in Xcode 5 and LLVM 5.0, you can elect to make the process of using additional frameworks much simpler, by using modules. Use of modules is a build setting, Enable Modules (C and Objective-C). By default, this setting is Yes for new projects constructed from the application templates.

Modules are cached information stored on your computer at Library/Developer/Xcode/DerivedData/ModuleCache. When you build an app that imports a framework header, if the information for that framework hasn’t been cached as a module, it is cached as a module at that moment. Here are some benefits of using modules:

Smaller code files after precompilation

When precompilation is performed, whatever is imported is literally copied into your code. The UIKit headers, together with the headers that they import, constitute about 30,000 lines of code. This means that, in order to compile any .m file of yours, the compiler must deal with a file that is at least 30,000 lines longer than the code you wrote. When you’re using modules, however, the imported header information lives in the modules, and the length of your code files is not significantly increased by precompilation. This might also mean that your code compiles faster.

Easier, shorter imports

Importing a framework’s header can be tedious. The header name must be in brackets, and you have to provide both the framework’s name (which is actually a folder name) and the name of the header file:

#import <AddressBookUI/AddressBookUI.h>

With modules, instead of the #import directive, you can use a new directive, @import (with an at-sign instead of a hash-sign), which requires only the bare name of the framework followed by a semicolon:

@import AddressBookUI;

Moreover, #imports are automatically converted to @imports, so you can express yourself either way, and your existing code using #import keeps working while taking advantage of modules.

Autolinking

Using a framework is a two-step process. Not only must you import the header, but also you must link to the framework. Modules permit you to omit the second step. This convenience is optional (it’s a build setting, Link Frameworks Automatically), but it’s turned on by default. The result is that once your code has imported a framework’s header, your target doesn’t need to link to the framework. Linking will be performed automatically during the build process.

Modules are ingenious and convenient, but they also have some aspects that might be considered disadvantages as opposed to the old regime of explicit importing and linking. For example, when a framework is explicitly linked into your project, you know it, because it is listed in the Link Binary With Libraries build phase and in the Project navigator. With modules, you don’t know what frameworks you’re using; there’s no list of your autolinked frameworks.

Moreover, because autolinked frameworks are not listed in the Project navigator, their headers are not navigable in the Project navigator (whereas the headers of explicitly linked frameworks are), and their headers are not searchable in the Project navigator (whereas the headers of explicitly linked frameworks are, by using a search scope of “within workspace and frameworks”).

Fortunately, if you miss those features, there’s no harm in linking to a framework manually, to create a listing for it in the Project navigator, even if that framework is also being autolinked.