iOS 8 Programming Fundamentals with Swift: Swift, Xcode, and Cocoa Basics (2015)

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, 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 Overview); 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.

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/iOS/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 later about name changes (Renaming Parts of a Project).

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. But do not use any other punctuation in your project name! Such punctuation can cause Xcode features to break in subtle ways. (For example, a question mark in a project name causes nib editor previews not to work.)

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.    New in Xcode 6, the Language pop-up menu lets you choose between Swift and Objective-C. This choice is not positively binding; it dictates the initial structure and code of the project template, but you are free to add Swift files to an Objective-C project, or Objective-C files to a Swift project. You can even start with an Objective-C project and decide later to convert it completely to Swift. (See Bilingual Targets.) For now, choose Swift.

6.    Make sure the Devices pop-up menu is set to iPhone. Again, this choice is not positively binding; but for now, let’s assume that our app is to run on iPhone only.

7.    Make sure Use Core Data is not checked. Click Next.

8.    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/iOS/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.

9.    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.

10.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 6. (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. At first, you’ll see a launch screen displaying the name of the app (Empty Window). This will then fade to show our app’s actual interface — 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). Alternatively, 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.swift 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)

symbol is a name, typically the name of a class or method. Among other things, this can be useful 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.

TIP

When the second filter bar is not highlighted, you are shown all symbols, including those defined by Swift and those defined by Cocoa (Figure 4-2). This is a great way to learn what object types exist, and to reach the headers where those types are declared (an important form of documentation: see Chapter 8).

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.

Below the search field, at the left, is the current search scope. This limits what files will be searched. Click it to see the Search Scopes panel. You can limit the search to a group (folder) within your project. You can also define a new scope: click New Scope to summon the scope configuration popover, where you can examine your options. Scopes are defined per user, not per project; scopes that you create here will appear in other projects.

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. Navigate (as you already know how to do, in at least three different ways) to the file AppDelegate.swift, and in the blank line after the last comment at the top of the file’s contents, above the import line, typehowdy. 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; save and build again, and your issues will be gone. If only real life were this easy!

Test navigator (Command-5)

This navigator 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 project has one test file containing a couple of test methods 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.swift, select in the line that says return true, 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.

The Debug navigator starts with four numeric and graphical displays of profiling information (CPU, Memory, Disk, and Network); 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 first button in the filter bar 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 (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.

Either the variables list or the console 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 Debug layout

Figure 6-4. The Debug layout

TIP

New in Xcode 6 is view debugging, which displays, and allows you to explore, your app’s view hierarchy. To switch to view debugging, choose Debug → View Debugging → Capture View Hierarchy (or click the Debug View Hierarchy button in the bar at the top of the Debug pane).

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 four 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 because it displays documentation (Chapter 8). The File inspector consists of multiple sections, each of which can be expanded or collapsed by clicking its header.

.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 lets you determine which variants of an image are listed, along with information about the selected image.

The view hierarchy is being debugged

In addition to the File inspector and Quick Help, the Object inspector describes the currently selected view, and the Size inspector displays the currently selected view’s size, position, and constraints.

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. You can navigate to related class files and header files (Superclasses, Subclasses, and Siblings; siblings are classes with the same superclass); 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 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 (“Show the Assistant editor”), 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.

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 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 configure the tracking behavior of an assistant pane, 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 selecting a category determines automatic tracking behavior. If a category has multiple files, a pair of arrow buttons appears at the right end of the assistant’s jump bar, with which you can navigate between them (or use the second jump bar component, Control-5). You can turn off tracking by setting the assistant’s first jump bar component to Manual.

TIP

If you want to close the assistant pane but continue to edit its contents without any assistant pane present, move the assistant pane’s contents to the main editor pane first (Navigate → Open In Primary Editor).

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 then 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.

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-}.

TIP

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, as well as 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-6).

The Project navigator and the project folder

