The Swift Handbook

Written by Flavio Copes. Updated July 2022

Download

1. Introduction to Swift

The Swift programming language was created by Apple in 2014, and it’s the official language to work with all the Apple Operating Systems lineup: iOS, iPadOS, watchOS, macOS, tvOS.

Swift is an open source, general purpose, compiled programming language.

It is a statically typed programming language.

Every value has a type assigned. The type of a value is always checked when used as argument or returned, at compile time. If there is a mismatch, the program will not compile.

The compiler of Swift is LLVM, and it is included within Xcode, the standard IDE used to work with Apple software development.

Swift is a modern programming language that was designed to “fit” in an ecosystem that was previously designed for a different programming language, called Objective-C.

Most of the software running on the iPhone and Mac today is based on Objective-C code, even for official Apple apps, although Swift usage is gaining traction year after year, and while Objective-C will be used for years to maintain and improve existing apps, new applications are likely going to be created with Swift.

Before Apple introduced Swift, Objective-C was heavily developed to introduce new capabilities and features, but in the recent years this effort has decreased a lot in favor of Swift development.

This does not mean Objective-C is dead or not worth learning: Objective-C is still an essential tool for any Apple developer.

That said, I am not going to cover Objective-C here, because we’re focusing on Swift.

The present and future of the Apple platform.

In just 6 years, Swift has gone through 5 major versions, and we’re now (at the time of writing) at version 5.

