We’ve explored the notion of classes, methods, and instance variables, but you probably still don’t have a real idea of how to go about making a program do something. So, this section reviews several key programming tasks that you’ll be using to implement your methods:
Declaring variables and constants
Understanding built-in swift data types
Making sense of optional values
Initializing objects
Using an object’s instance methods
Making decisions with expressions
Branching and looping
Earlier we documented what variable properties will look like in your Swift files, but we didn’t really get into the process of how you declare them (or use them). Nor did we talk about variables within methods!
Whatever the purpose, you declare your variables using this syntax:
Click here to view code image
var
Holy cow—that looks complicated! In practice, nearly everything but the var
keyword and the variable name are optional. I make a point of always trying to provide the variable type, but if you don’t, Swift will try to figure it out for you. The type is either a Swift data type or the name of a class that you want to instantiate and use.
Let’s begin by taking a look at a few Swift data types and how they are declared and used.
Swift includes a number of data types that enable you to work with common types of information. Most basic logic that we implement in this book will take advantage of one of these data types:
Int: Integers (whole numbers such as 1, 0, and –99).
Float: Floating-point numbers (numbers with decimal points in them).
Double: Highly precise floating-point numbers that can handle a large number of digits.
String: Collections of characters (numbers, letters, and symbols). Throughout this book, you’ll often use strings to collect user input and to create and format user output.
Bool: A Boolean value (that is, true
or false
), often used to evaluate conditions within your code.
Arrays: A collection of ordered values that are accessed via a numeric index.
Dictionaries: A collection of key/value pairs. A given value is accessed by providing its key.
Let’s start with something easy: integers (Int
) and floating-point numbers (Float
or Double
). To declare an integer variable that will hold a user’s age, you might enter the following:
var userAge: Int
If you wanted, you could even initialize it with a value, all in the same line:
var userAge: Int = 30
After a variable is declared, it can be used for assignments and mathematical operations. The following code, for example, declares two variables, userAge
and userAgeInDays
, and uses the first (an age in years) to calculate the second (the age in days):
var userAge: Int = 30
var userAgeInDays: Int
userAgeInDays = userAge * 365
Notice that for the userAgeInDays
, I declare it, then use it later. You’re welcome to do this, or declare and initialize the variables on the exact same line.
Floating-point numbers work the same way—you declare them, then use them. A simple example to calculate the circumference of a circle (circumference = diameter * 3.141), for example, could be written like this:
Click here to view code image
var diameter: Float = 2.5
var circumference: Float = diameter * 3.141
Pretty easy, don’t you think? Swift data types have much more to offer as well. Let’s see what else they can do!
Note
As I said earlier, everything but the var
keyword and the variable name is optional in a variable declaration. For example, the age calculation code could be written to leave out the variable type entirely:
Click here to view code image
var userAge = 30
var userAgeInDays = userAge * 365
Swift will automatically figure out what the variable is based on the initialization. Personally, I prefer including the variable type so that I can quickly see what each variable represents in my code.
Strings are one of the most frequently used Swift types in this book. You’ll be using strings for user input and output, data manipulation, and so on. As with every other variable, the life of a string begins with a declaration and an initialization:
var myName: String = "John"
Here, a string (myName
) is initialized to the value "John"
. Once initialized, the string can be manipulated using a number of techniques. String concatenation (adding two or more strings together) is performed with the addition (+
) operator. To change myName
to include my last name, I’d write the following:
myName = myName + " Ray"
You can even use a process called string interpolation to combine existing Strings
, values returned by methods, and other Swift data types into a new String
. Consider this line:
Click here to view code image
var sentence: String = "Your name is \(myName) and you are \(userAge) years old"
Here I’ve combined the myName
string and userAge
integer into a single string, assigned to a new variable named sentence
. Any time Swift encounters the pattern \(
in your code, it takes the result, turns it into a string, and substitutes it in place of the pattern. You can use this to quickly format strings based on other variables and methods.
In many languages, strings require special functions to check for equality. In Swift, the same comparison operators you’d use to compare two numbers also work for strings. We’ll look at comparisons a bit later.
A Boolean value has only two states—represented by true
or false
in Swift. Booleans are most often used in comparisons, although some methods have Boolean parameters that you’ll need to supply. As expected, Booleans are initialized using the same pattern you’ve seen for numbers and strings:
var myFlag: Bool = false
A useful category of data type is a collection. Collections enable your applications to store multiple pieces of information in a single object. An Array
is an example of a collection data type that can hold multiple objects, accessed by a numeric index.
You might, for instance, want to create an array that contains all the user feedback strings you want to display in an application:
Click here to view code image
var userMessages: [String] = ["Good job!", "Bad Job", "Mediocre Job"]
Notice that the word Array
doesn’t even appear in the declaration and initialization? That’s because all we need to do to declare an array is wrap the type we want to store (in this case, String
values) in square brackets. If I wanted an array of integers, I’d use a type of [Int]
and so on. The initialization values are provided as a comma-separated list enclosed in square brackets; if you use []
alone, the array is initialized as empty.
To access the strings in the userMessages
array, you use an index value. This is the number that represents a position in the list, starting with 0
. To return the "Bad job"
message, we use the number 1
(the second item in the list):
userMessages[1]
You can also use the index to assign values to an array, replacing what is currently stored:
Click here to view code image
userMessages[1]="Try again"
Swift lets you add new items to the end of the list using the array’s append
method. For example, to add a new message (“Meh”) to the end of userMessages
, I might write the following:
userMessages.append("Meh")
There are several other means of accessing and modifying arrays that we’ll use over the course of the next 21 hours.
Like arrays, dictionaries are another collection data type, but with an important difference. Whereas the objects in an array are accessed by a numeric index, dictionaries store information as key/value pairs. The key is usually an arbitrary string, whereas the value can be anything you want, even objects. If the previous userMessages
array were to be created as a Dictionary
instead, it might look like this:
Click here to view code image
var userMessages: [String:String] =
["positive":"Good job!", "negative":"Bad Job", "indifferent":"Mediocre Job"]
Similar to declaring the strings, I declare the dictionary without ever using the word dictionary. Instead, I provide the type data types that will form the keys and values within square brackets—for example, [
. For the userMessage
dictionary, I’m using keys that are strings, and values that are strings. The initialization is similar to an array, but consists of the key, a colon (:
), and then the value. Each key/value pair is separated by a comma. Empty dictionaries can be created with the initializer [:]
.
To access a value in the dictionary, I index into userMessages
just like an array, but using the key rather than an integer. To access the “Bad Job” message (tied to the “negative” key), I could type the following:
userMessages["negative"]
Keys can also be used to modify or assign new values. Here the key “apathy” is assigned the value “Meh”:
Click here to view code image
userMessages["apathy"] = "Meh"
Dictionaries are useful because they let you store and access data in abstract ways rather than in a strict numeric order.
Tip: Counting the Contents
Both dictionaries and arrays include a read-only variable property called count
that returns the number of elements they’ve stored. The number of elements in the userMessages
array (or dictionary), for example, can be accessed with the expression: userMessages.count
.
Just about everything that you’ll be working with in your iOS applications will be an object. Onscreen text, for example, will be instances of the class UILabel
. Buttons that you display are objects of the class UIButton
. You’ll learn about several of the common object classes in the next hour’s lesson. Apple has literally provided thousands of different classes that you can use to store and manipulate data.
Objects are declared and initialized just like Swift data types. For example, to declare and create a new instance of the UILabel
class, you could use the following code:
Click here to view code image
var myLabel: UILabel = UILabel()
Here, the initializer is UILabel()
. This returns a new, ready-to-use instance of the UILabel
class. You can initialize all classes using this same syntax
, but most will require additional setup after initialization. To speed things along, many will provide convenience methods. Convenience methods speed the process of creating a new object by taking the basic parameters needed to create and configure the object, all at once.
Note
When you read through the Xcode documentation (discussed in the next hour), you’ll see initialization methods denoted with the function name init
for Swift. This is the internal method name in the class. It is automatically invoked by using the
syntax.
When we initialized the UILabel
instance, we did create an object, but it doesn’t yet have any of the additional information that makes it useful. Attributes such as what the label should say, or where it should be shown on the screen, have yet to be set. We would need to use several of the object’s other methods to really turn it into something ready to be displayed.
These configuration steps are sometimes a necessary evil, but Apple’s classes often provide a special initialization method called a convenience method. These methods can be invoked to set up an object with a basic configuration so that it can be used almost immediately.
For example, the NSURL
class, which you use later to work with web addresses, defines a convenience method called initWithString
. We can use it to create a brand-new NSURL
object, complete with the URL, just by typing the following:
Click here to view code image
var iOSURL: NSURL = NSURL(string: "http://www.teachyourselfios.com/")!
This is where we (briefly) go off the tracks. Notice that nowhere in that line does initWithString
appear. The initWithString
method is the name of a convenience method in Objective-C. The method still goes by the same name when used in Swift, but it takes on a simplified form.
The general rule of thumb is that, in Swift, the initWith
is removed from the name of convenience method. Whatever remains of the name becomes the first named parameter of the method. A named parameter, as you’ll learn a bit later, is a parameter that requires you to spell out its name in the method call (in this case, string
).
Because Xcode supports autocompletion, it is usually pretty easy to start typing in a method named and find it in the list that appears. Just keep in mind that what you see in the Xcode documentation doesn’t necessarily apply to both Objective-C and Swift.
In your adventures in Swift, you will encounter code that doesn’t quite work the way you want. You’ll find legacy CGFloat
floating-point numbers that must be used in place of Swift Float
. You’ll find places where you need to turn Float
s into Int
s, and vice versa. You’ll even encounter objects that have no idea what they are. To get around these little snags, you’ll likely employ type conversion, or type casting.
For most of the simple data types, you can convert between types by using the syntax:
. For example, if a method calls for a CGFloat
and you have a Float
value (in the variable myFloat
), you can convert it to the proper type with the following:
CGFloat(myFloat)
Swift does everything it can to silently bridge these older data types with the new built-in Swift types—and it is usually very successful. Unfortunately, sometimes this manual conversion will have to happen.
Another common circumstance is when a method returns an object of one type when it needs to be another. When this happens, you must type cast the result.
Type casting takes an object of a higher-level class and tells Xcode which specific subclass it should be. Some methods will return an object of the type AnyObject
rather than a specific type. Does this make any sense? Not really, but it happens often.
For example, the NSDate
class includes several methods that return a date, but instead of being of the type NSDate
, they are of the type AnyObject
. The NSDate
type method distantPast
is one of these methods:
Click here to view code image
var myPastDate: NSDate = NSDate.distantPast() as NSDate
Because distantPast()
results in an object of the type AnyObject
, we must “tell” Xcode that it is really an NSDate
by adding as NSDate
to the end of the assigment. Using the syntax as
after any object will attempt to type cast that object as being of whatever class you name.
After a variable is cast to an object of the correct type, we can interact with it directly as that type. This looks a bit unusual, I know, but it will come in handy later in the book. It’s easier to understand when you see it in an actual application; so for the moment, just be aware that it is an available development tool.
Constants are declared and initialized just like variables, except they begin with the keyword let
. For example, to create a constant named lastName
that holds a String
with my last name, I would write the following:
let lastName: String = "Ray"
The key difference between a constant and a variable is that constants, once assigned, cannot be changed or reassigned. This, however, isn’t as limiting as you might think. When you assign an object to a constant, you can access and modify all the variable properties in that object, execute all its methods, and so on. It can still be used just like any other variable—you just can’t reassign it later.
Constants are more efficient than variables and should be used in their place wherever possible. I think you’ll be surprised to find that we use more constants in our applications than actual variables.
Possibly the most confusing, infuriating thing about Swift is the notion of optional values. In theory, it’s really quite simple. If you’ve developed in other languages, you’ve almost certainly written code that thought it was working with a value, only to find that the value had never been set or that a method that was supposed to return a value didn’t. Making the assumption that we know what is in a variable is dangerous, but it’s something that developers do every day.
In Swift, Apple decided that developers should acknowledge when they’re using a value that might not contain what they expect. The result requires interesting additions to the development process:
1. Method, variable, and constant declarations should state when they may not have, or may not return, a value. These are known as optional values.
2. Why would a method programmed to return a result ever make that result optional? If the method has bad input, or otherwise can’t complete the operation it is tasked with performing, it makes perfect sense to return “nothing”—represented in Swift using the keyword nil
.
3. When attempting to access methods or variables that are optional, developers must unwrap
the values. This means that the developer acknowledges that he or she knows what is in a variable (or returned by a method), and wants to access and use the results.
Now, you might think to yourself, “Hey, I know what I’m doing, I’m not going to write any code where I name a variable or method return type as optional! That would just be extra work!” You’re right, it is extra work—but it’s utterly unavoidable.
All the code that makes up the Cocoa Touch classes is being updated by Apple to denote which variable properties and methods return optional values—and there are many (and the list is growing). I could tell you stories about the number of times I’ve opened a project while writing this book, only to find that Apple has changed a class somewhere that breaks the code I’ve written. That’s one of the difficulties of being an early technology adopter.
Okay, enough ranting. What does all of this actually mean in terms of coding?
First, when declaring a variable, you can define it as optional by adding a ?
after the type name:
Click here to view code image
var myOptionalString: NSString? = "John"
This also means that if the string isn’t immediately initialized, it automatically contains the value nil
.
Note: What the... It’s an Optional Value, But It Has a Value (nil)?!
Yes, this is as weird as it sounds. In Swift, nil
represents literally nothing. When we need some value to represent no value at all, nil
is used. We can’t use something like an empty string (""
) or 0 because those are values. Get used to the idea of nil
, because even though it is something, it is also nothing. (Cue Seinfeld music.)
For method definitions, you denote an optional return value by adding ?
after the return type. In the sample class in Listing 3.1, I defined the method as having an optional return value of NSDate
using this syntax in the declaration:
Click here to view code image
func myInstanceMethod(myString: String, myURL: NSURL) -> NSDate? {
Tip: Optional Downcasting
You’ve seen the syntax for downcasting, but consider what happens if the class you’re downcasting cannot be cast to what you want. In this case, your app is likely going to crash. To deal with this scenario, you can create an optional downcast. With an optional downcast, if the downcast fails, the resulting variable will contain nil
.
To define a downcast operation as optional, simply add a ?
to the end of the as
keyword, as follows:
Click here to view code image
var myPastDate NSDate? = NSDate.distantPast() as? NSDate
Note
Constants can also be assigned as optional using the same syntax as variables. Although this might seem counterintuitive (don’t you assign a value when you create a constant?), it makes sense when you consider that you might be assigning a constant to the return value of a method with an optional return type.
After you’ve either created (or encountered Swift variables and methods) that are optional, you need to know how to access them. Accessing optional values is called unwrapping in Swift. The easiest, most brute-force way is to use optional values is to unwrap them by adding an exclamation mark (!
) to the end of their name.
In other words, each time I wanted to use the value in myOptionalString
, I would reference it as follows:
myOptionalString!
The same goes for the myInstanceMethod
method. To use it, I might write a line like this:
Click here to view code image
var myReturnedDate: NSDate = myInstanceMethod("A cool string", myURL: iOSURL)!
The addition of the !
tells Xcode that we want to access the return value and that we don’t care if it is nil
. We can take this a step further by defining what is called an implicitly unwrapped optional. This is just an optional value that will always be unwrapped automatically when we use it.
To create an implicitly unwrapped variable, you add a !
after the type name. For example, I could write the preceding line of code using an implicitly unwrapped variable, like this:
Click here to view code image
var myReturnedDate: NSDate! = myInstanceMethod("A cool string", myURL: iOSURL)
This declares myReturnedDate
as an optional NSDate
variable, but one that will be implicitly unwrapped. I can assign it the result of an optional method without unwrapping the return value of the method (because both are optional). However, when I go to use myReturnedDate
elsewhere in my code, it will automatically be unwrapped for me—just as if I had put the !
after it each time.
You really won’t be doing this very often, but Xcode is going to do it a lot when it writes code for you. Why? Because every interface object that connects to your code will be referenced through an implicitly unwrapped variable. An interface object may be nil
before it is loaded, so it has to be optional; but once your code is active, it should always have a value, and there’s no point in hindering its use—thus, it is implicitly unwrapped for you.
Another (gentler) way to deal with optional values is called optional binding. This is the assignment of an optional value to a constant. If the assignment succeeds, the optional value is accessible through the constant. If it fails, the optional value is nil
.
Applying optional binding to the myOptionalString
variable, I might write this simple logic to test to see whether an optional value should be used:
Click here to view code image
if let stringValue:String = myOptionalString {
// myOptionalString has a non-nil value.
}
This is a good approach for working with optionals in production-ready code. It gives you an opportunity to react to situations where optionals are set to nil
and errors may have arisen. If you unwrap an optional and try to work with it even if it is nil
, you may crash your code.
For most of the examples in the book, I manually unwrap values with !
because the code is simple and we know how the different components are going to interact. In apps bound for the App Store, I recommend using optional binding to trap for error conditions that you may not have anticipated.
Tip: Optionals: Don’t Be Scared
Optionals exist to help protect you from making bad assumptions in your code. At times, you’ll feel like every single method or variable property you use has been declared as optional—and you’ll likely start to think that your hair is optional as well. The good news is that Xcode recognizes optional values throughout Cocoa Touch and will prompt you if you’re missing the required ?
or !
characters. In most cases, it will even correct simple optional unwrapping errors for you.
Don’t feel like you need to start memorizing the tens of thousands of optional values in Cocoa Touch. Xcode knows, and it will let you know.
You’ve already seen how to declare and initialize objects, but this is only a tiny picture of the methods you’ll be using in your apps. Let’s start by reviewing the syntax of calling methods in Swift.
To use a method, provide the name of the variable that is referencing your object followed by the name of the method, followed by a period, the name of the method, and empty parentheses ()
(empty if there are no parameters). If you’re using a type (class) method, just provide the name of the class rather than a variable name:
Click here to view code image
Things start to look a little more complicated when the method has parameters. A single parameter method call looks like this:
Click here to view code image
Earlier I noted that convenience initialization methods will usually include at least one named parameter, such as string
when initializing an NSURL
object:
Click here to view code image
var iOSURL: NSURL = NSURL(string: "http://www.teachyourselfios.com/")!
This is important to note because the style of using an initial named parameter is only really used in convenience initialization methods. In other (general use) methods, the first parameter is just provided as a value.
Tip
If you aren’t sure whether the first parameter to a method is named or not, the Xcode documentation can help. If the first character after the parenthesis in a Swift method definition is an underscore (_
), that parameter is not named. You’ll learn all about the documentation system in the next hour.
For example, let’s look at a method that takes multiple parameters:
Click here to view code image
var myFullName: String = "John Ray"
var myDescription: String =
myFullName.stringByReplacingOccurrencesOfString(myFullName, withString: "is awesome!")
This code fragment stores my name in the myFullName
variable, then uses the stringByReplacingOccurrencesOfString:withString
method to change my last name from “Ray” to “is awesome!”
In this example, the first parameter to the stringByReplacingOccurrencesOfString:with-String
method has no name; I just put in the value (myFullName
). The second parameter does have a name (withString:
), which must be provided along with the value.
The syntax for multiple parameter method calls looks like this:
Click here to view code image
Note
At the time of this writing, it was very difficult to break lines in Swift without literally breaking the code. I’ve found that you can break lines around assignment statements (
) as long as there are spaces around the =
, as well as after a comma (,
) in lists of parameters.
Note
Throughout the lessons, methods are referred to by name. If the name includes a colon (:), this indicates a required named parameter. This is a convention that Apple has used in its documentation and that has been adopted for this book.
Something that you’ll see when looking at Swift code is that often the result of a method is used directly as a parameter within another method. In some cases, if the result of a method is an object, a developer may immediately use a method or variable property of that object without first assigning it to a variable. This is known as chaining.
Chaining results directly eliminates the need for temporary variables and can make code shorter and easier to maintain.
For example, consider this completely contrived code:
Click here to view code image
var myString: String = "JoHN ray"
myString = myString.lowercaseString
myString = myString.stringByReplacingOccurrencesOfString("john", withString: "will")
myString = myString.capitalizedString
Here I’ve created a string (myString
) that holds my name with very very poor capitalization. I decide that I want to replace my first name (John) with my brother’s name (Will). Because I cannot just search and replace on John because my capitalization is all messy and I don’t want to try to remember how I wrote my name (this is contrived folks), I decide to first convert myString
to lowercase by accessing the lowercaseString
variable property. Once complete, I can just search for john and replace it with will without worrying about capitalization. Unfortunately, that means I still need a properly capitalized version of the string when I’m done. So, I access the capitalizedString
variable property of my string when finished, and use its value for myString
. (In case you’re wondering, capitalizedString
provides a copy of the string with all of the first letters capitalized.)
The code should make sense, even if my logic is a bit shaky. That said, each of the methods and variable properties I’ve used return a string. Instead of assigning things over and over, I can chain each of these actions together into a single line:
Click here to view code image
var myString: String = "JoHN ray".lowercaseString.stringByReplacingOccurrencesOfString("john", withString: "will").capitalizedString
Chaining can be a powerful way to structure your code, but when overused it may lead to lines that can be difficult to parse. Do what makes you comfortable; both approaches are equally valid and have the same outcome.
Tip
Although I tend to leaved chained lines unbroken in my projects, you can break a chained line without causing an error if you break it immediately before one of the periods.
Although most of your coding will be within methods, you will also encounter closures when using the iOS frameworks. Sometimes referred to as handler blocks in the Xcode documentation, these are chunks of code that can be passed as values when calling a method. They provide instructions that the method should run when reacting to a certain event.
For example, imagine a personInformation
object with a method called setDisplayName
that would define a format for showing a person’s name. Instead of just showing the name, however, setDisplayName
might use a closure to let you define, programmatically, how the name should be shown:
Click here to view code image
personInformation.setDisplayName({(firstName: String, lastName: String) in
// Implement code here to modify the first name and last name
// and display it however you want.
})
Interesting, isn’t it? Closures are relatively new to iOS development and are used throughout this book. You’ll first encounter closures when writing alerts. The closure will provide the instructions that are executed when a person acts on an alert.
For an application to react to user input and process information, it must be capable of making decisions. Every decision in an app boils down to a true or false result based on evaluating a set of tests. These can be as simple as comparing two values, to something as complex as checking the results of a complicated mathematical calculation. The combination of tests used to make a decision is called an expression.
If you recall your high school algebra, you’ll be right at home with expressions. An expression can combine arithmetic, comparison, and logical operations.
A simple numeric comparison checking to see whether a variable userAge
is greater than 30 could be written as follows:
userAge>30
When working with objects, we need to use variable properties within the object and values returned from methods to create expressions. If I have stored an NSDate object with my birthday in it (myBirthday
), I could check to see whether the current day is my birthday with the expression:
Click here to view code image
myBirthday.isEqualToDate(NSDate())
Expressions are not limited to the evaluation of a single condition. We could easily combine the previous two expressions to find a person who is over 30 and is celebrating their birthday today:
Click here to view code image
userAge > 30 && myBirthday.isEqualToDate(NSDate())
As mentioned repeatedly, you’re going to be spending lots of time working with complex objects and using the methods within the objects. You cannot make direct comparisons between objects as you can with simple data types. To successfully create expressions for the myriad objects you’ll be using, you must review each object’s methods and variable properties.
Typically, depending on the outcome of the evaluated expression, different code statements are executed. The most common way of defining these different execution paths is with an if-then-else
statement:
Click here to view code image
if
// do this, the expression is true.
} else {
// the expression isn't true, do this instead!
}
For example, consider the comparison we used earlier to check a myBirthday NSDate
variable to see whether it was equal to the current date. If we want to react to that comparison, we might write the following:
Click here to view code image
if myBirthday.isEqualToDate(NSDate()) {
let myMessage: String = "Happy Birthday!"
} else {
let myMessage: String = "Sorry, it's not your birthday."
}
Another approach to implementing different code paths when there are potentially many different outcomes to an expression is to use a switch
statement. A switch
statement checks a variable for a value and then executes different blocks of code depending on the value that is found:
Click here to view code image
switch (
case
// The value matches this option
case
// The value matches this option
default:
// None of the options match the number.
}
Applying this to a situation where we might want to check a user’s age (stored in userAge
) for some key milestones and then set an appropriate userMessage
string if they are found, the result might look like this:
Click here to view code image
switch userAge {
case 18:
let userMessage: String = "Congratulations, you're an adult!"
case 21:
let userMessage: String = "Congratulations, you can drink champagne!"
case 50:
let userMessage: String = "You're half a century old!"
default:
let userMessage: String = "Sorry, there's nothing special about your age."
}
In some situations, you will need to repeat several instructions over and over in your code. Instead of typing the lines repeatedly, you can loop over them. A loop defines the start and end of several lines of code. As long as the loop is running, the program executes the lines from top to bottom and then restarts again from the top. The loops you’ll use are of two types: for
loops and condition-based while
/do-while
loops.
In a for
loop, the statements are repeated a (mostly) predetermined number of times. You might want to count to 1000 and output each number, or create a dozen copies of an object. These are perfect uses for a for
loop.
The for
loop you’ll encounter most often consists of this syntax:
Click here to view code image
for
// Do this, over and over!
}
The three “unknowns” in the for
statement syntax are a statement to initialize a counter variable to track the number of times the loop has executed, a condition to check to see whether the loop should continue, and finally, an increment for the counter. A loop that uses the integer variable count
to loop 50 times could be written as follows:
Click here to view code image
for var count=0;count<50;count=count+1 {
// Do this, 50 times!
}
The for
loop starts by setting the count
variable to 0
. The loop then starts and continues as long as the condition of count<50
remains true
. When the loop hits the bottom curly brace (}
) and starts over, the increment operation is carried out and count
is increased by 1
.
Note
Integers are usually incremented by using ++
at the end of the variable name. In other words, rather than using count=count+1
, most often you’ll encounter count++
, which does the same thing. Decrementing works the same way, but with --
.
for
loops can also iterate over collections using the following syntax:
Click here to view code image
for
// Do this for each value in the collection, where
}
Consider the array of messages we created earlier in the hour:
Click here to view code image
var userMessages: [String] = ["Good job!", "Bad Job", "Mediocre Job"]
To loop over this array of messages, we can write a “for in” loop as follows:
Click here to view code image
for message in userMessages {
// The message variable now holds an individual message
}
The same applies to dictionaries as well, but the syntax changes just a little bit. If userMessages
is defined as a dictionary:
Click here to view code image
var userMessages: [String:String] =
["positive":"Good job!", "negative":"Bad Job", "indifferent":"Mediocre Job"]
We can loop over each key/value pair like this:
Click here to view code image
for (key, value) in userMessages {
// The key and value variables hold an individual dictionary entry
}
In a condition-based loop, the loop continues while an expression remains true. You’ll encounter two variables of this loop type, while
and do-while
:
Click here to view code image
while
// Do this, over and over, while the expression is true!
}
and
Click here to view code image
do {
// Do this, over and over, while the expression is true!
} while
The only difference between these two loops is when the expression is evaluated. In a standard while
loop, the check is done at the beginning of the loop. In the do-while
loop, however, the expression is evaluated at the end of every loop. In practice, this difference ensures that in a do-while
loop, the code block is executed at least once; a while
loop may not execute the block at all.
For example, suppose that you are asking users to input their names and you want to keep prompting them until they type John. You might format a do-while
loop like this:
Click here to view code image
do {
// Get the user's input in this part of the loop
} while userName != "John"
The assumption is that the name is stored in a string called userName
. Because you wouldn’t have requested the user’s input when the loop first starts, you would use a do-while
loop to put the test condition at the end.
Loops are a very useful part of programming and, along with the decision statements, will form the basis for structuring the code within your object methods. They allow code to branch and extend beyond a linear flow.
Although an all-encompassing picture of programming is beyond the scope of this book, this should give you some sense of what to expect in the rest of the book. Let’s now close out the hour with a topic that causes quite a bit of confusion for beginning developers: memory management.