Figure 6-6. 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-6). 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.swift, 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 WindowTests group corresponds to the Empty WindowTests folder on disk, and the file Empty_WindowTests.swift listed inside the Empty WindowTests group lives inside the Empty WindowTests 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. Things inside this group are real, however; you can see that the file Info.plist does exist on disk — it’s 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, Main.storyboard and LaunchScreen.xib, appear in the Finder inside a folder that doesn’t visibly correspond to anything in the Project navigator, called Base.lproj. This arrangement has 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; 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. You’ve seen it; you know that it has contents; you know that they bear some relationship to the Project navigator; now forget about them. 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 to the Project navigator. 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 Products group don’t correspond to anything in the project folder. Xcode generates a reference to the executable bundle generated by building each target in your project, and by convention these references appear in the Products group.

Another conventional group is the Frameworks group, listing frameworks on which your code depends. Your code does depend on some frameworks, but by default they are not listed in the Project navigator, and the Project navigator has no Frameworks group, because these frameworks are not explicitly linked into your build; instead, your code uses modules, which means that the import statements at the top of your files are sufficient to cause linkage to take place implicitly. However, if you were to link explicitly to a framework, it would be listed in the Project navigator, and you might then create a Frameworks group, just to give that listing a nice place to live. I’ll discuss frameworks later in this chapter.