Swift is famously Apple’s products language, but it is not an Apple-only language. We can use it on several other platforms. It is open source, so porting the language to other platforms does not require any permission or licensing, and you can find Swift projects to create Web servers and APIs (https://github.com/vapor/vapor) as well as projects to interact with microcontrollers.

It is a general-purpose language, built with modern concepts, and it has a bright future.

The goal of this book is to get you up and running with Swift, starting from zero.

If you have a Mac or an iPad I recommend you to download the Playgrounds application made by Apple from the App Store. This app lets you run snippets of Swift code without having to create a full app first. It’s a very handy way to test your code, not just when you start learning, but all the times you need to try some code.

And it contains a series of awesome examples and tutorials to expand your Swift and iOS knowledge.

2. Variables

Variables let us assign a value to a label, and are defined using the var keyword:

var name = "Roger"
var age = 8

Once a variable is defined, we can change its value:

age = 9

Variables that you do not want to change can be defined as constants, using the let keyword:

let name = "Roger"
let age = 8

Changing the value of a constant is forbidden.

When you define a variable and you assign it a value, Swift implicitly infers the type of it.

8 is an Int value.

"Roger" is a String value.

A decimal number like 3.14 is a Double value.

You can also specify the type at initialization time:

let age: Int = 8

but it’s usual to let Swift infer it, and it’s mostly done when you declare a variable without initializing it.

You can declare a constant, and initialize it later:

let age : Int

age = 8

Once a variable is defined, it is bound to that type, and you cannot assign to it a different type, unless you explicitly convert it.

You can’t do this:

var age = 8
age = "nine"

Int and String are just two of the built-in data types provided by Swift.

3. Objects

In Swift, everything is an object. Even the 8 value we assigned to the age variable is an object.

In some languages, objects are a special type. But in Swift, everything is an object and this leads to one particular feature: every value can receive messages.

Each type can have multiple functions associated to it, which we call methods.

For example, talking about the 8 number value, we can call its isMultiple method, to check if the number is a multiple of another number:

A String value has another set of methods.

A type also has instance variables. For example the String type has the instance variable count, which gives you the number of characters in a string:

Swift has 3 different object types, which we’ll see more in details later on: classes, structs and enums.

Those are very different, but they have one thing in common: to object type, we can add methods, and to any value, of any object type, we can send messages.

4. Operators

We can use a wide set of operators to operate on values.

We can divide operators in many categories. The first is the number of targets: 1 for unary operators, 2 for binary operators or 3 for the one and only ternary operator.

Then we can divide operators based on the kind of operation they perform:

  • assignment operator
  • arithmetic operators
  • compound assignment operators
  • comparison operators
  • range operators
  • logical operators

plus some more advanced ones, including nil-coalescing, ternary conditional, overflow, bitwise and pointwise operators.

Note: Swift allows you to create your own operators and define how operators work on your types you define.

4.1. Assignment operator

The assignment operator is used to assign a value to a variable:

var age = 8

Or to assign a variable value to another variable:

var age = 8
var another = age

4.2. Arithmetic operators

Swift has a number of binary arithmetic operators: +, -, *, / (division), % (remainder):

1 + 1 //2
2 - 1 //1
2 * 2 //4
4 / 2 //2
4 % 3 //1
4 % 2 //0

- also works as a unary minus operator:

let hotTemperature = 20
let freezingTemperature = -20

+ is also used to concatenate String values:

"Roger" + " is a good dog"

4.3. Compound assignment operators

The compound assignment operators combine the assignment operator with arithmetic operators:

  • +=
  • -=
  • *=
  • /=
  • %=

Example:

var age = 8
age += 1

4.4. Comparison operators

Swift defines a few comparison operators:

  • ==
  • !=
  • >
  • <
  • >=
  • <=

You can use those operators to get a boolean value (true or false) depending on the result:

let a = 1
let b = 2

a == b //false
a != b //true
a > b // false
a <= b //true

4.5. Range operators

Range operators are used in loops. They allow us to define a range:

0...3 //4 times
0..<3 //3 times

0...count //"count" times
0..<count //"count-1" times

Here’s a sample usage:

let count = 3
for i in 0...count {
  //loop body
}

4.6. Logical operators

Swift gives us the following logical operators:

  • !, the unary operator NOT
  • &&, the binary operator AND
  • ||, the binary operator OR

Sample usage:

let condition1 = true
let condition2 = false

!condition1 //false

condition1 && condition2 //false
condition1 || condition2 //true

Those are mostly used in the if conditional expression evaluation:

if condition1 && condition2 {
  //if body
}

5. Conditionals:

5.1. if

if statements are the most popular way to perform a conditional check. We use the if keyword followed by a boolean expression, followed by a block containing code that is ran if the condition is true:

let condition = true
if condition == true {
    // code executed if the condition is true
}

An else block is executed if the condition is false:

let condition = true
if condition == true {
    // code executed if the condition is true
} else {
    // code executed if the condition is false
}

You can optionally wrap the condition validation into parentheses if you prefer:

if (condition == true) {
    // ...
}

And you can also just write:

if condition {
    // runs if `condition` is `true`
}

or

if !condition {
    // runs if `condition` is `false`
}

One thing that separates Swift from many other languages is that it prevents bugs caused by erroneously doing an assignment instead of a comparison. This means you can’t do this:

if condition = true {
    // The program does not compile
}

and the reason is that the assignment operator does not return anything, but the if conditional must be a boolean expression.

5.2. switch

Switch statements are a handy way to create a conditional with multiple options:

var name = "Roger"

switch name {
case "Roger":
    print("Hello, mr. Roger!")
default: 
    print("Hello, \(name)")
}

When the code of a case ends, the switch exits automatically.

A switch in Swift needs to cover all cases. If the tag, name in this case, is a string that can have any value, we need to add a default case, mandatory.

Otherwise with an enumeration, you can simply list all the options:

enum Animal {
    case dog
    case cat
}

var animal: Animal = .dog

switch animal {
case .dog:
    print("Hello, dog!")
case .cat:
    print("Hello, cat!")
}

A case can be a Range:

var age = 20

switch age {
case 0..<18:
    print("You can't drive!!")
default: 
    print("You can drive")
}

5.3. Ternary conditional

The ternary conditional operator is a shorter version of an if expression. It allows us to execute an expression if a condition is true, and another expression if the condition is false.

Here is the syntax:

`condition` ? `value if true` : `value if false`

Example:

let num1 = 1
let num2 = 2

let smallerNumber = num1 < num2 ? num1 : num2 

// smallerNumber == 1

The syntax is shorter than an if statement, and sometimes it might make more sense to use it.

6. Loops

6.1. for-in

for-in loops can be used to iterate a specific amount of times, using a range operator:

for index in 0...3 {
  //iterate 4 times, `index` is: 0, 1, 2, 3
}

You can iterate over the elements of an array or set:

let list = ["a", "b", "c"]
for item in list {
  // `item` contains the element value
}

And on the elements of a dictionary:

let list = ["a": 1, "b": 2, "c": 2]
for (key, value) in list {
  // `key` contains the item key
  // `value` contains the item value
}

6.2. while

A while loop can be used to iterate on anything, and will run while the condition is true:

while [condition] {
    //statements...
}

The condition is checked at the start, before the loop block is executed.

Example:

var item = 0
while item <= 3 { //repeats 3 times
    print(item)
    item += 1
}

6.3. repeat-while

A repeat-while loop in Swift is similar to the while loop, but in this case the condition is checked at the end, after the loop block, so the loop block is executed at least once. Then the condition is checked, and if it is evaluated as true, the loop block is repeated:

repeat {
    //statements...
} while [condition]

Example:

var item = 0
repeat { //repeats 3 times
    print(item)
    item += 1
} while item < 3

6.4. continue and break

Swift provides you 2 statements that you can use to control the flow inside a loop: continue and break

continue is used to stop the current iteration, and run the next iteration of the loop.

break ends the loop, not executing any other iteration.

Example:

let list = ["a", "b", "c"]
for item in list {
  if (item == "b") {
    break
  }
  //do something
}

7. Comments

A comment in Swift can take 2 forms: a single-line comment, and a multi-line comment.

A single-line comment is

//this is a comment

and it can be put at the end of a line of code:

let a = 1 //this is a comment

A multi-line comment is written using this syntax:

/* this
 is
    a multi-line
 comment
*/

Swift allows to nest multi-line comments:

/* this
 is
    a /* nested */ multi-line
 comment
*/

which is handy especially when commenting out large portions of code that already contains multi-line comments.

8. Semicolons

In Swift, semicolons are optional.

You can write statements on separate lines, and you don’t need to add a semicolon:

let list = ["a", "b", "c"]
var a = 2

You can add a semicolon, but it adds nothing meaningful in this case:

let list = ["a", "b", "c"];
var a = 2;

But if you want to write more than one statement on the same line, then you need to add a semicolon:

var a = 2; let b = 3

9. Numbers

In Swift, numbers have 2 main types: Int and Double.

An Int is a number without decimal point. A Double is a number with decimal point.

Both use 64 bits, on modern computers that work with 64 bits, and 32 bit on 32-bit platforms.

The range of values they can store depends on the platform used, and can be retrieved using the int property of each type:

Then, in addition to Int and Double, we have lots of other numeric types, mostly used to interact with APIs built in the past and that needed to interact with C or Objective-C, and you must be aware that we have them:

  • Int8 is an integer with 8 bits

  • Int16 is an integer with 16 bits

  • Int32 is an integer with 32 bits

  • Int64 is an integer with 64 bits

  • UInt8 is an unsigned integer with 8 bits

  • UInt16 is an unsigned integer with 16 bits

  • UInt32 is an unsigned integer with 32 bits

  • UInt64 is an unsigned integer with 64 bits

UInt is like Int, but unsigned, and it ranges from 0 to Int.max * 2.

Float is a decimal number with 32 bits.

Then using Cocoa APIs you might use other numeric types like CLong, CGFloat, and more.

You will always use Int or Double in your code, and use those specific types to particular cases.

Any of those types can always be converted to Int and Double types, instantiating a number passing the value inside parentheses to Double() or Int():

let age : UInt8 = 3
let intAge = Int(age)

You can also convert a number from Double to Int:

let age = Double(3)
let count = Int(3.14)

10. Strings

Strings are one of the most popular tools in programming.

In Swift, a string can be defined using the string literal syntax:

let name = "Roger"

We use double quotes. Single quotes are not valid string delimiters.

A string can span over multiple lines, using 3 double quotes:

let description = """
	a long
  	long 
      	long description
	"""

You can use string interpolation to embed an expression in a string:

let age = 8

let name = """
	Roger, age \(age)
	Next year he will be \(age + 1)
	"""

Concatenate two strings with the + operator:

var name = "Roger"
name = name + " The Dog"

Append text to a string with the += operator:

var name = "Roger"
name += " The Dog"

Or using the append(_:) method:

var name = "Roger"
name.append(" The Dog")

You can count the characters in a string using the count string property:

let name = "Roger"
name.count //5

Any string comes with a set of useful methods, for example:

  • removeFirst() to remove the first character
  • removeLast() to remove the last character
  • lowercased() to get a new string, lowercased
  • uppercased() to get a new string, uppercased
  • starts(with:) which returns true if the string starts with a specific substring
  • contains() which returns true if the string contains a specific character

and many, many more.

When you need to reference an item into the string, since strings in Swift are unicode, we can’t simply reference the letter o in let name = "Roger" using name[1]. You need to work with indexes.

Any string provides the starting index with the startIndex property:

let name = "Roger"
name.startIndex //0

To calculate a specific index in the string, you calculate it using the index(i:offsetBy:) method:

let name = "Roger"
let i = name.index(name.startIndex, offsetBy: 2)
name[i] //"g"

The index can be used also to get a substring:

let name = "Roger"
let i = name.index(name.startIndex, offsetBy: 2)
name.suffix(from: i) //"ger"

//Or using the subscript:

name[i...] //"ger"

When you get a substring from a string, the type of the result is Substring, not String.

let name = "Roger"
let i = name.index(name.startIndex, offsetBy: 2)
print(type(of: name.suffix(from: i))) 
//Substring

Substrings are more memory efficient, because you do not get a new string, but the same memory structure is used behind the scenes, although you need to be careful when you deal with strings a lot, as there are optimizations you can implement.

Strings are collections, and they can be iterated over in loops.

11. Booleans

Swift provides the Bool type, which can have two values: true and false.

var done = false
done = true

Booleans are especially useful with conditional control structures like if statements or the ternary conditional operator:

var done = true

if done == true {
    //code
}

12. Arrays

We use arrays to create a collection of items.

In this example we create an array holding 3 integers:

var list = [1, 2, 3]

We can access the first item using the syntax list[0], the second using list[1] and so on.

Elements in an array in Swift must have the same type.

The type can be inferred if you initialize the array at declaration time, like in the case above.

Otherwise the type of values an array can include must be declared, in this way:

var list: [Int] = []

Another shorthand syntax is:

var list = [Int]()

You can also explicit the type at initialization, like this:

var list: [Int] = [1, 2, 3]

A quick way to initialize an array is to use the range operator:

var list = Array(1...4) //[1, 2, 3, 4]

To get the number of items in the array, use the count property:

var list = [1, 2, 3]
list.count //3

If an array is empty, its isEmpty property is true.

var list = [1, 2, 3]
list.isEmpty //false

You can append an item at the end of the array using the append() method:

var list: [Int] = [1, 2, 3]
list.append(4)

or you can an item at any position of the array using insert(newElement: <Type> at: Int):

var list: [Int] = [1, 2, 3]
list.insert(17, at: 2)
//list is [1, 2, 17, 3]

An array must be declared as var to be modified. If it’s declared with let, you cannot modify it by adding or removing elements.

To remove one item from the array, use remove(at:) passing the index of the element to remove:

var list: [Int] = [1, 2, 3]
list.remove(1)
//list is [1, 3]

removeLast() and removeFirst() are two handy ways to remove the last and first element.

To remove all items from the array, you can use removeAll() or you can assign an empty array:

var list: [Int] = [1, 2, 3]
list.removeAll()
//or
list = []

The sort() method sorts the array:

var list = [3, 1, 2]
list.sort()
//list is [1, 2, 3]

There are a lot more methods, but those are the basic ones.

Arrays are equal when they contain the same elements, of the same type:

[1, 2, 3] == [1, 2, 3] //true

Arrays are passed by value, which means if you pass an array to a function, or return it from a function, the array is copied.

Arrays are collections, and they can be iterated over in loops.

13. Sets

Sets are used to create collections of non-repeated items.

While an array can contain many times the same item, you only have unique items in a set.

You can declare a set of Int values in this way:

let set: Set<Int> = [1, 2, 3]

or you can initialize it from an array:

let set = Set([1, 2, 3])

Add items to the set using insert():

var set = Set([1, 2, 3])
set.insert(17)

Unlike arrays, there is no order or position in a set. Items are retrieved and inserted randomly.

The way to print the content of a set ordered is to transform it into an array using the sorted() method:

var set = Set([2, 1, 3])
let orderedList = set.sorted()

To check if a set contains an element, use the contains() method:

var set = Set([1, 2, 3])
set.contains(2) //true

To get the number of items in the set, use the count property:

let set = Set([1, 2, 3])
set.count //3

If a set is empty, its isEmpty property is true.

let set = Set([1, 2, 3])
set.isEmpty //false

To remove one item from the array, use remove() passing the value of the element:

var set = Set([1, 2, 3])
set.remove(1)
//set is [2, 3]

To remove all items from the set, you can use removeAll():

set.removeAll()

Sets, like arrays, are passed by value, which means if you pass it to a function, or return it from a function, the set is copied.

Sets are great to perform set math operations like intersection, union, subtracting, and more.

These methods help with this:

  • intersection(_:)
  • symmetricDifference(_:)
  • union(_:)
  • subtracting(_:)
  • isSubset(of:)
  • isSuperset(of:)
  • isStrictSubset(of:)
  • isStrictSuperset(of:)
  • isDisjoint(with:)

Sets are collections, and they can be iterated over in loops.

14. Dictionaries

We use dictionaries to create a collection of key-value pairs.

Here is how to create a dictionary with 1 key-value pairs, where the key is a String and the value is an Int:

var dict = ["Roger": 8, "Syd": 7]

In this case the type is inferred. You can also explicitly set the type at declaration time:

var dict: [String: Int] = ["Roger": 8, "Syd": 7]

In this example we create an empty dictionary of Int keys and String values:

var dict = [String: Int]()

//or

var dict: [String: Int] = [:]

You can access the value assigned to a key using this syntax:

var dict = ["Roger": 8, "Syd": 7]

dict["Roger"] //8
dict["Syd"] //7

You can change the value assigned to a key in this way:

dict["Roger"] = 9

A dictionary must be declared as var to be modified. If it’s declared with let, you cannot modify it by adding or removing elements.

Use the same syntax to add a new key/value pair:

dict["Tina"] = 4

To remove a key/value paid, assign the value to nil:

dict["Tina"] = nil

Or call the removeValue(forKey:) method:

dict.removeValue(forKey: "Tina")

To get the number of items in the dictionary, use the count property:

var dict = ["Roger": 8, "Syd": 7]
dict.count //2

If a dictionary is empty, its isEmpty property is true.

var dict = [String: Int]()
dict.isEmpty //true

There are a lot of methods related to dictionaries, but those are the basic ones.

Dictionaries are passed by value, which means if you pass it to a function, or return it from a function, the dictionary is copied.

Dictionaries are collections, and they can be iterated over in loops.

15. Tuples

Tuples are used to group multiple values into a single collection. For example we can declare a variable dog containing a String and an Int value:

let dog : (String, Int)

And we can initialize them with a name and an age

let dog : (String, Int) = ("Roger", 8)

But as with any other variable, the type can be inferred during initialization:

let dog = ("Roger", 8)

You can use named elements:

let dog = (name: "Roger", age: 8)

dog.name //"Roger"
dog.age //8

Once a tuple is defined, you can decompose it to individual variables in this way:

let dog = ("Roger", 8)
let (name, age) = dog

and if you need to just get one of the values, you can use the special underscore keyword to ignore the other ones:

let dog = ("Roger", 8)
let (name, _) = dog

Tuples are an awesome tool for various needs.

The most obvious one is a short way to group similar data.

Another one if those needs is returning multiple items from a function. A function can only return a single item, so a tuple is a convenient structure for that.

Another handy functionality allowed by tuples is swapping elements:

var a = 1
var b = 2

(a, b) = (b, a)

//a == 2
//b == 1

16. Optionals and nil

Optionals are one key feature of Swift.

When you don’t know if a value will be present or absent, you declare the type as an optional.

The optional wraps another value, with its own type. Or maybe not.

We declare an optional adding a question mark after its type, like this:

var value: Int? = 10

Now value is not an Int value. It’s an optional wrapping an Int value.

To find out if the optional wraps a value, you must unwrap it.

We do so using an exclamation mark:

var value: Int? = 10
print(value!) //10

Swift methods often return an optional. For example the Int type initializer accepts a string, and returns an Int optional:

This is because it does not know if the string can be converted to a number.

If the optional does not contain a value, it evaluates as nil, and you cannot unwrap it:

nil is a special value that cannot be assigned to a variable. Only to an optional:

You typically use if statements to unwrap values in your code, like this:

var value: Int? = 2

if let age = value {
    print(age)
}

17. Enumerations

Enumerations are a way to group a set of different options, under a common name.

Example:

enum Animal {
    case dog
    case cat
    case mouse
    case horse
}

This Animal enum is now a type.

A type whose value can only be one of the cases listed.

If you define a variable of type Animal:

var animal: Animal

you can later decide which value to assign it using this syntax:

var animal: Animal
animal = .dog

We can use enumerations in control structures like switches:

enum Animal {
    case dog
    case cat
    case mouse
    case horse
}

let animal = Animal.dog

switch animal {
case .dog: print("dog")
case .cat: print("cat")
default: print("another animal")
}

Enumerations values can be strings, characters or numbers.

You can also define an enum on a single line:

enum Animal {
    case dog, cat, mouse, horse
}

And you can also add type declaration to the enumeration, and each case has a value of that type assigned:

enum Animal: Int {
    case dog = 1
    case cat = 2
    case mouse = 3
    case horse = 4
}

Once you have a variable, you can get this value using its rawValue property:

enum Animal: Int {
    case dog = 1
    case cat = 2
    case mouse = 3
    case horse = 4
}

var animal: Animal
animal = .dog

animal.rawValue //1

Enumerations are a value type. This means they are copied when passed to a function, or when returned from a function. And when we assign a variable pointing to an enumeration to another variable.

18. Structures

Structures are one essential Swift concepts.

Structures are everywhere in Swift. Even the built-in types are structures.

We can create instances of structures, which we call objects.

In most languages, objects can only be created from classes. Swift has classes, too, but you can create objects also from structures and the official documentation advises to prefer structures because they are easier to use.

They are a light versions of classes.

A struct can have properties. A struct can have methods (functions) A struct can define subscripts A struct can define initializers A struct can conform to protocols A struct can be extended

One important thing classes allow is inheritance, so if you need that, you have classes.

A struct is defined using this syntax:

struct Dog {

}

Inside a structure you can define stored properties:

struct Dog {
    var age = 8
    var name = "Roger"
}

This structure definition defines a type. To create a new instance with this type, we use this syntax:

let roger = Dog()

Once you have an instance, you can access its properties using the dot syntax:

let roger = Dog()
roger.age
roger.name

The same dot syntax is used to update a property value:

roger.age = 9

You can also create a struct instance passing the values of the properties:

let syd = Dog(age: 7, name: "Syd")
syd.age
syd.name

To do so, properties must be defined variables, with var, not as constants (with let). It’s also important to respect the order those properties are defined.

Structures can have instance methods: functions that belong to an instance of a structure.

struct Dog {
    var age = 8
    var name = "Roger"
    func bark() {
        print("\(name): wof!")
    }
}

And we also have type methods:

struct Dog {
    var age = 8
    var name = "Roger"
    func bark() {
        print("\(name): wof!")
    }
    static func hello() {
        print("Hello I am the Dog struct")
    }
}

Invoked as Dog.hello()

Structures are a value type. This means they are copied when passed to a function, or when returned from a function. And when we assign a variable pointing to a structure to another variable.

This also means that if we want to update the properties of a structure we must define it using var and not let.

All types in Swift are defined as structures: Int, Double, String, arrays and dictionaries, and more, are structures.

19. Classes

Classes are a bit similar to structures, but they have some key differences.

A class is defined using this syntax:

class Dog {

}

Inside a class you can define stored properties:

class Dog {
    var age = 0
}

A class definition defines a type. To create a new instance with this type, we use this syntax:

let roger = Dog()

Once you have an instance, you can access its properties using the dot syntax:

let roger = Dog()
roger.age

The same dot syntax is used to update a property value:

roger.age = 9

One big difference is that classes are reference types. Structures (and enumerations) are value types.

This means that assigning a class instance to another variable does not copy the instance. Both variables point to the same instance:

class Dog {
    var age = 0
}

let roger = Dog()
let syd = roger

roger.age = 9
//syd.age == 9

This also means we can define a reference to a class using let, and we can change its properties, as you saw in the example above.

We can create instances of classes, and we call them objects.

As with structs, classes can have properties, methods, and more.

Contrary to structs, we must define an initializer in order to initialize the values when we create an instance:

class Dog {
    var age : Int
    
    init(age: Int) {
        self.age = age
    }
}

let roger = Dog(age: 9)

You can only declare properties without initializing them if you have an initializer.

See the use of self. We need it because age is both an instance property and the init(age:) method parameter. self.age references the age instance property.

Classes can have instance methods: functions that belong to an instance of a class.

class Dog {
    var age = 8
    var name = "Roger"

    func bark() {
      print("\(name): wof!")
    }
}

And we also have type methods:

class Dog {
    var age = 8
    var name = "Roger"

    func bark() {
        print("\(name): wof!")
    }
    static func hello() {
        print("Hello I am the Dog struct")
    }
}

Invoked as Dog.hello()

One important thing classes allow is inheritance.

A class can inherit all the properties and methods from another class.

Say we have a class Animal. Every animal has an age:

class Animal {
    var age: Int
}

Not every animal has a name. Dogs have a name. So we create a Dog class extending from Animal:

class Dog: Animal {
    var name: String
}

Now we must add an initializer for both classes. In the Dog case, after we do the class-specific initialization, we can call the parent class initializer using super.init():

class Animal {
    var age: Int
    
    init(age: Int) {
        self.age = age
    }
}

class Dog: Animal {
    var name: String
    
    init(age: Int, name: String) {
        self.name = name
        super.init(age: age)
    }
}

var horse = Animal(age: 8)
var roger = Dog(age: 8, name: "Roger")

Animal is now a superclass, and Dog is a subclass.

There’s more to say about classes, but this is a good introduction.

20. Functions

Your program’s code is organized into functions.

A function is declared using the func keyword:

func bark() {
    print("woof!")
}

Functions can be assigned to structures, classes and enumerations, and in this case we call them methods.

A function is invoked using its name:

bark()

A function can return a value:

func bark() -> String {
    print("woof!")
	  return "barked successfully"
}

And you can assign it to a variable:

let result = bark()

A function can accept parameters. Each parameter has a name, and a type:

func bark(times: Int) {
    for index in 0..<times {
        print("woof!")
    }
}

The name of a parameter is internal to the function.

We use the name of the parameter when we call the function, to pass in its value:

bark(times: 3)

When we call the function we must pass all the parameters defined.

Here is a function that accepts multiple parameters:

func bark(times: Int, repeatBark: Bool) {
    for index in 0..<times {
        if repeatBark == true {
            print("woof woof!")
        } else {
            print("woof!")
        }            
    }
}

In this case you call it in this way:

bark(times: 3, repeat: true)

When we talk about this function, we don’t call it bark(). We call it bark(times:repeat:).

This is because we can have multiple functions with the same name, but different set of parameters.

You can avoid using labels by using the _ keyword:

func bark(_ times: Int, repeatBark: Bool) {
    //...the function body
}

So you can invoke it in this way:

bark(3, repeat: true)

It’s common in Swift and iOS APIs to have the first parameter with no label, and the other parameters labeled.

It makes for a nice and expressive API, when you design the names of the function and the parameters nicely.

You can only return one value from a function. If you need to return multiple values, it’s common to return a tuple:

func bark() -> (String, Int) {
    print("woof!")
	  return ("barked successfully", 1)
}

And you can assign the result to a tuple:

let (result, num) = bark()

print(result) //"barked successfully"
print(num) //1

Functions can be nested inside other functions. When this happens, the inner function is invisible to outside the outer function.

21. Protocols

A protocol is a way to have different objects, of different types, have a common set of functionality.

A protocol is defined in this way:

protocol Mammal {

}

Structs and classes can adopt a protocol in this way:

struct Dog: Mammal {

}

class Cat: Mammal {

}

A protocol can define properties and methods, without providing values and implementations, and a struct/class must implement them:

protocol Mammal {
    var age: Int { get set }
    func walk()
}

The property can be defined as get or get set. If it’s get, the property must be implemented as read only, with a getter.

Any type that adopts the protocol must conform to the protocol by implementing those methods or providing those properties:

struct Dog: Mammal {
    var age: Int = 0
    func walk() {
        print("The dog is walking")
    }
}

class Cat: Mammal {
    var age: Int = 0
    func walk() {
        print("The cat is walking")
    }
}

Structs and classes can adopt multiple protocols:

struct Dog: Mammal, Animal {

}

class Cat: Mammal, Animal {

}

Notice that for classes, this is the same syntax used to define a superclass. If there is a superclass, list it as the first item in the list, after the colon.

22. Where To Go From Here

I hope this little handbook was useful to shine a light on how to get started with Swift, and I hope you are now interested to learn more about it!

I can now point you to two places to learn more: