Book Image

Beginning Swift

By : Rob Kerr, Kåre Morstøl
Book Image

Beginning Swift

By: Rob Kerr, Kåre Morstøl

Overview of this book

Take your first foray into programming for Apple devices with Swift.Swift is fundamentally different from Objective-C, as it is a protocol-oriented language. While you can still write normal object-oriented code in Swift, it requires a new way of thinking to take advantage of its powerful features and a solid understanding of the basics to become productive.
Table of Contents (10 chapters)

Swift Variables and Constants


Virtually all programming languages include the ability for programmers to store values in memory using an associated name chosen by the programmer. Variables allow programs to operate on data values that change during the run of the program.

Declaring Swift Variables

A Swift variable declaration uses the following basic syntax:

var <variable name> : <type> = <value>

Given this syntax, a legal declaration for a Pi variable would be the following:

                        var pi : Double = 3.14159

This declaration means: create a variable named Pi , which stores a Double data type, and assign it an initial value of 3.14159.

Note

The Swift Standard Library has Pi built in, accessed by using the Float.pi and Double.pi properties.

Variables Versus Constants

You may want to store a named value in your program that will not change during the life of the program. In the previous example, the value of Pi should never change during the course of a program. How can we ensure that, once defined, this named value can never be accidentally changed by our code?

Swift variables are declared using the var keyword, while Swift constants are declared using the let keyword, for example:

var pi1 = 3.14159
let pi2 = 3.15159

In this code, the named value pi1 is a variable, and its value can be changed by the code after it is declared. The following line of code later in the program would be legal, even though it would result in an invalid value for pi1:

pi1 = pi1 * 2.0

On the other hand, since pi2 was declared as a constant, using the let keyword, the following line of code later in the program would result in a compile-time error, since changing a let constant is illegal:

pi2 = pi2 * 2.0

Generally, any time you create a named value that will never be changed during the run of your program, you should use the let keyword to create a constant. The Swift compiler enforces this recommendation by creating a compile-time warning whenever a var is created that is not subsequently changed.

Note

Other than the restriction on mutating the value of a constant once declared (for safety), Swift variables and constants are used in virtually identical ways, and you usually won't think about whether a symbol is a variable or a constant after declaring it.

Type Inference

In the previous example, we created the variable pi1 without specifying its data type. We took advantage of a Swift compiler feature called type inference.

When you assign the value of a variable or constant as you create it, the Swift compiler will analyze the right-hand side of the assignment, infer the data type, and assign that data type to the variable or constant you're creating. For example, in the following declaration, the compiler will create the variable name as a String data type:

var name = "George Smith"

As a type-safe language, once a data type is inferred by the compiler, it remains fixed for the life of the variable or constant. Attempting to assign a non-string value to the name variable declared above would result in a compile-time error:

name = 3.14159  // Error: "Cannot assign value of type 'Double' to 'String'

While Swift is a type-safe language, where variable types are explicit and do not change, it is possible to create Swift code that behaves like a dynamic type language using the Swift Any data type. For example, the following code is legal in Swift:

var anyType: Any
anyType = "Hello, world"
anyType = 3.14159

While this is legal, it's not a good Swift programming practice. The Any type is mainly provided to allow bridging between Objective-C and Swift code. To keep your code as safe and error-free as possible, you should use explicit types wherever possible.

Variable Naming

Swift variables and constants have the same naming rules as most C-inspired programming languages:

  • Must not start with a digit

  • After the first character, digits are allowed

  • Can begin with and include an underscore character

  • Symbol names are case sensitive

  • Reserved language keywords may be used as variable names if enclosed in backticks (for example, `Int`:Int = 5)