The Target

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 (possibly more than one 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. 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, new in Xcode 6 and iOS 8, you can write a framework as part of your iOS app; with a custom framework, you can factor common code into a single locus, and you can configure its privacy details as a namespace. A custom framework needs to be built, so it, too, is a target. Also new to Xcode 6 and iOS 8 are application extensions, such as today extensions (content to appear in the notification center) and photo editing extensions (custom photo editing interface to appear in the Photos app). These, too, are targets.

The project name and the list of targets can appear in two ways (Figure 6-7): either as a column on the left side of the editor, or, if that column is collapsed to save space, as a pop-up menu at the top left of the editor. If, in the column or pop-up menu, you select the project, you edit the project; if you select a target, you edit the target. I’ll use those expressions a lot in later instructions.

Two ways of showing the project and targets

Figure 6-7. Two ways of showing the project and targets

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. 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.

The app target’s build phases

Figure 6-8. The app target’s build phases

Two of the build phases have contents. The meanings of these 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 .swift files; those are the code files that constitute the target. Sure enough, it currently contains ViewController.swift and AppDelegate.swift. If you add a new Swift file to your project (typically in order to declare another class), you’ll specify that it should be part of the app target, and it will automatically be added to the Compile Sources build phase.

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.

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 your launch nib file, LaunchScreen.xib, 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 in the catalog are written out to the top level of the app bundle, and the asset catalog itself is transformed into a .car file; 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.

It is possible that you might need to alter the Link Binary With Libraries build phase. Certain libraries, usually frameworks, are linked to the compiled code (now referred to, following compilation, as the binary), thus telling your code to expect those libraries to be present on the device when the app runs. Our Empty Window project does link to some frameworks, but it doesn’t use this build phase to do it; instead, it imports the frameworks as modules and the frameworks are linked automatically. However, in some cases you would need to link the binary with additional frameworks explicitly; I’ll talk about that later in this chapter.

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 reason to add 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, and for archiving the app to be submitted to the App Store.

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.

Configurations

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.

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.

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 an existing scheme, though it is not likely that you would need to do so. Another possibility is that you might create an additional scheme. One way to do this is by choosing Manage Schemes from the Scheme pop-up menu in the project window toolbar. Your schemes are all listed in this pop-up menu (Figure 6-13), and thus you can easily switch between them before you build and run.

The Scheme pop-up menu

Figure 6-13. The Scheme pop-up menu

Hierarchically appended to each scheme listed in the Scheme pop-up menu are the destinations. 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.

Each simulated device has a system version that is installed on that device. At the moment, all our simulated devices are running iOS 8.3; thus, there is no distinction to be drawn, and the system version is not shown. However, you can download additional SDKs (systems) in Xcode’s Downloads preference pane. If you do, and 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 as part of a Simulator destination name. For example, if your project’s deployment target (see Chapter 9) is 7.1, the Scheme pop-up menu in the project window toolbar might say “iOS 8.3” or “iOS 7.1” after the destination name. (I’ll discuss SDKs further at the end of this chapter.)

TIP

If you have downloaded additional SDKs, and if your app is configured to run on multiple systems, and you still don’t see, among the destinations, any simulated devices with those systems, choose Window → Device to summon the Devices window. This is where you manage what simulated devices exist. Here you can create, delete, and rename simulated devices, and specify whether a simulated device actually appears as a destination in the Scheme pop-up menu.

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.

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.swift and AppDelegate.swift into 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. (Later in this chapter, I’ll explain in detail what “begins to run” involves.)

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 required while 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 (insideBase.lproj) as Main.storyboard does in the project folder; as I said earlier, this folder structure has to do with localization (to be discussed in Chapter 9).

Assets.car and AppIcon60x60.png

An asset catalog and a pair of icon files. In preparation for this build, I added some icon images and some other image resources to the original asset catalog, Images.xcassets. This file has been processed (using the actool command-line tool), resulting in a compiled asset catalog file (.car) containing any images that have been added to the catalog. At the same time, the icon files have been written out to the top level of the app bundle, where the system can find them.

Info.plist

A configuration file in a strict text format (a property list file). It is derived from, but is not identical to, the project’s Info.plist. It contains instructions to the system about how to treat and launch the app. For example, the project’s Info.plist has a calculated bundle name derived from the product name, $(PRODUCT_NAME); in the built app’s Info.plist, this calculation has been performed, and the value reads Empty Window. Also, in conjunction with the asset catalog writing out our icon files to the app bundle’s top level, a setting has been added to the built app’s Info.plisttelling the system the name of those icon files.

Frameworks

Nine frameworks have been added to the built app. Our app uses Swift; these frameworks contain the entirety of the Swift language! Other frameworks used by our app are built into the system, but not Swift. This packaging of the Swift frameworks into the app bundle permits Apple to evolve the Swift language rapidly and independently of any system version, and allows Swift to be backward compatible to iOS 7. The downside is that these frameworks add about 5 MB to the size of our app; but this is a small price to pay for the power and flexibility of Swift. (Perhaps in the future, when the Swift language has settled down, it will be built into the system instead of the individual app, and Swift-based apps will become smaller.)

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 may 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, additional frameworks, or additional resources such as 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 section 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 file in the project does not have to be named Info.plist! 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 to Empty Window/Info.plist. (Take a look at the build settings and see!)

The property list file is a collection of key–value pairs. You can edit it, and you may 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 and edit 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. (But editing an Info.plist as raw XML is risky, because if you make a mistake you can invalidate the XML, causing things to break with no warning.)

§  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 are processed 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 will 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, consult 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. You prepare these nib files by editing a .storyboard or .xib file graphically in Xcode; in effect, you are designing some objects that you want instantiated when the app runs and the nib file loads.

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 single 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 file being listed in the app target’s Copy Bundle Resources build phase.

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.

I’ll talk more about the app launch process and the main storyboard later in this chapter. See Chapter 7 for more about editing .storyboard and .xib 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, ensuring 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

Destination

You should almost certainly check this checkbox (“Copy items if needed”). 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.

Added 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

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

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 the 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? 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 sets, to match the resolution of the screen. Because iOS 8 can run on single-resolution, double-resolution, and triple-resolution devices, you need up to three sizes of every image. In order to work properly with the framework’s image-loading methods, such resources employ a special naming convention: for example, listen.pnglisten@2x.png, and listen@3x.png. The resulting proliferation of image files in the Project navigator can be overwhelming and error-prone. Asset catalogs alleviate the problem.

Instead of tediously naming the multiple versions of listen.png manually as I add them to my project, I can let an asset catalog help me. 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 slots for three images at three different sizes. I drag the images into their proper slots. The names of the original image files don’t matter! The images are automatically 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. I can rename the placeholder to something more descriptive than Image — let’s call it listen. 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.

TIP

The entries in an asset catalog can be inspected by selecting an image and using the Attributes inspector (Command-Option-4). This shows the original name and (more important) the pixel size of the image.

Code Files 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 ViewController.swift and AppDelegate.swift. As development of your app proceeds, you will probably add further code files to the project, and you will ensure they are part of the target and thus that they, too, are listed in its Compile Sources build phase. What typically happens is that you want to add a new object type declaration to your code; you will often do so by adding a new file to the project, because this makes your object type declaration easier to find, and because Swift privacy depends upon code being isolated in individual files (Chapter 5).

When you choose File → New → File to create a new file, you can specify either the Cocoa Touch Class template or the Swift File template. The Swift File template is just a blank file. If your goal is to subclass a Cocoa class, the Cocoa Touch Class template will usually be more suitable; Xcode will write the initial class declaration for you, and in the case of some commonly subclassed superclasses such as UIViewController and UITableViewController, it provides stub declarations of some commonly implemented methods.

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 is the name of the binary; by default, the binary’s name comes from the EXECUTABLE_NAMEenvironment variable (here, “Empty Window”).

The entry point

The trickiest part of the app launch process is getting started. Having located and loaded the binary, the system must call into it. But where? If this app were an Objective-C program, the answer would be clear. Objective-C is C, so the entry point is the main function. Our project would have a main.m file containing the main function, like this:

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.

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

Our app, however, is a Swift program. It has no main function! Instead, Swift has a special attribute: @UIApplicationMain. If you look in the AppDelegate.swift file, you can see it, attached to the declaration of the AppDelegate class:

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {

This attribute essentially does everything that the Objective-C main.m file was doing: it creates an entry point that calls UIApplicationMain to get the app started.

Under certain circumstances, you might like to remove the @UIApplicationMain attribute and substitute a main file. You are free to do this. Your file can be an Objective-C file or a Swift file. Let’s say it’s to be a Swift file. You would create a main.swift file and make sure it is added to the app target. The name is crucial, because a file called main.swift gets a special dispensation: it is allowed to put executable code at the top level of the file. The file should contain essentially the Swift equivalent of the Objective-C call to UIApplicationMain, like this:

import UIKit

UIApplicationMain(

    Process.argc, Process.unsafeArgv, nil, NSStringFromClass(AppDelegate))

Why might you do that sort of thing? Presumably, it would be because you want to do other things in the main.swift file, or because you want to customize the call to UIApplicationMain.

UIApplicationMain

Regardless of whether you write your own main.swift file or you rely on the Swift @UIApplicationMain attribute, you are calling UIApplicationMain. This one function call is the primary thing your app does. Your entire app is really nothing but a single gigantic call toUIApplicationMain! Moreover, UIApplicationMain is responsible for solving some tricky problems as your app gets going. 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? Let’s pause to understand what UIApplicationMain does:

1.    UIApplicationMain creates your app’s first instance — the shared application instance, which subsequently is to be 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 this argument is nil, which will usually be the case, the default class is UIApplication. If, however, you needed to subclass UIApplication, you would specify that subclass here, by substituting an explicit value, such asNSStringFromClass(MyUIApplicationSubclass) (depending on what the subclass is called) as the third argument in the call to UIApplicationMain.

2.    UIApplicationMain also 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 manual version of main.swift, that specification is NSStringFromClass(AppDelegate). If we use the @UIApplicationMain attribute, that attribute is attached, by default, to the AppDelegate class declaration in AppDelegate.swift (which was created for us by the Single View Application template); the attribute means: “This is the application delegate class!”

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 (or storyboard entry point); 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 file defining this class, ViewController.swift, was also created by the template.

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

5.    UIApplicationMain now turns to the app delegate instance and starts calling some of its code — in particular, it calls application:didFinishLaunchingWithOptions: (or application:willFinishLaunchingWithOptions: if it exists). 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.

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.

App without a storyboard

In my description of the app launch process, I used several times the phrase “if there is a main storyboard.” In the Xcode 6 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 possible, though, notto 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 Single View Application template; let’s call it Truly Empty. Now follow these steps:

1.    Edit the target. In the General pane, select “Main” in the Main Interface field and delete it (and press Tab to set this change).

2.    Delete Main.storyboard and ViewController.swift from the project.

3.    Select and delete the entire code content of AppDelegate.swift.

You now have a project with an app target but no storyboard and no code! To make a minimal working app, you need to edit AppDelegate.swift in such a way as to recreate the AppDelegate class with just enough code to create and show the window, as shown in Example 6-1.

Example 6-1. An App Delegate class with no storyboard

import UIKit

@UIApplicationMain

class AppDelegate : UIResponder, UIApplicationDelegate {

    var window : UIWindow?

    func application(application: UIApplication,

        didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?)

        -> Bool {

            self.window = UIWindow(frame:UIScreen.mainScreen().bounds)

            self.window!.backgroundColor = UIColor.whiteColor()

            self.window!.makeKeyAndVisible()

            return true

    }

}

The result is a minimal working app with an empty white window; you can prove to yourself that your code is creating the window by changing its backgroundColor to something else (such as UIColor.redColor()) and running the app again.

This is a working app, but it’s kind of useless. It doesn’t do anything, and it can’t really do much of anything, because it doesn’t have a view controller. In fact, you will see a warning about that in the console: “Application windows are expected to have a root view controller at the end of application launch.”

We could silence the warning by making a generic view controller and making it the window’s root view controller. Just before the call to makeKeyAndVisible, insert this line:

self.window!.rootViewController = UIViewController()

This silences the warning, but the app remains more or less useless. What we need here is an instance of our own view controller — one that has a view that we can configure in a nib. So let’s now make a UIViewController subclass along with a .xib file containing its view:

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

2.    Name the class MyViewController and specify that it is a subclass of UIViewController. Check the “Also create XIB file” checkbox! Specify Swift as the language. 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 two files for us: MyViewController.swift, defining MyViewController as a subclass of UIViewController, and MyViewController.xib, the source of the nib from which a MyViewController instance will obtain its view.

4.    Now go back to the app delegate’s application:didFinishLaunchingWithOptions:, in AppDelegate.swift, and change the root view controller’s class to MyViewController, associating it with its nib, like this:

5.  self.window!.rootViewController =

    MyViewController(nibName:"MyViewController", bundle:nil)

We have now 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 MyViewController instance to fetch its view from the MyViewController nib, which has been compiled from MyViewController.xib; thus, we can use MyViewController.xib to customize the app’s initial interface. Besides illustrating explicitly what it is that UIApplicationMain does for you implicitly, this is also a perfectly reasonable way to construct an app.

Frameworks and SDKs

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 and run 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/iPhoneOS.sdk.

When your code runs on a device:

The code, as it starts running, looks in the device’s top-level /System/Library/Frameworks/ folder 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 rangeOfString:) 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, which your code can import. Thus, for example, your code can speak of NSString and can call rangeOfString: because it imports the NSString header. Actually, what your code imports is the UIKit header, which in turn imports the Foundation header, which in turn imports the NSString header. And you can see this happening at the top of any of your own code’s header files:

import UIKit

If you Command-click UIKit, you are taken to Swift’s rendering of the UIKit header. There at the top is import Foundation. If you look at the Foundation header and scroll down, you’ll see import Foundation.NSString. And if you look in the NSString header, you’ll see the declaration of the rangeOfString: method.

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 keyword, specifying either that framework or a framework that itself imports the desired framework. In Swift, you specify a framework by using its module name.

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 from 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.

However, it appears that our project does not do any explicit linking. If you look in the app target’s Link Binary With Libraries build phase, it is empty. This is because Swift uses modules, and modules can perform autolinking. In Objective-C, both those features are optional, and are governed by build settings. But in Swift, use of modules and autolinking is automatic.

Modules are cached information stored on your computer at Library/Developer/Xcode/DerivedData/ModuleCache. Merely opening a Swift project causes any imported modules to be cached here. If you drill down into the ModuleCache folder, you’ll see the modules for over a dozen frameworks and headers (.pcm files). Swift’s use of modules simplifies the importing and linking process, and improves compilation times.

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.

Moreover, it is sometimes necessary to link to a framework manually. For example, let’s say you want to use an MKMapView (Map Kit View) in your interface. You can configure this in a .storyboard or .xib file, but when you build and run your app, it crashes, complaining: “Could not instantiate class named MKMapView.” The reason is that the nib, as it loads, finds that it contains an MKMapView but doesn’t know what an MKMapView is. Adding import MapKit at the top of your code doesn’t solve the problem; you do need to do that if your code wants to talk to the MKMapView, but it doesn’t help the nib understand what an MKMapView is when it loads. The solution is to link to the MapKit framework manually:

1.    Edit the target and look at the Build Phases pane.

2.    Under Link Binary With Libraries, click the Plus button.

3.    A list of available frameworks appears (along with dynamic libraries). Scroll down to MapKit.framework, select it, and click Add.

This solves the problem; your app now builds and runs.

New in iOS 8 and Xcode 6, you can also create your own framework as part of your project. A framework is a module, so this can be a useful way to structure your code, as I described in Chapter 5 when discussing Swift privacy. To make a new framework:

1.    Edit the target and choose Editor → Add Target.

2.    On the left of the dialog, under iOS, select Framework & Library; on the right, select Cocoa Touch Framework. Click Next.

3.    Give your framework a name; let’s call it Coolness. You can pick a language, but I’m not sure this makes any difference, as no code files will be created. The Project and Embed pop-up menus should be correctly set by default. Click Finish.

A new Coolness framework target is created in your project, along with a corresponding test target. If you now add a .swift file to the Coolness target, and inside it define an object type and declare it public, and if you import Coolness back in one of your main app target’s files, such asAppDelegate.swift, that file will be able to see the public members of the Coolness framework.

WARNING

Embedded frameworks of this kind are not backward-compatible to iOS 7.

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! The project name is not a name that users will ever see — it is not, for example, the name that appears visibly associated with the app on the device — and you are free to change it.

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 app target, the test target, the built app, the label in the launch screen — and, by implication, various relevant build settings. You should feel free to accept.

WARNING

Because of a bug in Xcode, renaming your project can break your test target, thus preventing your app from building. See Unit Testing.

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. It’s fine to change the group name, but beginners should not change the name of that folder on disk, as it 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.

Bilingual Targets

It is legal for a target to be a bilingual target — one and the same target that contains both Swift files and Objective-C files. A bilingual target can be useful for various reasons. Here are some reasons that have arisen in my own real programming life:

Swift is limited

The API might contain a method or function that you need to call, or a class or struct that you need to use, that Swift cannot deal with. So you need to switch momentarily into Objective-C.

Swift is slow

You may find that, for certain intensive repeated operations, Swift is unacceptably slow, whereas the same code written in Objective-C is fast.

Swift is new

Perhaps you already have an app written in Objective-C, and you want to migrate it into Swift. You are unlikely to want to rewrite all the code from scratch simultaneously. More probably, you will rewrite just one file (class) at a time. During this rewriting process, you’ll have a bilingual target. A common variant of this situation is: Your code is written in Swift, but a third-party library that you want to use is written in Objective-C.

Bilingual targets are not difficult to use. Communication between the two languages is generally straightforward (see Appendix A). The key question is how Swift and Objective-C hear about one another’s code in the first place.

Recall that Objective-C, unlike Swift, has a visibility problem already: Objective-C files cannot automatically see one another. Instead, each Objective-C file that needs to see another Objective-C file must be instructed explicitly to see that file, usually with an #import directive at the top of the first file. In order to prevent unwanted exposure of private information, an Objective-C class declaration is conventionally spread over two files: a header file (.h) containing the @interface section, and a code file (.m) containing the @implementation section. Also conventionally, only .h files are ever imported. Thus, if declarations of class members, constants, and so forth are to be public, they are placed in a .h file.

Visibility of Swift and Objective-C to one another depends upon this convention: it works through .h files. There are two directions of visibility, and they must be considered separately:

How Swift sees Objective-C

When you add a Swift file to an Objective-C target, or an Objective-C file to a Swift target, Xcode offers to create a bridging header. This is a .h file in the project. Its default name is derived from the target name — for example, Empty Window-Bridging-Header.h — but the name is arbitrary and can be changed, provided you change the target’s Objective-C Bridging Header build setting to match. (Similarly, if you decline the bridging header and you decide later that you want one, create a .h file manually and point to it in the app target’s Objective-C Bridging Header build setting.) An Objective-C .h file will then be visible to Swift provided you #import it in this bridging header.

How Objective-C sees Swift

If you have a bridging header, then when you build your target, the appropriate top-level declarations of all your Swift files are automatically translated into Objective-C and are used to construct a hidden bridging header inside the Intermediates build folder for this target, deep inside your DerivedData folder. The easiest way to see this is with the following Terminal command:

$ find ~/Library/Developer/Xcode/DerivedData -name "*Swift.h"

This will reveal the name of the hidden bridging header. For example, for our Empty Window project, the hidden bridging header for the app target is called Empty_Window-Swift.h; note that the space in the target name has been translated into an underscore. Alternatively, examine (or change) the target’s Product Module Name build setting; the hidden bridging header’s name is derived from this. Your Objective-C files will be able to see your Swift declarations, provided you #import this hidden bridging header into each Objective-C file that needs to see it.

For simplicity, I will refer to these two bridging headers as the visible and invisible bridging headers, respectively.

For example, let’s say that I’ve added to my Empty Window app target a Thing class written in Objective-C. It is distributed over two files, Thing.h and Thing.m. Then:

§  For Swift code to see the Thing class, I need to #import "Thing.h" in the visible bridging header (Empty Window-Bridging-Header.h).

§  For Thing class code to see my Swift declarations, I need to import the invisible bridging header (#import "Empty_Window-Swift.h") at the top of Thing.m.

Exactly where at the top of a .m file you #import the invisible bridging header can make a difference. The usual sign of trouble is that you get an “Unknown type name” compile error, where the “unknown” type is a class declared in Objective-C. You get this error even though you areimporting the .h file containing this class declaration in the visible bridging header, thus making it known to Swift. The solution is to #import the .h file containing the declaration for the “unknown” type in your Objective-C files as well, before you #import the invisible bridging header.

For example, if my Empty Window bilingual target suddenly started generating a compile error complaining of an unknown type name Thing, I would find all the Objective-C files where I say #import "Empty_Window-Swift.h" and say #import "Thing.h" before that line. Having to do this can be an annoyance, especially if the Objective-C file in question has no need to know about Thing, but it resolves the issue and allows compilation to proceed.

Having translated numerous iOS apps from Objective-C to Swift, I will end by outlining my procedure:

1.    Pick a .m file to be translated into Swift. Objective-C cannot subclass a Swift class, so if you have defined both a class and its subclass in Objective-C, start with the subclass. Leave the app delegate class for last.

2.    Remove that .m file from the target. To do so, select the .m file and use the File inspector.

3.    In every Objective-C file that #imports the corresponding .h file, remove that #import statement and import in its place the invisible bridging header. (If you’re already importing the invisible bridging header in this file, you don’t need to import it again.)

4.    If you were importing the corresponding .h file in the visible bridging header, remove the #import statement.

5.    Create the .swift file for this class. Make sure it is added to the target.

6.    In the .swift file, declare the class and provide stub declarations for all members that were being made public in the .h file. If this class needs to adopt Cocoa protocols, adopt them; you may have to provide stub declarations of required protocol methods as well. If this file needs to refer to any other classes that your target still declares in Objective-C, import their .h files in the visible bridging header.

7.    The project should now compile! It doesn’t work, of course, because you have not written any real code in the .swift file. But who cares about that? Time for a beer!

8.    Now fill out the code in the .swift file. My technique is to translate more or less line-by-line from the original Objective-C code, even though the outcome is not particularly idiomatic (Swifty).

9.    When the code for this .m file is completely translated into Swift, build and run and test. If the runtime complains (probably accompanied by crashing) that it can’t find this class, find all references to it in the nib editor and reenter the class’s name in the Identity inspector (and press Tab to set the change). Save and try again.

10.On to the next .m file! Repeat all of the above steps.

11.When all of the other files have been translated, translate the app delegate class. At this point, if there are no Objective-C files left in the target, you can delete the main.m file (replacing it with a @UIApplicationMain attribute in the app delegate class declaration) and the .pch(precompiled header) file.

Your app should now run, and is now written in pure Swift (or is, at least, as pure as you intend to make it). Now go back and think about the code, making it more Swifty and idiomatic. You may well find that things that were clumsy or tricky in Objective-C can be made much neater and clearer in Swift.