iOS Programming Basics
Creating mobile apps for both iOS and Android is fun and rewarding. With Xcode in place, you are ready to write code, build, and run iOS apps now. Objective-C had been the primary programming language for iOS apps until Swift was officially announced at the 2014 Apple Worldwide Developers Conference. If you’re just starting to learn iOS programming, you should go with Swift because there is no reason to choose the old way and miss the latest and great features. Your next steps should be learning the fundamentals of the following:
The purpose of this chapter is to get you comfortable with reading the Swift code in this book. To achieve this goal, you will be creating a HelloSwift project while learning Swift programming language highlights.
You will create another Xcode iOS project in the second part of the chapter. All iOS apps have a user interface (UI). You normally start by creating the UI using the most important Xcode tool, Storyboard Editor, which draws the UI widgets and components and connects them to your code. You also will see the typical iOS project structures and components while creating this iOS app. You may not need to understand everything about the iOS framework in the beginning, but the first storyboard lesson should be “just enough” for you to feel the different programming paradigm. Later, the materials in Chapters 3 and 4 continue with step-by-step instructions for common programming tasks and framework topics. Follow these mapping instructions, and the ideas will more easily stick with you as you get a broader picture of the whole app.
The Swift Language in a Nutshell
Swift, the newest programming language for creating iOS apps, has many similar rules and aspects of language syntax to Java. I am very confident that learning the Swift language won’t be the highest hurdle for you; Java or C# developers will pick up Swift code naturally. Just to give you a quick preview, Table 2-1 depicts a quick Java-to-Swift comparison:
Table 2-1. Java-to-Swift Language Syntax Comparison in an Absolute Nutshell
Swift also defines file and the module access control: private, public, and internal. Although they have different meanings from their Java counterparts, if you define each class in each own file, the private/public access controls can be used the same way. The default internal access control is also public to any file in the same project, but is not visible when being imported to other projects. The Swift internal control is more useful for creating framework projects as opposed to app modules.
HelloSwift with Xcode
Instead of my describing the uses and syntax rules in a formal way, you are going to create a HelloSwift Xcode project and write the code listing from Table 2-1 yourself. You will also perform the following common Xcode programming tasks: create a class, build and run a project, and use the debugger.
Create a Swift Command-Line Project
Let’s create a command-line Swift program, because it is really simple and you can focus on the Swift language subjects without being sidetracked by other questions.
Follow these instructions to proceed:
Figure 2-1. Choosing an Xcode template
The HelloSwift project appears in the Project Navigator area (see Figure 2-2).
Figure 2-2. Creating the HelloSwift project
The command-line template creates the main.swift file for you. This is the entry point of the program, just like the main(...) in Java. You will be writing code in main.swift to demonstrate common object-oriented code.
Figure 2-2 shows that the typical Xcode workspace contains three areas from left to right and a top toolbar. Inside each area, there are subviews that you may switch to using the selector bars.
Click on any of the icons on the selector bars, or hover your mouse over the pointer in Figure 2-2, to see the hover text tips in the workspace, to get yourself familiar with Xcode workspace. The subviews appear more condensed than those in Eclipse, but essentially Xcode is a tool for the same purpose: editing project files and compiling, building, debugging, and running the executables. You will use it repeatedly throughout the book.
Create a Swift Class
To create a new Swift class, you can create it in the existing main.swift file, or follow the Java convention to create it in its own file as shown in the following steps:
Figure 2-3. Create a Swift class from the folder context menu
Listing 2-1. Declare MobileDeveloper Class
class MobileDeveloper {
}
Note Unlike Java, a Swift class doesn’t implicitly inherit from any class. It can be the base class on its own.
Listing 2-2. Stored Property in Swift
class MobileDeveloper {
var name = "" // var type is String inferred by the value
}
Note Semicolon (;) is optional for terminating a statement.
Create a Swift Protocol
JAVA ANALOGY
The Java interface defines object obligations.
In object-oriented programming (OOP), it is important to define a set of behaviors that are expected of certain objects. In Java, you declare an interface; in Swift, you declare a protocol.
Create a Swift protocol called Programmer by doing the following:
Listing 2-3. Declare the Programmer Protocol
protocol Programmer {
func writeCode(arg: String) -> Void
}
Implement the Protocol
JAVA ANALOGY
Implement a Java interface.
To conform to the expected behavior defined in a Swift protocol, the tagged class must implement the methods defined in the protocol. To make the MobileDeveloper class implement the Programmer protocol, do the following:
Listing 2-4. Conform to MobileDeveloper Protocol
class MobileDeveloper : Programmer {
...
}
Note If the Swift class already has a superclass, list the superclass name before any protocols it adopts, followed by a comma (,)—for example, class MobileDeveloper : Person, Programmer
Listing 2-5. Method Body
class MobileDeveloper: Programmer {
...
func writeCode(arg: String) -> Void {
println("(self.name) wrote: Hello, (arg)")
}
}
Note (self.name) is evaluated first inside the quoted String literal.
Use the Swift Instance
JAVA ANALOGY
Programmer you = new MobileDeveloper(); you.setName("You");
you.writeCode("Java");
You have created a Swift MobileDeveloper class and implemented the Programmer obligations, in pretty much the same way you normally do in Java except with minor syntax differences. To use the class, it is the same as Java in principle, calling a method defined in the receiver from the sender. Modify HelloSwift/main.swift as shown in Listing 2-6.
Listing 2-6. Swift Entry main.swift
var you = MobileDeveloper()
you.name = "You";
you.writeCode("Java");
Xcode Debugger
Knowing how to use the debugger when creating software can make a big difference in your productivity. Do the following to see the common debugging tasks in the Xcode debugger:
Figure 2-4. Breakpoint
Note To turn on line numbers in Xcode editors, go to the Xcode top menu bar and select Xcode Preferences... Text Editing Show Line Numbers. There are other handy settings there that you may want to look at (e.g., shortcut keys are defined under Key Binding).
Figure 2-5. Xcode debugging
Stack Trace, Variables Inspector, Output Console, and the Debug toolbar have a similar look and feel in most IDEs, including Xcode and Eclipse.
This completes your HelloSwift application exercise. As you follow through the iOS projects in this book, you will discover more productivity tips in the Xcode IDE.
More About the Swift Language
A lot of Java syntax and conventional coding approaches would work just fine in Swift. However, Swift does have some pretty neat features of its own, so it’s worth taking a quick look at them now.
To go through this section, it is best to use the new Xcode feature called Playgrounds. Launch Xcode and select Get Started with a playground. You can write any code snippets you want and see the result or syntax errors immediately. Figure 2-6 depicts the Playground: you write code in the left panel and the right panel renders the result immediately.
Figure 2-6. Xcode Playground
JAVA ANALOGY
Java scratchpad.
Variables and Constants
You declare a variable using the var keyword, and you use let to declare constants. While Java variables are always defined within the enclosing brackets, Swift variables are global if defined not within enclosing brackets. The following code snippet (see Listing 2-7) depicts usage of Swift variables:
Listing 2-7. Common variables usages
var GlobalVar : String = "Global Variable"; // global scope
class MyClass {
var mProperty : String = ""; // class scope
let mConstant : Int = 0; // constant
func myMethod(arg : String) {
var aVar : String = ""; // local variable in method scope
let aConstant = 1;
}
}
Type Safety and Type Inference
Both Java and Swift are type-safe languages. Any variable must be declared with a type; the compiler will help flag any mismatched types. In Swift, if the type can be inferred by its value, you don’t need to explicitly declare the type. Listing 2-8 is essentially exactly the same as Listing 2-7. The Swift type inference feature encourages developers to assign initial values that reduce the common errors from uninitialized data.
Listing 2-8. Common type inference usages
var GlobalVar = "Global Variable"
class MyClass {
var mProperty = ""
let mConstant = 0
func myMethod(arg : String) {
var aVar = "";
let aConstant = 1
}
}
Optional Variable
The optional variables are declared with the type and a postfixed question mark (?), called optional type. This indicates the value it contains may be absent (nil, equivalent to null in Java) for the intended type. For example, Listing 2-9 depicts the difference in Swift and Java for converting a string to an integer.
Listing 2-9. Optional Type in Swift vs. Handle Exception in Java
////// Java NumberFormatException
String intStr = "123"; // or "xYz"
int myInt;
try {
myInt = Integer.parseInt("234");
} catch (NumberFormatException e) {
myInt = Integer.MAX_VALUE;
}
////// Swift Optional Int
var intStr = "123"
var myInt : Int? = intStr.toInt() // myInt can be nil
Optionals make the Swift language more type safe and more robust by encouraging developers to understand whether the variable can be absent. Listing 2-10 demonstrates two practical Swift optional usages:
Listing 2-10. Swift Optional Int
var intStr = "123"
var myOptionalInt : Int? = intStr.toInt() // Optional Int
if myOptionalInt != nil {
var myInt = myOptionalInt! // Unwrap Int? to Int
println("unwrapped Int: (myInt)")
}
// optional binding used in if and while local scope.
if var myInt = intStr.toInt() {
// myInt is auto unwrapped
println("unwrapped and local scope: (myInt)")
}
Implicitly Unwrapped Optionals
For the situations where a variable will always have a value after the value is set, you declare the variable as Implicitly Unwrapped Optional postfixed with ! instead of ?. For example: var delegate: MyDelegate!
Any of the Optionals usages described earlier are applicable here. You treat it as Optionals but you don’t need to force unwrapping it. You commonly see this usage in the iOS framework for properties that are initialized somewhere else (i.e., by the caller). Particularly, iOS frameworks embed delegate properties everywhere. These delegates are declared as Implicitly Unwrapped Optionals but their values are typically assigned by the caller. As another example, UI widgets are normally drawn in the Storyboard editor and connect to your code as IBOutlet properties. These IBOutlet properties are declared as Implicitly Unwrapped Optionals. I just wanted to give you a quick heads up for now because you will see these usages frequently later in this book.
Tuples
Tuples group multiple values into a single compound value. This seems to have been the useful feature that Java developers (as well as those using C#, Objective-C, C/C++, etc.) have been looking for. For example, you can pass or return a value without creating a class or a struct (structs are also supported in Swift). Listing 2-11 shows the most common tuple usages.
Listing 2-11. Common Tuple Usages
var xyz = (x: 0, y: 0, z: 0)
println("xyz (xyz) x is: (xyz.x) y is: (xyz.y) z is: (xyz.z)")
// or decompose tuples
var xy : (Int, Int) = (1, 1) // or simply var xy = (1, 1)
var (a, b) = xy
println("xy (xy) x is: (a) y is: (b)")
func httpResponse() -> (rc: Int, status: String) {
return (200, "OK")
}
var resp = httpResponse()
println("resp is: (resp.rc) (resp.status)")
Collections
JAVA ANALOGY
java.lang.ArrayList and HashMap.
Array and Dictionary are the two Swift collections. Listing 2-12 shows the common usages, which include the following:
Listing 2-12. Common Array and Dictionary usages
// collections
var emptyArray = Array<String>() // or [String]()
var emptyDict = Dictionary<Int, String>() // [Int: String]()
var colors = ["red", "green", "blue"]
var colorDictionary = ["r" : "red", "g" : "green", "b" : "blue"]
colors.append("alpha") // or: colors += "alpha"
colorDictionary["a"] = colors[3]
colors.insert("pink", atIndex: 2)
colors.removeAtIndex(2)
println(colors.isEmpty ? "empty" : "(colors.count)")
Constant Dictionary or Array is immutable, meaning it is not allowed to add or remove elements, nor modify existing items. On the other hand, variable Dictionary or Array is mutable.
Control Flow
Similar to Java, in Swift you use if and switch to make conditionals, and use for-in, for, while, and do-while to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required. Listing 2-13 demonstrates the following common control flow usages:
Listing 2-13. Control Flows
for (var idx = 0; idx < 10; idx++) { // optional parenthesis
println("for-loop: (idx)")
}
for item in [1,2,3,4,5] { // or for item in 1...5
println("for-in: (item)")
}
for c in "HelloSwift" { // loop thru characters
print(c)
}
for (key, value) in colorDictionary {
println("for-in dictionary:(key) - (value)")
}
// while-loop, or do-while that run at least once
var idx = 0
while idx < colors.count {
println("while-loop: (colors[idx])")
idx++
}
Switch
In Swift, the switch cases can be any types in addition to int primitive data. Listing 2-14 shows the following improved control flow switch usages in Swift:
Listing 2-14. Improved Switch
var condition = "red"
switch condition {
case "red", "green", "blue": // combined cases
println("(condition) is a prime color")
// always break implicitly (no follow thru)
case "RED", "GREEN", "BLUE":
println("(condition) is a prime color in uppercase")
default: // not optional anymore
println("(condition) is not prime color")
}
var range = 9 // by range
switch range {
case 0:
println("zero")
case 0...9:
println("one-digit number")
case 10...99:
println("two-digit number")
case 10...999: // first hit first
println("three-digit number")
default:
println("four or more digits")
}
var coord = (0, 1)
switch coord { // by tuples
case (0...Int.max, 0...Int.max):
println("1st quad")
case (Int.min...0, 0...Int.max):
println("2nd quad")
case (Int.min...0, Int.min...0):
println("3rd quad")
case (0...Int.max, Int.min...0):
println("4th quad")
default:
println("on axis")
}
A switch can bind the matched value within its local scope. You can specify a where clause to test the condition, too. Listing 2-15 demonstrates both value bindings.
Listing 2-15. Temporary Value Binding and Using where Clause
var rect = (10, 10)
switch rect {
case let (w, h) where w == h:
println("((w, h)) is a square")
default:
println("rectangle but not square")
}
Enumerations
You use enum to define a common type for a group of related values. After the enumeration is defined, you use it just as a Swift type that is type safe. Listing 2-16 shows the following common enum usages:
Listing 2-16. Common Enum usages
enum DayOfWeek { // raw value is optional
case SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
var aDay = DayOfWeek.SUNDAY
switch aDay {
case DayOfWeek.SATURDAY, DayOfWeek.SUNDAY:
println("(aDay) is weekend")
default:
println("(aDay) is weekday")
}
enum DayOfWeek2 : String { // assign raw value
case SUNDAY = "Sun", MONDAY = "Mon", TUESDAY = "Tue",
WEDNESDAY = "Wed", THURSDAY = "Thu", FRIDAY = "Fri", SATURDAY = "Sat"
}
var aDay2 = DayOfWeek2.SUNDAY
switch aDay2 {
case DayOfWeek2.SATURDAY, DayOfWeek2.SUNDAY:
println("(aDay2.rawValue) is weekend")
default:
println("(aDay2.rawValue) is weekday")
}
// associated values
enum Color {
case RGB(Int, Int, Int)
case HSB(Float, Float, Float)
}
var aColor = Color.RGB(255, 0, 0)
switch aColor {
case var Color.RGB(r, g, b):
println("R: (r) G: (g) B: (b) ")
default:
println("")
}
Functions
Swift functions are declared with the func keyword. Unlike Java methods, the parameter and return type are declared after the type name. Here are the typical Swift function usages you most likely will encounter (see Listing 2-17):
Listing 2-17. Function Usages
func doWork(parm : String) -> String { // simple form
return "TODO: " + parm
}
println(doWork("Swift"))
// External parameter names is part of the func signature
func doWork2(name parm : String) -> String {
// arg = arg.uppercaseString; // error: constant parm
return "TODO: " + parm
}
println(doWork2(name: "Swift"))
// use # to indicate same name for internal and external
func doWork3(#name: String) -> String {
return "TODO: " + name
}
println(doWork3(name: "Swift"))
// With default parm value, it also implies #, same external name
func doWork4(name: String = "Swift") -> String {
return "TODO: " + name
}
println(doWork4()) // default parm value
// parm is constant by default unless declaring it with var
func doWork5(var name: String = "Swift") -> String {
name = name.uppercaseString;
return "TODO: " + name;
}
// variadic parms
func sumNumbers(parms : Int...) -> Int {
var sum = 0
for number in parms {
sum += number
}
return sum
}
println(sumNumbers(2,5,8))
// func is a type, can be used for parm or return type.
func separateByAnd(p1: String, p2: String) -> String {
return p1 + " and " + p2
}
func printTwoString(p1: String, p2: String, format: (String, String)->String) {
println(format(p1, p2))
}
printTwoString("Horse", "Carrot", separateByAnd)
// closure expression is unnamed func
printTwoString("Horse", "Carrot",
{(p1: String, p2: String) -> String in
return p1 + " and " + p2
})
printTwoString("Horse", "Carrot",
{p1, p2 in // type inferences
return p1 + " and " + p2
})
printTwoString("Horse", "Carrot",
{ // Inference and shorthanded parm names, $0, $1 ...
return $0 + " and " + $1
})
You may get by with not using most of the shorthand options at first, but for iOS programming, you definitely will need to get used to the external parameter names, because they are used heavily in iOS frameworks.
Class
Swift classes contain similar elements to Java classes. The simplest Java-like class form can be depicted as shown in Listing 2-18.
Listing 2-18. Simple Java-Like Swift Class
class SimpleClass {
var mProperty : Int = 0 // public int mProperty ...
var mConstant : String = "MyKey" // public String ...
func myMethod(name: String) -> Void { println(name)}
}
Property
Java fields can be mapped to Swift properties as shown previously in Listing 2-18; they are called stored properties. In Swift, properties can do more than Java fields. Listing 2-19 demonstrates the following Swift property usages:
Listing 2-19. Swift Class Property
class MyClass {
var width = 10, height = 10 // stored properties
// computed properties, can have set as well
var size : Int {
get {
return width * height
}
}
var size2 : Int { // readonly, shorthanded
return width * height
}
// property observer
var depth : Int = 10 {
willSet {
println("depth ((depth)) will be set to (newValue)")
}
didSet {
println("depth ((depth)) was be set from (oldValue)")
}
}
// Swift class Type property,
class var MyComputedTypeProperty : String {
return "Only computed Type property is supported"
}
// use inner struct stored Type property as a workaround
struct MyStatic {
static let MyConst = "final static in Java"
static var MyVar: String?
}
}
println(MyClass.MyStatic.MyConst)
MyClass.MyStatic.MyVar = "class var in Java"
println(MyClass.MyStatic.MyVar)
Method
Methods are functions defined inside a type context (i.e., a class). They are still functions as described previously (see Listing 2-17). You can define instance methods or class methods just as Java does. Listing 2-20 shows the following typical method usages:
Listing 2-20. Common Method Usages
class MyClass {
///////// methods copied from Listing 2-17 //////////
func doWork(parm : String) -> String { // just like func
return "TODO: " + parm
}
// default parm value, always imply externl parm name
func doWork2(name: String = "Swift") -> String {
return "TODO: " + name
}
// func is a type, can be used for parm or return type, etc.
func separateByAnd(p1: String, p2: String) -> String {
return p1 + " and " + p2
}
func printTwoString(p1:String, p2:String, format:(String, String)->String) {
println(format(p1, p2))
}
// Type methods, aka Java class static method
class func DoWork(parm : String) -> String {
return "TODO: (Just like Java class static method)" + parm
}
}
var c = MyClass()
println(c.doWork("Swift"))
println(c.doWork2()) // default parm value
println(c.doWork2(name: "Swift")) // external name enforced
// closure is unnamed func
c.printTwoString("Horse", p2: "Carrot", format: c.separateByAnd)
// Inference and shorthanded parm names apply to method, too.
c.printTwoString("Apple", p2: "Orange", format: {
return $0 + " and " + $1
})
MyClass.DoWork("Swift Type method")
Reference Type vs. Value Types
Just as in Java, reference types are passed by reference (the reference to the instance is copied to another variable) and values types are passed by copy (the whole value type instance is copied to another memory space). However, you do need to pay attention to certain differences:
Most GUI apps are composed of more than programming source code; for example, a typical iOS project contains Swift or Objective-C source code, libraries, storyboard files, images or multimedia non-code application resources, an application-information property Info.plist file, and so forth. Xcode compiles and builds the whole project and bundles all the artifacts required for an app into an archive file with an .app file extension, and signs the .app file with the appropriate signing certificate.
Let’s translate a simple HelloMobile ADT project to Xcode so that you can visualize these software artifacts in a typical iOS app. The Android app in Figure 2-7 was created using the ADT Create Android Project template:
Figure 2-7. HelloMobile, Android version
To create the HelloMobile iOS app, start Xcode and proceed with the following steps:
Figure 2-8. Single View Application template
The bare-bones HelloMobile iOS project is created and it now appears in the Xcode Project Navigator area (see Figure 2-9).
Figure 2-9. HelloMobile project
It is immediately runnable. Let’s examine the typical iOS software artifacts in an Xcode project, which comprise the iOS app:
a. AppDelegate.swift: Each iOS app must have one AppDelegate class. Similar to android.app.Application, you don’t need to modify this file if your program doesn’t need to track the global application state.
b. ViewController.swift: There is a ViewController class paired with the content view. The intended purpose is the same as the Android Fragment class in an Android project: the content view controller for the content view.
a. You commonly create one storyboard scene per content view and use only one storyboard file for all content views so you can visually implement the linkages among them.
b. Developers should provide different assets for each device configuration. This is done for the same purpose as providing alternative resources in Android.
a. PNG and JPEG image formats are both supported as of this writing. Using the assets catalog is not a must. You may drop any resource files into the Xcode project. (You may want to create a folder to organize them yourself.)
a. The best Android analogy is the AndroidManifest.xml file, but not exactly. You may glance through this file to get a feel for the configurations and settings that Xcode needs to know about the app. Xcode initially creates it in XML format, which you can edit directly.
a. In Xcode, the folder can be a actual folder of a light-blue color (e.g., the Images.xcassets folder).
b. Folders in Xcode can also be just a tag, called a group, with a yellow color (e.g., HelloMobile, Supporting Files, etc.). Their actual location could be in any of the physical subfolders, but you should not care.
a. For this simple project, you don’t need to modify anything. But you should glance through the editor to get a quick idea of what Xcode requires to compile and build the executable.
The iOS app is not completed yet, but it has everything a typical iOS app should have.
Xcode Storyboard
ANDROID ANALOGY
There is no storyboard-like feature in ADT. You use the Graphic Layout Editor to create one content view in one layout file at a time.
Use the Xcode Storyboarding feature to visually compose the UI for your app. As its name implies, not only does it create individual screens and UI widgets, but it also lets you compose the whole app as one storyboard. Since iOS apps are all GUI apps, this tool will greatly determine your productivity in creating iOS apps through the following actions:
There are other subviews in the Utility area that you can select from the top selector tool bar. All of them are important; you should take a moment to get familiar with them (see Figure 2-10):
Figure 2-10. Select UI components from Object Library
Now is the time for you to get familiar with Xcode Storyboarding feature. The iOS HelloMobile project doesn’t look like the counterpart Android app yet; it only has one screen in the Main.storyboard file, which is empty. The counterpart Android layout file has three UI widgets: EditText, TextView, and a hello Button.
First you will Implement the user interface of the HelloMobile iOS app. Xcode storyboard provides everything you need for this mission.
Object Library and Attributes Inspector
ANDROID ANALOGY
Drag and drop the UI widgets from Palette View in the ADT Graphical Layout Editor and set the widget attributes in the layout file.
You need to add three UI widgets to the content view, just as in the counterpart Android app, using the following steps:
Figure 2-11. Disable Size Classes in File Inspector
Tip The iOS widgets you need are called TextField, Label, and Button.
Figure 2-12. Guide lines
Figure 2-13. Three simple UI widgets
Figure 2-14. Three simple widgets added to storyboard
The look and feel in portrait mode is close enough for our purposes: using a storyboard to visually compose the content view without writing a single line of code. The iOS HelloMobile is not completed yet: the Hello ... button doesn’t read “Hello...”, and the landscape mode is not acceptable yet. Both are important topics and have their own sections in Chapter 3.
In this exercise, I just wanted to give you a quick look of the Xcode storyboard editor. The Xcode workspace seems quite different from Eclipse. Spend a moment to get familiar with the Xcode workspace, including the storyboard editor, the Utility area, selector tool bar, and so forth. Xcode storyboarding is a very important tool that will greatly influence your productivity when creating iOS apps.
Summary
On the surface, you actually learned about a lot of things in this chapter. You started with a discussion of Swift–Java language comparisons to learn their similarities, then you went over Swift language topics to highlight the new language features. However, the rest of the book will focus on iOS programming instead of the Swift language. The code will address readability as opposed to being concise using new tricks. You surely won’t have a problem reading all the Swift code in the rest of the book. Sooner or later, though, you will need the reference document to the Swift programming language, which is available free in iTunes (https://itunes.apple.com/us/book/swift-programming-language/id881256329).
You created a HelloMobile iOS project using Xcode to visualize a typical iOS application structure so you can visualize. You also got your first taste of the Xcode storyboard, which is very important and which you will use for every iOS app, including all the sample projects in this book—so plan on revisiting the storyboard repeatedly.