Constants and variables
Constants and variables associate an identifier (such as myName
or currentTemperature
) with a value of a particular type (such as String
or Int
), where the identifier can be used to retrieve the value. The difference between a constant and a variable is that a variable can be updated or changed, while a constant cannot be changed once a value is assigned to it.
Constants are good for defining the values that you know will never change, such as the temperature that water freezes at or the speed of light. Constants are also good for defining a value that we use many times throughout our application, such as a standard font size or the maximum number of characters in a buffer. There will be numerous examples of constants throughout this book, and it is recommended that we use constants rather than variables whenever possible.
Variables tend to be more common in software development than constants. This is mainly because developers tend to prefer variables to constants. In Swift, we receive a warning if we declare a variable that is never changed. We can make useful applications without using constants (although it is a good practice to use them); however, it is almost impossible to create a useful application without variables.
Note
The use of constants is encouraged in Swift. If we do not expect or want the value to change, we should declare it as a constant. This adds a very important safety constraint to our code that ensures that the value never changes.
You can use almost any character in the identifier of a variable or constant (even Unicode characters); however, there are a few rules that you must follow:
- An identifier must not contain any whitespace
- It must not contain any mathematical symbols
- It must not contain any arrows
- An identifier must not contain private use or invalid Unicode characters
- It must not contain line- or box-drawing characters
- Also, it must not start with a number, but they can contain numbers
- If you use a Swift keyword as an identifier, surround it with back ticks
Note
Keywords are words that are used by the Swift programming language. Some examples of keywords that you will see in this chapter are var
and let
. You should avoid using Swift keywords as identifiers to avoid confusion when reading your code.
Defining constants and variables
Constants and variables must be defined prior to using them. To define a constant, you use the let
keyword and to define a variable, you use the var
keyword. The following code shows how to define both constants and variables:
// Constants let freezingTemperatureOfWaterCelsius = 0 let speedOfLightKmSec = 300000 // Variables var currentTemperature = 22 var currentSpeed = 55
We can declare multiple constants or variables in a single line by separating them with a comma. For example, we could shrink the preceding four lines of code down to two lines, as shown here:
// Constants let freezingTempertureOfWaterCelsius = 0, speedOfLightKmSec = 300000 // Variables var currentTemperture = 22, currentSpeed = 55
We can change the value of a variable to another value of a compatible type; however, as we noted earlier, we cannot change the value of a constant. Let's look at the following Playground. Can you tell what is wrong with the code from the error message that is shown?
Did you figure out what was wrong with the code? Any physicist can tell you that we cannot change the speed of light, and in our code, the speedOfLightKmSec
variable is a constant, so we cannot change it here either. When we attempt to change the speedOfLightKmSec
constant, an error is reported. We are able to change the value of the highTemperture
variable without an error because it is a variable. We mentioned the difference between variables and constants a couple of times because it is a very important concept to grasp, especially when we define mutable and immutable collection types later in Chapter 3, Using Swift Collections and the Tuple Type.
Type safety
Swift is a type-safe language. In a type-safe language, we are required to be clear on the types of values we store in a variable. We will get an error if we attempt to assign a value to a variable that is of the wrong type. The following Playground shows what happens if we attempt to put a string value into a variable that expects integer values; note that we will go over the most popular types a little later in the chapter:
Swift performs a type check when it compiles code; therefore, it will flag any mis-matched types with an error. The error message in this Playground explains pretty clearly that we are trying to insert a string literal into an integer variable.
So the question is: how does Swift know that integerVar
is of the Int
type? Swift uses type inference to figure out the appropriate type. Let's take a look at what type inference is.
Type inference
Type inference allows us to omit the variable type when we define it. The compiler will infer the type, based on the initial value. For example, in Objective-C, we would define an integer like this:
int myInt = 1
This tells the compiler that the myInt
variable is of the int
type, and the initial value is the number 1
. In Swift, we would define the same integer like this:
var myInt = 1
Swift infers that the variable type is an integer because the initial value is an integer. Let's take a look at a couple of more examples:
var x = 3.14 // Double type var y = "Hello" // String type var z = true // Boolean type
In the preceding example, the compiler will correctly infer that variable x
is Double
, variable y
is String
, and variable z
is Boolean
, based on the initial values.
Explicit types
Type inference is a very nice feature in Swift and is one that you will probably get used to very quickly; however, there are times when we would like to explicitly define a variable's type. For example, in the preceding example, the variable x
is inferred to be Double
, but what if we wanted the variable type to be Float
? We can explicitly define a variable type like this:
var x : Float = 3.14
Notice the Float
declaration (the colon and the word Float
) after the variable identifier. This tells the compiler to define this variable to be of the Float
type and gives it an initial value of 3.14
. When we define a variable in this manner, we need to make sure that the initial value is of the same type as we are defining the variable to be. If we try to give a variable an initial value that is of a different type than we are defining it as, we will receive an error.
We will need to explicitly define the variable type if we were not setting an initial value. For example, the following line of code is invalid because the compiler does not know what type to set the variable x
to:
var x
If we use this code in our application, we will receive a Type annotation missing in pattern
error. If we are not setting an initial value for a variable, we are required to define the type like this:
var x: Int
Now that we have seen how to explicitly define a variable type, let's take a look at some of the most commonly used types.
Numeric types
Swift contains many of the standard numeric types that are suitable for storing various integer and floating-point values.
Integers
An integer is a whole number and can be either signed (positive, negative, or zero) or unsigned (positive or zero). Swift provides several integer types of different sizes. The following chart shows the value ranges for the different integer types on a 64-bit system:
Tip
Unless there is a specific reason to define the size of an integer, I would recommend using the standard Int
or UInt
type. This will save you from needing to convert between different types of integers.
In Swift, Int (as well as other numerical types) are actually named types, implemented in the Swift standard library using structures. This gives us a consistent mechanism for memory management of all the data types as well as properties that we can access. For the preceding chart, I retrieved the minimum and maximum values of each integer type using the min
and max
properties. Take a look at the following Playground to see how I retrieved the values:
Integers can also be represented as binary, octal, and hexadecimal numbers. We just need to add a prefix to the number to tell the compiler which base the number should be in. The following chart shows the prefix for each numerical base:
The following Playground shows how the number 95
is represented in each of the numerical bases:
Swift also allows us to insert arbitrary underscores in our numeric literals. This can improve the readability of our code. As an example, if we were defining the speed of light, which is constant, we can define it like this:
let speedOfLightKmSec = 300_000
Swift will ignore these underscores; therefore, they do not affect the value of the numeric literals in any way.
Floating-point
A floating-point number is a number with a decimal component. There are two standard floating-point types in Swift: Float
and Double.
The Float
type represents a 32-bit floating-point number, while the Double type represents a 64-bit floating-point number. Swift also supports an extended floating-point type Float80. The Float80 type is an 80-bit floating-point number.
It is recommended that we use the Double
type over the Float
type unless there is a specific reason to use the latter. The Double
type has a precision of at least 15 decimal digits, while the Float
type can be as little as six decimal digits. Let's look at an example of how this can effect our application without us knowing it. The following screenshot shows the results if we add two decimal numbers together using both a Float
type and a Double
type:
As we can see from the screenshot, the two decimal numbers that we are adding contain nine digits past the decimal point; however, the results in the Float
type only contains seven digits, while the results in the Double
type contains the full nine digits.
The loss of precision can cause issues if we are working with currency or other numbers that need accurate calculations. The floating-point accuracy problem is not an issue confined to Swift; all the languages that implement the IEEE 754 floating-point standard have similar issues. The best practice is to use Double
types for floating-point numbers unless there is a specific reason not to.
What if we have two variables, one an Int
and the other a Double
? Do you think we can add them as the following code depicts?
var a : Int = 3 var b : Double = 0.14 var c = a + b
If we put the preceding code into a Playground, we would receive the following error: operator '+' cannot be applied to operands of type Int and Double
This error lets us know that we are trying to add two different types of numbers, which is not allowed. To add an Int
and a Double
together, we need to convert the Int
value into a Double
value. The following code shows how to this:
var a : Int = 3 var b : Double = 0.14 var c = Double(a) + b
Notice how we use the Double()
function to convert the Int
value to a Double
value. All of the numeric types in Swift have a conversion convenience initializer, similar to the Double()
function shown in the preceding code sample. For example, the following code shows how you can convert an Int
variable to Float
and UInt16
variables:
var intVar = 32 var floatVar = Float(intVar) var uint16Var = UInt16(intVar)
The Boolean type
Boolean values are often referred to as logical values because they can be either true
or false
. Swift has a built-in Boolean type that accepts one of the two built-in Boolean constants: true
and false
.
Boolean constants and variables can be defined like this:
let swiftIsCool = true let swiftIsHard = false var itIsWarm = false var itIsRaining = false
Boolean values are especially useful when working with conditional statements, such as if
and while
. For example, what do you think this code would do?
let isSwiftCool = true let isItRaining = false if (isSwiftCool) { print("YEA, I cannot wait to learn it") } if (isItRaining) { print("Get a rain coat") }
If you answered that this code would print out YEA, I cannot wait to learn it
, then you would be correct. Since isSwiftCool
is set to true, the YEA, I cannot wait to learn it
message is printed out, but isItRaining
is false; therefore, the Get a rain coat
message is not printed.
The string type
A string is an ordered collection of characters, such as Hello
or Swift
. In Swift, the string type represents a string. We have seen several examples of strings already in this book, so the following code should look familiar. This code shows how to define two strings:
var stringOne = "Hello" var stringTwo = " World"
Since a string is an ordered collection of characters, we can iterate through each character of a string. The following code shows how to do this:
var stringOne = "Hello" for char in stringOne.characters { print(char) }
The preceding code will display the results shown in the following screenshot:
There are two ways to add one string to another string. We can concatenate them or include them inline. To concatenate two strings, we use the +
or +=
operator. The following code shows how to concatenate two strings. The first example appends stringB
to the end of stringA
and the results are put into a new stringC
variable. The second example appends stringB
directly to the end of stringA
without creating a new string:
var stringC = stringA + stringB stringA += stringB
To include a string inline, with another string, we use a special sequence of characters \( )
. The following code shows how to include a string inline with another string:
var stringA = "Jon" var stringB = "Hello \(stringA)"
In the previous example, stringB
will contain the message Hello Jon
, because Swift will replace the \(stringA)
sequence of characters with the value of stringA
.
In Swift, we define the mutability of variables and collections by using the var
and let
keywords. If we define a string as a variable using var
, the string is mutable, meaning that we can change and edit the value of the string. If we define a string as a constant using let
, the string is immutable, meaning that we cannot change or edit the value once it is set. The following code shows the difference between a mutable and an immutable string:
var x = "Hello" let y = "HI" var z = " World" //This is valid, x is mutable x += z //This is invalid, y is not mutable. y += z
Strings in Swift have two methods that can convert the case of the string. These methods are lowercased()
and uppercased()
. The following example demonstrates these methods:
var stringOne = "hElLo" print("Lowercase String: " + stringOne.lowercased()) print("Uppercase String: " + stringOne.uppercased())
If we run this code, the results will be as follows:
Lowercase String: hello Uppercase String: HELLO
Swift provides four ways to compare a string; these are string equality, prefix equality, suffix equality, and isEmpty
. The following example demonstrates these ways:
var stringOne = "Hello Swift" var stringTwo = "" stringOne.isEmpty //false stringTwo.isEmpty //true stringOne == "hello swift" //false stringOne == "Hello Swift" //true stringOne.hasPrefix("Hello") //true stringOne.hasSuffix("Hello") //false
We can replace all the occurrences of a target string with another string. This is done with the stringByReplacingOccurrencesOfString()
method. The following code demonstrates this:
var stringOne = "one,to,three,four" print(stringOne.replacingOccurrences(of: "to", with: "two"))
The preceding example will print one,two,three,four
to the screen because we are replacing all the occurrences of to
with two
.
We can also retrieve substrings and individual characters from our strings. The following example shows various ways to do this:
var stringOne = "one,to,three,four" print(stringOne.replacingOccurrences(of: "to", with: "two")) var path = "/one/two/three/four" //Create start and end indexes let startIndex = path.index(path.startIndex, offsetBy: 4) let endIndex = path.index(path.startIndex, offsetBy: 14) let myRange = startIndex..<endIndex path.substring(with: myRange) //returns the String /two/three path.substring(to:startIndex) //returns the String /one path.substring(from:endIndex) //returns the String /four path.characters.last path.characters.first
In the preceding example, we use the substring(with:)
function to retrieve the substring between a start and end index. The indexes are created with the index(_: offsetBy:)
function. The first property in the index(_: offsetBy:)
function gives the index of where we wish to start, and the offsetBy
property tells us how much to increase the index by.
The substring(to:)
function creates a substring from the beginning of the string to the index. The substring(from:)
function creates a substring from the index to the end of the string. We then used the last
property to get the last character of the string and the first
property to get the first character.
We can retrieve the number of characters in a string by using the count
property. The following example shows how you can use this function:
var path = "/one/two/three/four" var length = path.characters.count
This completes our whirlwind tour of strings. I know we went through these properties and functions very quickly, but we will be using strings extensively throughout this book, so we will have a lot of time to get used to them.
Optional variables
All the variables we have looked at so far, are considered to be non-optional variables. This means that the variables are required to have a non-nil value; however, there are times when we want or need our variables to contain nil
values. This can occur if we return a nil
from a function whose operation has failed or if a value is not found.
In Swift, an optional variable is a variable that we are able to assign nil
(no value) to. Optional variables and constants are defined using ?
(question mark). Let's look at the following Playground; it shows us how to define an Optional
type and shows what happens if we assign a nil
value to a Non-Optional
variable:
Notice the error we receive when we try to assign a nil
value to the non-optional variable.
Optional variables were added to the Swift language as a safety feature. They provide a compile-time check of our variables to verify that they contain a valid value. Unless our code specifically defines a variable as optional, we can assume that the variable contains a valid value, and we do not have to check for nil
values. Since we are able to define a variable prior to initiating it, this could give us a nil
value in a non-optional variable; however, the compiler checks for this. The following Playground shows the error that we receive if we attempt to use a non-optional variable prior to initiating it:
To verify that an optional variable or constant contains a valid (non-nil) value, our first thought may be to use the !=
(not equals to) operator to verify that the variable is not equal to nil
, but there are also other ways. These other ways are optional binding and optional chaining. Before we cover optional binding and optional chaining, let's see how to use the !=
(not equals to) operator and what force unwrapping is.
To use force unwrapping, we must first make sure that the optional has a non-nil value and then we can use the explanation point to access that value. The following example shows how we can do this:
var name: String? name = "Jon" if name != nil { var newString = "Hello " + name! }
In this example, we create an optional variable named name
and we assign it a value of Jon
. We then use the !=
operator to verify that the optional is not equal to nil
. If it is not equal to nil
, we use the explanation point to access its value. While this is a perfectly viable option, it is recommended that we use the optional binding method discussed next instead of force unwrapping.
Optional binding is used to check whether an optional variable or constant has a non-nil value, and, if so, assign that value to a temporary variable. For optional binding, we use the if let
or if var
keywords together. If we use if let
, the temporary value is a constant and cannot be changed, while the if var
keyword puts the temporary value into a variable that allows us to change the value. The following code illustrates how optional binding is used:
var myOptional: String? if let temp = myOptional { print(temp) print("Can not use temp outside of the if bracket") } else { print("myOptional was nil") }
In the preceding example, we use the if let
keywords to check whether the myOptional
variable is nil
. If it is not nil
, we assign the value to the temp
variable and execute the code between the brackets. If the myOptional
variable is nil
, we execute the code in the else
bracket, which prints out the message, myOptional was nil
. One thing to note is that the temp
variable is scoped only for the conditional block and cannot be used outside of the conditional block.
It is perfectly acceptable with optional binding to assign the value to a variable of the same name. The following code illustrates this:
if let myOptional = myOptional { print(myOptional) print("Can not use temp outside of the if bracket") } else { print("myOptional was nil") }
To illustrate the scope of the temporary variable, let's take a look at the following code:
var myOptional: String? myOptional = "Jon" if var myOptional = myOptional { myOptional = "test" print("Inside: \(myOptional)") } print("Outside: \(myOptional)")
In this example, the first line that is printed to the console is Inside: test
because we are within the scope of the if var
statement where we assign the value of test
to the myOptional
variable. The second line that is printed to the console would be Outside: Optional
(Jon
) because we are outside of the scope of the if var
statement where the myOptional
variable is set to Jon
.
We can also test multiple optional variables in one line. We do this by separating each optional check with a comma. The following example shows how to do this:
if let myOptional = myOptional, myOptional2 = myOptional2, myOptional3 = myOptional3 { // only reach this if all three optionals // have non-nil values }
Optional chaining allows us to call properties, methods, and subscripts on an optional that might be nil
. If any of the chained values return nil
, the return value will be nil
. The following code gives an example of optional chaining using a fictitious car
object. In this example, if either car
or tires
are nil
, the variable s
will be nil
; otherwise, s
will be equal to the tireSize
property:
var s = car?.tires?.tireSize
The following Playground illustrates the three ways to verify whether an optional contains a valid value prior to using it:
In the preceding Playground, we begin by defining the optional string variable, stringOne
. We then explicitly check for nil
by using the !=
operator. If stringOne
is not equal to nil
, we print the value of stringOne
to the console. If stringOne
is nil
, we print the Explicit Check: stringOne is nil
message to the console. As we can see in the results console, Explicit Check: stringOne is nil
is printed to the console because we have not assigned a value to stringOne
yet.
We then use optional binding to verify that stringOne
is not nil
. If stringOne
is not nil
, the value of stringOne
is put into the tmp
temporary variable, and we print the value of tmp
to the console. If stringOne
is nil
, we print the Optional Binding
: stringOne is nil
message to the console. As we can see in the results console, Optional Binding
: stringOne is nil
is printed to the console because we have not assigned a value to stringOne
yet.
We use optional chaining to assign the value of the characters.count
property of the stringOne
variable to the charCount1
variable if stringOne
is not nil
. As we can see, the charCount1
variable is nil
because we have not assigned a value to stringOne
yet.
We then assign a value of http://www.packtpub.com/all to the stringOne
variable and rerun all the three tests again. This time, stringOne
has a non-nil value; therefore, the value of charCount2
is printed to the console.
Note
It would be tempting to say that I may need to set this variable to nil
, so let me define it as optional, but that would be a mistake. The mindset for optionals should be to only use them if there is a specific reason for the variable to have nil
value.
We will be discussing optionals further in Chapter 10, Using Optional Types, later in this book.
Enumerations
Enumerations (otherwise known as enums) are a special data type that enable us to group related types together and use them in a type-safe manner. Enumerations in Swift are not tied to integer values as they are in other languages, such as C or Java. In Swift, we are able to define an enumeration with a type (string, character, integer, or floating-point) and then define its actual value (known as the raw value). Enumerations also support features that are traditionally only supported by classes, such as computed properties and instance methods. We will discuss these advanced features in depth in Chapter 5, Classes and Structures. In this section, we will look at the traditional features of enumerations.
We will define an enumeration that contains the list of Planets
like this:
enum Planets { case Mercury case Venus case Earth case Mars case Jupiter case Saturn case Uranus case Neptune }
The values defined in an enumeration are considered to be the member values (or simply the members) of the enumeration. In most cases, you will see the member values defined like the preceding example because it is easy to read; however, there is a shorter version. This shorter version lets us define multiple members in a single line, separated by commas, as the following example shows:
enum Planets { case Mercury, Venus, Earth, Mars, Jupiter case Saturn, Uranus, Neptune }
We can then use the Planets
enumeration like this:
var planetWeLiveOn = Planets.Earth var furthestPlanet = Planets.Neptune
The type for the planetWeLiveOn
and furthestPlanet
variables is inferred when we initialize the variable with one of the member values of the Planets
enumeration. Once the variable type is inferred, we can then assign a new value without the Planets
prefix, as shown here:
planetWeLiveOn = .Mars
We can compare an enumeration value using the traditional equals (==
) operator or use a switch
statement. The following example shows how to use the equals
operator and the switch
statement with an enum:
// Using the traditional == operator if planetWeLiveOn == .Earth { print("Earth it is") } // Using the switch statement switch planetWeLiveOn { case .Mercury: print("We live on Mercury, it is very hot!") case .Venus: print("We live on Venus, it is very hot!") case .Earth: print("We live on Earth, just right") case .Mars: print("We live on Mars, a little cold") default: print("Where do we live?") }
Enumerations can come prepopulated with raw values, which are required to be of the same type. The following example shows how to define an enumeration with string values:
enum Devices: String { case MusicPlayer = "iPod" case Phone = "iPhone" case Tablet = "iPad" } print("We are using an " + Devices.Tablet.rawValue)
The preceding example creates an enumeration with three types of devices. We then use the rawValue
property to retrieve the raw value for the Tablet
member of the Devices
enumeration. This example will print a message, saying We are using an iPad
.
Let's create another Planets
enumeration, but this time, assign numbers to the members, as follows:
enum Planets: Int { case Mercury = 1 case Venus case Earth case Mars case Jupiter case Saturn case Uranus case Neptune } print("Earth is planet number \(Planets.Earth.rawValue)")
The big difference between these last two enumerations examples is that in the second example, we only assign a value to the first member (Mercury
). If integers are used for the raw values of an enumeration then we do not have to assign a value to each member. If no value is present, the raw values will be auto-incremented.
In Swift, enumeration can also have associated values. Associated values allow us to store additional information along with member values. This additional information can vary each time we use the member. It can also be of any type, and the types can be different for each member. Let's take a look at how we might use associate types by defining a Product
enumeration, which contains two types of products:
enum Product { case Book(cost: Double, year: Int, pages: Int) case Puzzle(cost: Double, pieces: Int) } var masterSwift = Product.Book(cost: 49.99, year: 2016, pages: 310) var worldPuzzle = Product.Puzzle(cost: 9.99, pieces: 200) Switch masterSwift { case .Book(let price, let year, let pages): print("Mastering Swift was published in \(year) for the price of \(price) and has \(pages) pages") case .Puzzle(let price, let pieces): print("Master Swift is a puzze with \(pieces) pieces and sells for \(price)") } switch worldPuzzle { case .Book(let price, let year, let pages): print("World Puzzle was published in \(year) for the price of \(price) and has \(pages) pages") case .Puzzle(let price, let pieces): print("World Puzzle is a puzze with \(pieces) pieces and sells for \(price)") }
In the preceding example, we begin by defining a Product
enumeration with two members—Book
and Puzzle
. The Book
member has an associated value of Double
, Int
, Int
, types and the Puzzle
member has an associated value of Double, Int
types. Notice that we are using named associated types where we assign a name for each associated type. We then create two products masterSwift
and worldPuzzle
. We assign the masterSwift
variable a value of Product.Book
with the associated values of 49.99
, 2016
, and 310
. We then assign the worldPuzzle
variable a value of Product.Puzzle
with the associated values of 9.99, 200
.
We can then check the Products
enumeration using a switch
statement, as we did in an earlier example. We extract the associated values within the switch
statement. In this example, we extracted the associated values as constants with the let
keyword, but you can also extract the associated values as variables with the var
keyword.
If you put the previous code into a Playground, the following results will be displayed:
"Master Swift was published in 2016 for the price of 49.99 and has 310 pages" "World Puzzle is a puzzle with 200 and sells for 9.99"
We have only scratched the surface of what enumerations can do in Swift. In Chapter 5, Classes and Structures we will look at some additional features on enumerations with Swift and see why they are so powerful.