When creating variable and constant names in Swift, the generally accepted naming convention is to use a camelCase naming convention, beginning with a lowercase letter. Following generally accepted naming conventions makes code easier for others to read and understand (https://swift.org/documentation/api-design-guidelines/#follow-case-conventions).

For example, the following would be a conventional variable declaration:

var postalCode = "48108"

However, the following would not be conventional, and would be considered incorrect by many other Swift developers:

var PostalCode = "48108"
var postal_code  = "48108"
var POSTALCODE = "48108"

Unlike many other programming languages, Swift is not restricted to the Western alphabet for its variable name characters. You may use any Unicode character as part of your variable declarations. The following variable declarations are legal in Swift:

var helloWorld = "Hello, World"
var 你好世界 = "Hello World"
var 😊 = "Smile!"

Note

Just because you can use any Unicode character within a variable name, and can use reserved words as variables when enclosed in backticks, it doesn't mean you should. Always consider other developers who may need to read and maintain your code in the future. The priority for variable names is that they should make code easier to read, understand, and maintain.

Working with Variables

In this section, you'll use an Xcode playground to create a variable and constant, and observe the difference between them. So, let's get started.

To work with variables, follow these steps:

  1. Launch Xcode as before, and create a new playground named Create a Variable.playground.

  2. Add the following code to the playground to create a constant (that is, an immutable variable) named name, and a variable named address:

    let name = "John Doe"
    var address = "201 Main Street"
    print("\(name) lives at \(address)")

    In this code, both name and address store string text in named memory locations. And we can include them both in the print statement in the same way.

  3. Now add the following code to change John Doe's address and print the new information to the console:

    address = "301 Fifth Avenue"
    print("\(name) lives at \(address)")

    In the console output, the address is changed as expected.

  4. Finally, let's try to change the string stored in the name variable:

    name = "Richard Doe"

    In this case, the Swift compiler generates a compile-time error:

    Cannot assign to value: 'name' is a 'let' constant

By declaring name as an immutable variable with let, we let the compiler know no code should be allowed to change the content of the variable after its value is initially set.

Tuples

One of Swift's unique language features is its inclusion of tuples. By default, variables and constants store a single value. Tuples allow a variable or constant name to refer to a set of values. While tuples do not exist in many languages, you can think of them as compound values, and they function almost identically to a structure, which is a single named object which can store more than one variable embedded within it.

By using a tuple, we could take the following variable declaration:

var dialCode = 44
var isoCode = "GB"
var name = "United Kingdom"

We could combine it to the following:

var country = (44, "GB", "United Kingdom")

Then we can access the individual members of the tuple as follows:

print(country.0)  // outputs 44
print(country.1)  // outputs GB
print(country.2)  // outputs United Kingdom

Tuple members can also be given individual names, as follows:

var country = (dialCode: 44, isoCode: "GB", name: "Great Britain")

print(country.dialCode)  // outputs 44
print(country.0)              // also outputs 44!
print(country.isoCode)   // outputs GB
print(country.name)       // outputs United Kingdom

Swift functions can accept multiple input parameters, but return only one value. A common use case for a tuple variable type is to include more than one value from a function:

func getCountry() -> (dialCode: Int, isoCode: String, name: String) {
    let country = (dialCode: 44, isoCode: "GB", name: "United Kingdom")
    return country
}

let ret = getCountry()

print(ret)

A second way to return multiple values from a function is to use inout parameters, which allows a function to change the value of an input parameter within that function.

While there are valid use cases for changing inout parameter values, returning a tuple has the advantage of returning a value type—rather than modifying input values.

Note

Tuples behave much like structures—which are predefined compound data types in Swift and many other languages. You may be tempted to use tuples rather than making the extra effort to create structures since they provide similar utility. Be careful not to overuse tuples. They are convenient for ad hoc, lightweight composite data types, but when used in complex programming, use cases can result in code that's more difficult to understand and harder to maintain. Use tuples as they're intended, as a means to bundle a few related components of a data element.

Creating a Tuple

Let's look at creating a tuple. We'll use an Xcode playground to create and use a tuple. Here are the steps:

  1. Launch Xcode as before, and create a new playground named Create a Tuple.playground.

  2. Add the following code to the playground to create a tuple containing a person's name, address and age:

    let person1 = ("John Doe", "201 Main Street", 35)
    print("\(person1.0) lives at \(person1.1) and is \(person1.2) years old.")

    This code is very similar to the previous , except that we've used a tuple to group together values describing John Doe—rather than using separate variables for each element.

    While this syntax is legal, acceptable, and common, it can begin to result in difficult to understand and maintain code—especially when a tuple contains more than two simple values. To make a tuple more maintainable, you can give variable names to each of its components.

  3. Add the following to the playground:

    let person2 = (name: "Jane Doe", address: "301 Fifth Avenue", age: 35)
    print("\(person2.name) lives at \(person2.address) and is \(person2.age) years old.")

    In this second approach, each member of the tuple has a descriptive name, making it easier for the reader of the program to understand and maintain the code.

Optionals

Another unique language feature Swift provides is the optional. In most programming languages, all variables and constants must hold some value. But, in the real world, sometimes a value is unknown. For example, an address may or may not contain a second address line, and more than 60 countries in the world don't use postal codes. Optionals allow variables to indicate whether their value is missing (that is, not assigned), or is truly a  blank value.

Note

When variables are declared optional in Swift, they behave very similarly to column values in SQL database such as Oracle, SQL Server, and MySQL.

Optionality for Swift variables is optional (pun intended). To declare a variable as an optional, add a question mark (?) to the end of its data type (or assign another optional variable's value to it so the optional property is inferred from the existing variable).

The following variable name is not an optional:

var name: String = "Hello"

This next variable name is an optional, and has an initial value of nil:

var name: String?

The presence of the question mark intuitively expresses that the variable may—or may not—contain a string. If the optional is not assigned a value, it will automatically be set to nil, meaning it has no value.

Declaring an Optional

Earlier in this lesson, we declared variables with initial values assigned. These variables are not optional, have a value, and can never be assigned a nil value, or an unwrapped optional variable's value.

In this section, we define a variable as an optional by adding a question mark to the type name, which makes it subject to the Swift compiler's optional validation rules.

A third possibility is to declare a force unwrapped variable—a variable that can be nil, but is not optional. This type of variable is declared by placing an exclamation point (!) after the type (rather than the question mark (?) for the optional), for example:

var customerAge: Int!

When a variable is declared in this fashion, the compiler will allow the variable to be assigned a nil value at any time, but will not warn the programmer at compile time when the variable's value is (or could be) assigned a nil value.

There are limited circumstances where this technique is required, and in general it should be avoided.

Note

Why don't we make all variables optional? Optional is a powerful Swift feature, but working with optional variables requires more code as they are used, primarily to check for nil values before accessing the optional value. In general, you should use optional variables when variables may be missing values, but not use optional variables when you know a variable will always have a value.

Working with Optionals

As mentioned, the simplest way to declare a variable as an optional is to append the data type with a question mark, for example:

var name: String?

Because of Swift's type inference, the following line of code will create a second variable of optional type:

var nameCopy = name

The syntax to assign a value to this variable is the same as it would be if the variable was not declared as optional:

name = "Adam Smith"

The difference between optional and non-optional variables is primarily when you access the value of an optional, which we'll cover next.

Optional nil Values

Optional variables in Swift can be directly compared to the absence of value (nil) and assigned a nil value. For example, in the following two statements, variable a initially has a value of 4, then is assigned a nil value, and then is checked for having a nil value:

var a: Int? = 4
a = nil
if a == nil {
  print("a is nil")
}

While the presence or absence of a value within an optional can be directly tested, extracting and using the value contained within an optional requires that the optional (the envelope) be unwrapped, and the content (value) extracted. We'll learn how to do this next.

Accessing Optional Values

Think of an optional as a value wrapped in an envelope. You cannot access the contents of an envelope without opening it (unwrapping it), and then removing the contents.

You can primarily unwrap an optional and use its value in two ways:

  • Force unwrap

  • Conditional unwrap

We'll learn each of these techniques next.

Force Unwrapping an Optional

Look at the two optional Int variables:

var a: Int?
var b: Int = 4

You could attempt to assign a to b, for example:

b = a

But this would result in a compile-time error:

Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

As the error indicates, accessing the value of an unwrapped optional variable is (always) illegal. One approach to solving this problem is to force unwrap the variable as we use it. To force unwrap a variable, simply place an exclamation mark (!) after the variable name, for example:

b = a!

Force unwrapping is similar to using a type cast in many languages. In Swift, a force unwrap tells the compiler to assume that the optional contains a value.

However, a force unwrap shifts all the responsibility to the programmer for ensuring optionals actually have values. The above example, b = a!, would allow the code to compile, but would generate the following runtime error, and the application will crash:

Fatal error: Unexpectedly found nil while unwrapping an Optional value

Because variable a is an optional with no value, there is no value to extract from it to assign to b.

Note

Force unwrapping should not be viewed as a way to get around compiler type-safety features. Only use force unwrapping when you're absolutely certain that it's impossible for an optional variable to contain a nil value. In the following code, a force unwrap would be acceptable:

var a: Int? = 2
var b: Int = 4
b = a!

Conditionally Unwrapping Optionals

While there are times when force unwrapping variables is safe, you should typically take advantage of Swift's type-safety features by using conditional unwrapping.

With conditional unwrapping, we ask the compiler to first check whether the optional has a value, and return the value if present, or nil if not.

For example, to assign the value of optional a to a new, non-optional variable b, we can use the following code:

var a: Int? = 4
if let b = a {
   print(b)
}

This code snippet would print the value 4 to the console. If we had not assigned the initial value 4 to a, then nothing would have been printed.

Using Optionals

Use an Xcode playground to create and use an optional, by performing the following steps:

  1. Launch Xcode as before, and create a new playground named Using Optionals.playground.

  2. Add the following code to the playground to create an optional containing a person's name:

    var name: String? = nil
  3. Now add the following code to check whether the optional is nil:

    if name == nil {
        print("name is nil")
    } else {
        print("name is not nil")
    }

    Of course, since we assigned the value nil, it is nil.

    A more common way to check for a non-nil optional is to use the if/let syntax covered previously.

  4. Add the following code to assign a value to the optional content, then print it to the console:

    name = "John Doe"
    if let n = name {
        print(n)
    } else {
    print("the name is still nil")

    Because you assigned a value to the variable name, the string John Doe is printed to the console.

  5. Finally, comment out the variable assignment. The output will now change to the name is still nil, because the if/let syntax detected that the variable name contains no value.

The Swift guard Statement

It's very common that Swift functions should only execute when parameters passed to them are in an expected state. In early versions of Swift, the conditional unwrapping technique was often used to provide this type of safety checking. For example, a function that accepts an optional Int value, but should only proceed when the parameter is not nil might look as follows:

func doubleValue(input: Int?) -> Int? {
   if let i = input {
      return i * 2
   }
   return nil
}

While this function is only a few lines of code, imagine if the work done on the unwrapped variable was more complex. To allow parameter and other data state checking to be concisely done at the beginning of functions, Swift includes a guard keyword.

The following is a version of doubleValue that uses the guard syntax to place data state checks at the top of the function:

func doubleValue(input: Int?) -> Int? {
   guard let i = input else { return nil }
   return i * 2
}

This is the end of this section. Here, we have had a deep look at how to declare variables and constants in Swift. We also worked with tuples and optionals.

Activity: Variable Summary

In Swift, variables are declared before being used. Variables can be declared in various ways, and may not even need to have their type explicitly stated when the compiler can infer data type from initial assignment.

Use an Xcode playground to practice how to declare variables, constants, and tuples.

  1. Launch Xcode as before, and create a new playground named Topic B Summary.playground.

  2. Add the following code to the playground to create three variables storing values related to the weather conditions in Berlin:

    let cityName = "Berlin"
    var humidityPercentage: Double?
    var temperatureCentigrade: Double?

    Note that cityName is a constant, non-optional variable, with an initial string value. Since we know the name of the city in advance, and it doesn't change for this program, it's most appropriate to use let to declare this value as a constant.

    humidityPercentage and temperatureCentigrade are declared as optional, since we do not yet know the weather conditions in Berlin at the start of this program.

  3. Next, add the following line of code to create a tuple to collect the weather report data into a single variable named weather:

    var weather = (city: cityName, humidityPercentage: humidityPercentage, temperature: temperatureCentigrade)

    Recall that providing reference names for each tuple member is optional, but is included here to make the remaining part of the program clearer to other programmers who may need to read this program later.

  4. Next, set the value of humidity within the tuple:

    weather.1 = 0.70

    Note that even though you created a reference name for humidity (humidityPercentage), you can still set the value using the ordinal position within the tuple. The following line of code would probably be better in this case:

    weather.humidityPercentage = 0.70
  5. Now print the tuple to the console. On noticing that the variable provided is a tuple, the console print() function prints all members of the tuple—along with the reference names provided:

    print(weather)

    The output of the print statement is as follows:

    (city: "Berlin", humidityPercentage: Optional(0.69999999999999996), temperature: nil)
  6. Finally, print each of the tuple's components, each on its own line:

    print("City: \(weather.city)")
    print("Humidity: \(String(describing:weather.humidityPercentage))")
    print("Temperature: \(String(describing:weather.temperature))")

    The output of this code is as follows:

    City: Berlin
    Humidity: Optional(0.69999999999999996)
    Temperature: nil