Swift 4 Programming Cookbook

3 (3 reviews total)
By Keith Moon
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Swift Building Blocks

About this book

Swift 4 is an exciting, multi-platform, general-purpose programming language. Being open source, modern and easy to use has made Swift one of the fastest growing programming languages. If you interested in exploring it, then this book is what you need.

The book begins with an introduction to the basic building blocks of Swift 4, its syntax and the functionalities of Swift constructs. Then, introduces you to Apple's Xcode 9 IDE and Swift Playgrounds, which provide an ideal platform to write, execute, and debug the codes thus initiating your development process. Next, you'll learn to bundle variables into tuples, set order to your data with an array, store key-value pairs with dictionaries and you'll learn how to use the property observers. Later, explore the decision-making and control structures in Swift and learn how to handle errors in Swift 4.

Then you'll, examine the advanced features of Swift, generics and operators, and then explore the functionalities outside of the standard library, provided by frameworks such as Foundation and UIKit. Also, you'll explore advanced features of Swift Playgrounds. At the end of the book, you'll learn server-side programming aspect of Swift 4 and see how to run Swift on Linux and then investigate Vapor, one of the most popular server-side frameworks for Swift.

Publication date:
September 2017
Publisher
Packt
Pages
384
ISBN
9781786460899

 

Swift Building Blocks

In this chapter, we will cover the following recipes:

  • Your first Swift program
  • Strings, Ints, Floats, and Bools
  • Optionals, unwrap, and force unwrap
  • Functions
  • Object classes
  • Structs
  • Enumerations
  • Closures
  • Protocols
 

Introduction

Since Apple announced the Swift programming language at WWDC 2014, it has gone on to become one of the fastest growing programming language. TIOBE is a company that measures software quality and publishes a ranking index of programming language usage. At the time of writing, Swift ranks the 12th most popular language on this index and has overtaken Objective-C (visit http://www.tiobe.com/tiobe_index).

Swift is a modern, general purpose programming language that focuses on type safety, and an expressive and concise syntax. Seen as a replacement for the aging Objective-C, its use among Mac and iOS developers has skyrocketed, ensuring its place as the future of development on Apple's platforms.

While occupying this niche would alone ensure Swift's place as a useful and important programming language. Apple's decision to open source Swift's runtime and compiler have allowed Swift's influence to extend beyond Apple's ecosystem, giving it the potential to be used across all platforms and for any scenario.

Since open sourcing the Swift toolchain, Apple has provided support for running your Swift code on Linux. In the later chapters, we will investigate using a Swift server to execute your code. In addition, the release of the Swift Playgrounds iPad app, which happened alongside iOS 10, turns your tablet into a lightweight Integrated Development Environment (IDE). However, the simplest way to get up and running with Swift is still on Mac and with Apple's Xcode IDE. Therefore, this book will assume that this is also the development environment of the reader. Xcode also provides a perfect way to explore the structure and syntax of the Swift standard library, foundation, and any other framework available for iOS or Mac development in the form of its Playgrounds feature.

An Swift Playgrounds is a simplified environment for executing Swift code. For our purposes, Playgrounds provide an ideal way to create, execute, debug, and understand the recipes contained in this book. As such, it will also be assumed that the reader is using an Xcode Playground to implement the recipes contained in this book, unless otherwise stated.

Swift 3, released in 2016, presented a major step forward in standardizing the language syntax and, as a result, migrating code written in Swift 2 to Swift 3 was not always an easy task. Swift 4, by contrast, has been designed to be source compatible with Swift 3; therefore, the task of migrating between Swift 3 and Swift 4 should be minimal. Swift 4 has been available in a prerelease form since the beta release of Xcode 9, and will be finalized with the release of Xcode 9 and iOS 11. This book will use Swift 4 throughout, and differences from Swift 3 will be highlighted.

In this chapter, we will look at the building blocks of the Swift language, examining the syntax and functionality of the basic Swift constructs that everything else is based on.

All the code for this chapter can be found in GitHub repository at https://github.com/SwiftProgrammingCookbook/SwiftBuildingBlocks.

 

Your first Swift program

In the first recipe, we will get up and running with Swift using a Swift Playground, and we will run our first piece of Swift code.

Getting ready

To run our first Swift program, we need to download and install our IDE. During the beta of Apple's Xcode 9, it is available as a direct download from Apple's developer website at http://developer.apple.com/download, access to this beta will require a free Apple developer account. Once the beta has ended and Xcode 9 is publicly available, it will also be available from the Mac App Store. By obtaining it from the Mac App Store, you will be informed of updates automatically, so this is the preferred route once Xcode 9 is out of beta.

Downloading Xcode

Follow these steps to download Xcode from the Mac App Store:

  1. Open up the Mac App Store, either from the dock or via Spotlight:
  1. Search for xcode:
  1. Click on Install:
Xcode is a large download (over 4 GB). So, depending on your internet connection, this can take a while.
  1. The progress can be monitored from Launchpad:

Follow these steps to get Xcode as a direct download:

  1. Go to the Apple Developer download page at http://developer.apple.com/download:
  1. Click on the Download button to download Xcode within a .xip file:
  1. Double-click on the downloaded file to unpack the Xcode application.
  1. Drag the Xcode application into your Applications folder:

How to do it...

With Xcode downloaded, let's create our first Swift playground:

  1. Launch Xcode from the icon in your dock.
  2. From the welcome screen, choose Get started with a playground:
  1. From the template chooser, select the blank template from the iOS tab:
  1. Choose a name for your playground and a location to save it:
Xcode Playgrounds can be based on one of the three different Apple platforms: iOS, tvOS, and macOS (the operating system formerly known as OSX). Playgrounds provide full access to the frameworks available to either iOS, tvOS, or macOS, depending on which you choose. An iOS playground will be assumed for the entirety of this book, chiefly because this is the platform of choice of the author. Where recipes do have UI components, the iOS platform will be used until stated otherwise.
  1. You are now presented with a view that looks like this:
  1. Let's replace the word playground with Swift!.
  2. Click on the blue play icon in the bottom left-hand corner of the window to execute the code in the playground:
  1. Congratulations! You have just run some Swift code.
  1. On the right-hand side of the window, you will see the output of each line of code in the playground. We can see that our line of code has output "Hello, Swift!":

There's more...

If you put your cursor over the output on the right-hand side, you will see two buttons: one that looks like an eye and another that is a rounded square:

Click on the eye button to get a Quick Look box of the output. This isn't that useful for just a string, but can be useful for more visual output, such as colors and views:

Click on the square button, and a box will be added in-line, under your code, showing the output of the code. This can be really useful if you want to see how the output changes as you change the code:

See also

We will learn more about playgrounds and how we can take them further in Chapter 6, Swift Playgrounds.

 

Strings, Ints, Floats, and Bools

Some of the most basic operations in Swift, and any programming language, involve manipulating text and numbers, and determining true/false answers.

Getting ready

Let's take a look at the basic types in Swift that enable us to perform these operations. As we do that, we will learn how to assign constants and variables, and touch on Swift's static typing and mutability system.

How to do it...

Let's execute some Swift code that explores the basic types, and then we can walk through it step by step:

  1. Type the following into a new Playground file:
let phrase: String = "The quick brown fox jumps over the lazy dog" 
let numberOfFoxes: Int = 1
let numberOfAnimals: Int = 2

let averageCharactersPerWord: Float = (3+5+5+3+5+4+3+4+3) / 9
print(averageCharactersPerWord) // 43

/*
phrase = "The quick brown ? jumps over the lazy ?" // Doesn't compile
*/

var anotherPhrase = phrase
anotherPhrase = "The quick brown jumps over the lazy "
print(phrase) // "The quick brown fox jumps over the lazy dog"
print(anotherPhrase) // "The quick brown jumps over the lazy "

var phraseInfo = "The phrase" + " has: "
print(phraseInfo) // "The phrase has: "

phraseInfo = phraseInfo + "\(numberOfFoxes) fox and \(numberOfAnimals) animals"
print(phraseInfo) // "The phrase has: 1 fox and 2 animals"

print("Number of characters in phrase: \(phrase.count)")

let multilineExplanation = """
Why is the following phrase often used?
"The quick brown fox jumps over the lazy dog"
This phrase contains every letter in the alphabet.
"""

let phrasesAreEqual = phrase == anotherPhrase
print(phrasesAreEqual) // false

let phraseHas43Characters = phrase.count == 40 + 3
print(phraseHas43Characters) // true
  1. Execute the playground to verify that it compiles and runs without any errors. Your playground should look like the following screenshot, with an output for each line in the timeline on the right-hand side and printed values in the console at the bottom:

How it works...

Let's step through the preceding code line by line to understand it:

 let phrase: String = "The quick brown fox jumps over the lazy dog"

We designate that we want to assign a constant by using the let keyword, and we give that constant a name--phrase. After the :, we define the type of constant we want to assign; in this case, we want to assign a String (String is how most programming languages refer to text). The = sign indicates that we are assigning a value to the constant we have defined, and The quick brown fox jumps over the lazy dog is a String literal, which means that it's an easy way to construct a string. So, we are assigning the String literal on the right-hand side of the = sign to the constant on the left-hand side of the = sign:

let numberOfFoxes: Int = 1 
let numberOfAnimals: Int = 2

Now, we will assign some more constants, but this time they are Int, or integers:

let averageCharactersPerWord: Float = (3+5+5+3+5+4+3+4+3) / 9 

Rather than assigning a value directly, we can assign the outcome from a mathematical expression to the constant. This constant is a Float or floating point number; in other words, it can store fractions rather than integers:

print(averageCharactersPerWord)

The print function allows us to see the output from any expression printed to the console or displayed in the playground. The playground has displayed the output of the statement as 3.88889. The actual value of the mathematical expression we performed is 3.88888888... with an infinite number of 8s. However, the print function has rounded this up to just five decimal places. This potential difference between the true value of a floating point number and how it's represented by the Swift language is important to remember when dealing with floats:

phrase = "The quick brown ? jumps over the lazy ?" // Doesn't compile 

The playground doesn't produce an output for this line because it is commented out. The /* syntax before the line and the */ syntax after the line denote that this is a comment block, so the Swift compiler should ignore anything typed in this block.

Remove /* and */ (you'll see that // Doesn't compile is still highlighted in green; this is because // denotes that anything after this, on the same line, is also a comment); now try and run the code. The code cannot run because the compiler has detected a problem with this line, so let's look at the line to determine the issue.

On the left-hand side of the = sign, we have phrase, which we declared earlier, and now we are trying to assign a new value to it. We can't do this because we defined phrase as a constant using the let keyword. We should only use let for things we know will not change. If we want to define something that can change, we declare it as a variable using the var keyword:

var anotherPhrase = phrase 

Here, we take our constant phrase and assign it to the anotherPhrase variable. This ability to define something as unchanging, or immutable, is an important concept in Swift, and we will revisit in the later chapters:

anotherPhrase = "The quick brown  jumps over the lazy "

Since anotherPhrase is a variable, we can assign a new value to it. Strings in Swift are fully Unicode compliant, so we can have some fun and use emojis instead of words:

print(phrase) // "The quick brown fox jumps over the lazy dog" 
print(anotherPhrase) // "The quick brown jumps over the lazy "

In the preceding lines, we have done the following:

  • Defined a string called phrase
  • Defined a string called anotherPhrase as having the same value as phrase
  • Changed the value of anotherPhrase
  • Printed the value of phrase and anotherPhrase

When we do this, we see that only anotherPhrase prints the new value that was assigned, even though the values of phrase and anotherPhrase were initially the same. Although phrase and anotherPhrase had the same value, they do not have an intrinsic connection; so, when anotherPhrase is assigned a new value, this does not affect phrase.

Strings can be easily combined using the + operator; this gives the expected result:

var phraseInfo = "The phrase" + " has: " 
print(phraseInfo) // "The phrase has: "

You will often want to create strings by including values derived from other expressions. We can do this with String interpolation. The values inserted after \( and before ) can be anything that can be represented as a string, including other Strings, Ints, Floats, or expressions:

phraseInfo = phraseInfo + "\(numberOfFoxes) fox and \(numberOfAnimals) animals" 
print(phraseInfo) // "The phrase has: 1 fox and 2 animals"

Multiline string literals can be defined using """ at the beginning and end of the string. The contents of the multiline string must be on a separate line from the start and end signifiers; there is no need to escape and use " within the string as you would need to do in a single-line string:

let multilineExplanation = """
Why is the following phrase often used?
"The quick brown fox jumps over the lazy dog"
This phrase contains every letter in the alphabet.
"""
Multiline string literals were introduced with Swift 4 and are not available in Swift 3.

Strings in Swift are collections, which are containers of elements; in this case, a string is a collection of characters. We will cover collections in more depth in a later recipe, but for now, it's enough to know that your collections can tell you how many elements they contain through their count property. We use this to output the number of characters in the phrase:

print("Number of characters in phrase: \(phrase.count)") 
The concept of strings acting as collections was introduced with Swift 4. In Swift 3, you could not get a string's character count directly; instead, you could access the character array within the String and get its count property. This means that the preceding print statement would be print("Number of characters in phrase: \(phrase.characters.count)"). In Swift 4, String still has a character array, and therefore code written for Swift 3 will continue to work in Swift 4.

Boolean values represent either true or false. Here, we are assigning the result of the phrase == anotherPhrase equality expression to the phrasesAreEqual constant. The == operator compares the values on the left- and right-hand side of the operator:

let phrasesAreEqual: Bool = phrase == anotherPhrase 
print(phrasesAreEqual) // false

As we discussed earlier, although we assigned anotherPhrase the value of phrase initially, we then assigned a new, different value to anotherPhrase; therefore, phrase does not equal anotherPhrase, and the expression assigns the Boolean value of false:

let phraseHas43Characters: Bool = phrase.characters.count == 40 + 3  
print(phraseHas43Characters) // true

Each side of the == operator can be any expression that evaluates to match the type of the other side. In this case, the character count of phrase equals 43, so the constant is assigned the value of true.

There's more...

While we defined constants and variables earlier, we also defined the type of thing we are assigning to explicitly. For example, consider the following:

let clearlyAString: String = "This is a string literal" 

Swift is a statically typed language, which means any constant or variable that we define has to have a specific type, which cannot be changed. However, in the preceding line, the clearlyAString constant is clearly a String! The right-hand side of the expression is a String literal, and therefore we know that the left-hand side will be a String. More importantly, the compiler also knows this. Swift is all about being concise, so since the type can be inferred by the compiler, we do not need to explicitly state it. Try the following instead, and see whether you can run the code:

let clearlyAString = "This is a string literal" 

In fact, all the type declaration that we have made so far can be removed! So, go back through the code we have already written and remove all type declarations (:String, :Int, :Float, and :Bool), as they can all be inferred.

See also

 

Optionals, unwrap, and force unwrap

In the real world, we don't always know the answer to a problem, and problems can occur if we blindly assume that someone does. The same is true in programming languages, especially when dealing with external systems that we may not control. In many languages, including Objective-C (until recently), there was no way to indicate that something being declared may not exist at the time you attempt to access it. This would lead to either fragile code that could have broken if a nil unexpectedly found its way in or tests being run all over the code to ensure that a value did exist where it was needed, which added complexity and increased the boilerplate code that had to be written.

The nil or null term is used in programming languages to denote the absence of a value, not to be confused with the number 0 as the value or the empty (zero length) string "", they would be something, this... is nothing. Swift uses nil, and this can be assigned to a variable to remove any value currently assigned, replacing it with nil or nothing.

With a focus on Swift being type-safe and making it easier to write safe code, this ambiguity had to be addressed, and the Swift language does this with the use of optionals.

Getting started

Enter the following into the playground:

var dayOfTheWeek: String = "Monday" 
dayOfTheWeek = "Tuesday"
dayOfTheWeek = "Wednesday"
dayOfTheWeek = nil

When you try to run the code, you'll see that the compiler has raised an error and will not let you assign nil to the dayOfTheWeek variable. Quite right too--the day of the week might change, but there will never not be a current day of the week.

As we declared the type to be String, that is what the compiler expects, and nil is not a String, so it can't be assigned to this variable.

The same is true even if you remove the type declaration and have the compiler infer it, as we did in the preceding recipe. This is the type inferred at the point the variable is declared, and since it is being assigned a string value, the type of String is inferred. All other uses of this variable are checked against this inferred type of String.

Delete the last line, as the compiler issue will prevent us from running further code in the playground.

How to do it...

We will look at a different scenario where it is appropriate to have an optional variable. Melody and Finn are playing a game. In each round, Finn will hold his hand behind his back and choose a number of fingers to hold up, Melody will guess how many, and Finn will show her how many fingers he had chosen to hold up.

To help keep track of the game, Melody stores how many fingers Finn has held up in a variable. When Finn shows his hand, Melody can enter a value for the number of fingers, but when Finn's hands are behind his back, Melody doesn't know how many fingers Finn is holding up, and so can't store a value for how many fingers are being held up.

Let's enter the following code:

// Start of the game
var numberOfFingersHeldUpByFinn: Int?
// Finn's hand behind his back
numberOfFingersHeldUpByFinn = nil
// Finn shows his hand
numberOfFingersHeldUpByFinn = 3
// Finn puts hand back behind his back
numberOfFingersHeldUpByFinn = nil
// Finn shows his hand
numberOfFingersHeldUpByFinn = 1
print(numberOfFingersHeldUpByFinn)
// End of the game
let lastNumberOfFingersHeldUpByFinn: Int = numberOfFingersHeldUpByFinn!

Unlike the day of the week example, this code compiles without issues, despite the fact that we assign nil to the variable.

How it works...

We know that there will be time during the game when we don't know how many fingers are being held up, so the variable is optional--it may be an Int or it maybe nil. You will remember from earlier that nil does not mean 0. It is entirely possible that the other player may be holding up zero fingers (that is, a clenched fist) as this is a valid answer. In this scenario, nil represents a lack of knowledge about the number of fingers. To declare this variable as optional, we define the expected type, but with an additional ?:

var numberOfFingersHeldUpByFinn: Int? 

In Swift, this is referred to as an optionally wrapped Int. We have wrapped the Int type in the concept of being optional. I am emphasizing this term wrapping because we will need to unwrap this optional type later on. At the start of the game, we don't know how many fingers are being held up, so we assign nil to this variable, which is allowable for optional variables:

numberOfFingersHeldUpByFinn = nil 

Once Finn's hand is shown and we know how many fingers he has held up, we can assign that Int value to the variable:

numberOfFingersHeldUpByFinn = 3 

Since the variable type is an optional Int, the valid values are either an Int or nil; if we try something of another type, we will get a compiler error:

numberOfFingersHeldUpByFinn = "three" // Doesn't compile because "three" isn't an Int or nil

As we discussed earlier, Swift has a static type system, so the type of a variable can't be changed after it is declared. Therefore, although we have assigned a value of the Int type to the variable, this hasn't changed the variable type to the non-optional Int; its type remains Int?, an optional Int. Since the type is still optional, we can assign it a nil value when Finn puts his hands behind his back again:

numberOfFingersHeldUpByFinn = nil  

When we print an optional variable, the output tells us that it is optional, for example, Optional(1):

numberOfFingersHeldUpByFinn = 1 
print(numberOfFingersHeldUpByFinn)

You will notice that the compiler highlights an issue that says Expression implicitly coerced from 'Int?' to 'Any' on the print line. We see this because we are passing an option call to the print comment, which is expecting a non-optional value. To solve this issue, we can provide a value to use if our optional value happens to be nil; there is actually a really concise way to do this. The ?? operator can be can be applied after an optional value, and the value to the right of the operator will be used if the optional value is nil; this is called the nil coalescing operator:

print(numberOfFingersHeldUpByFinn ?? "Unknown")

At the end of the game, we want to store the number of fingers that Finn held up during the last round of the game. Since we know that we will play at least one round of the game, we know that there must be a value for the last number of fingers that were held up. Therefore, we declare the lastNumberOfFingersHeldUpByFinn variable as a non-optional Int:

let lastNumberOfFingersHeldUpByFinn: Int = numberOfFingersHeldUpByFinn! 

However, our numberOfFingersHeldUpByFinn variable is an optional Int. This is a problem for the compiler and the safeness and stability of our code as we would be applying a value that can be nil at runtime to a variable that is not allowed to be nil. If you remove ! from the preceding statement, that's what we will be doing, and the compiler will complain:

let lastNumberOfFingersHeldUpByFinn: Int = numberOfFingersHeldUpByFinn // Does not compile 

To get around this issue, we need to declare that although we initially declared this variable as optional, we know there is a value assigned to it, so we want to declare that this is now non-optional, which is denoted by the !.

In Swift terminology, we are unwrapping the optionality of the variable. Once it is unwrapped, and therefore non-optional, we can assign it to the lastNumberOfFingersHeldUpByFinn variable, which is also non optional.

Beware! Use of this forced unwrapping can be risky. When you forcibly unwrap an optional, as we just did, you are declaring to the compiler that you are sure that there will be a value in that variable at that point in the execution of the code. If for some reason you don't, and the variable is nil, you will get an error at runtime and your execution will terminate. If this code is running in an app, then the app will crash. We will see safer ways to unwrap an optional value in the later chapters.

Take the counting fingers game described earlier. Imagine that we ended the game before playing the first round:

// Start of the game 
var numberOfFingersHeldUpByFinn: Int?
// Hand behind his back
numberOfFingersHeldUpByFinn = nil
print(numberOfFingersHeldUpByFinn) // nil
// End of the game
let lastNumberOfFingersHeldUpByFinn: Int = numberOfFingersHeldUpByFinn!

This code will compile and run, but will crash at runtime because at the point that numberOfFingersHeldUpByFinn is assigned to lastNumberOfFingersHeldUpByFinn, the value of numberOfFingersHeldUpByFinn is nil.

There's more...

So far, we have seen non-optional variables, where a value of the correct type must be provided, and optional variables, where the value can either be the underlying type or nil. In a perfect world, this would be all we need. However, we may often find ourselves in the situation where we need to define a variable that we know should have a non-nil value, and, when it comes to be used, will have a value, but at the point of declaration, we don't know exactly what that value is. For these situations, we can declare a variable as an implicitly unwrapped optional (IUO) in Swift.

Run the following code in the playground:

var legalName: String!  
// At birth
legalName = nil
// At birth registration
legalName = "Alissa Jones"
// At enrolling in school
print(legalName)
// At enrolling in college
print(legalName)
// Registering Marriage
legalName = "Alissa Moon"
// When meeting new people
print(legalName)

In this example, we have a person's legal name, which is used at many points during their life, for instance, when registering for educational institutions. It can be changed, either by legal request or through marriage, and yet you would never expect someone's legal name to not exist. However, that is exactly what happens when someone is born! When a person is born (or initialized!), they don't have a legal name until their birth is registered. So, if we were trying to model this in code, a person's legal name could be represented as an IUO.

In code, we declare a variable to be an IUO by placing a ! sign after the type:

let legalName: String!  
IUOs present the same risk as forced unwrapping. You are promising that, although it's possible for the variable to be nil, when something tries to access it, a value will be there. If there isn't, the execution will terminate and your app will crash.

There is some subtlety to how IUOs behave when they are assigned to other variables and the type is inferred. Put the following into the playground, as it's best illustrated with code:

var input: Int! = 5 // Int! 
print(input) // 5
var output1 = input // Int?
print(output1 as Any) // Optional(5)
var output2 = input + 1 // Int
print(output2) // 5

When an IUO is assigned to a new variable, the compiler can't be sure that there is a non-nil value assigned. So, if an IUO is assigned to a new variable, as is the case with output1 here, the compiler plays it safe and infers that the type of this new variable is an optional. If, however, the value of the IUO has been unwrapped, then the compiler knows that it has a non-nil value, and will infer a non-optional type. When assigning output2 earlier, the value of the input is unwrapped in order to add 1 to it; therefore, the type of output2 is inferred to be the non-optional Int.

See also

 

Functions

Functions are a building block of almost all programming languages, allowing functionality to be defined and reused. Swift's syntax provides an expressive way to define your functions, creating concise and readable code.

How to do it...

Let's look at how functions are defined in Swift:

func nameOfFunction(parameterLabel1 parameter1: ParameterType1, parameterLabel2 parameter2: ParameterType2,...) -> OutputType { 

// Function's implementaion
// If the function has an output type,
// the function must return a valid value
return output
}

Let's look at this in more detail to see how a function is defined:

  • func : This indicates that you are declaring a function.
  • nameOfFunction : This will be the name of your function, and by convention is written in camel case (this means that each word, apart from the first, is capitalized and all spaces are removed). This should describe what the function does, and should provide some context to the value returned by the function, if one is returned. This will be how you will invoke the method from elsewhere in your code, so bear that in mind when naming it.
  • parameterLabel1 parameter1: ParameterType1 : This is the first input, or parameter, into the function. You can specify as many parameters as you like, separated by commas. Each parameter has a parameter name (parameter1) and type (ParameterType1); the parameter name is how the value of the parameter will be made available to your function's implementation. You can optionally provide a parameter label in front of the parameter name (parameterLabel1 ), which will be used to label the parameter when your function is used (at the call site).
  • -> OutputType : This indicates that the function returns a value and indicates the type of that value. If no value will be returned, this can be omitted. In the following code illustration, the curly brackets indicate the start and end of the function's implementation; anything within them will be executed when the function is called:
{  
// Function's implementaion
}
  • return output : If the function returns a value, you type return and then the value to return. This ends the execution of the function; any code written after the return statement is not executed.

Now, let's put this into action.

How it works...

Imagine that we are building a contacts app to hold the details of your family and friends, and we want to create a string of a contact's full name. Let's explore some of the ways functions can be used:

// Input parameters and output 
func fullName(givenName: String, middleName: String, familyName: String) -> String {
return "\(givenName) \(middleName) \(familyName)"
}

The preceding function takes three string parameters and outputs a string that puts all these together with spaces in between and returns the resulting string. The only thing this function does is take some inputs and produce an output without causing any side effects; this type of function is often called a pure function. To call this function, we enter the name of the function followed by the input parameters within () brackets, where each parameter value is preceded by its label:

let myFullName = fullName(givenName: "Keith", middleName: "David", familyName: "Moon") 
print(myFullName) // Keith David Moon

Since the function returns a value, we can assign the output of this function to a constant or a variable, just like any other expression:

// Input parameters, with a side effect and no output 
func printFullName(givenName: String, middleName: String, familyName: String) {
print("\(givenName) \(middleName) \(familyName)")
}

The next function takes the same input parameters, but its goal is not to return a value. Instead, it prints out the parameters as one string separated by spaces:

printFullName(givenName: "Keith", middleName: "David", familyName: "Moon") 

We can call this function in the same way as the preceding function, although it can't be assigned to anything since it doesn't have a return value:

// No inputs, with an output 
func authorsFullName() -> String {
return fullName(givenName: "Keith", middleName: "David", familyName: "Moon")
}

The preceding function takes no parameters as everything it needs to perform its task is contained within it, although it does output a string. This function calls the fullName function we defined earlier, taking advantage of its ability to produce a full name when given the component names; reusing is the most useful feature that functions provide:

let authorOfThisBook = authorsFullName()

Since authorsFullName takes no parameters, we can execute it by entering the function name followed by empty brackets (), and since it returns a value, we can assign the outcome of authorsFullName to a variable:

// No inputs, no ouput 
func printAuthorsFullName() {
let author = authorsFullName()
print(author)
}

Our final example takes no parameters and returns no value:

printAuthorsFullName()

You can call this function in the same way as the previous functions with no parameters, but there is no return value to assign.

As you can see from the preceding examples, having input parameters and providing an output value are not required when defining a function.

There's more...

Now, let’s look at a couple of ways to make your use of functions more expressive and concise.

Default parameter values

One convenience in Swift is that you can specify default values for parameters, which allows you to omit the parameter when calling. Let's take the preceding example situation, where we are creating a contact app to hold information about our family and friends. Many of your family members are likely to have the same family name as you, so you can set your family name as the default value for that parameter so that you only need specify the family name if it is different from the default.

Let's enter the following code into a playground:

func fullName(givenName: String, middleName: String, familyName: String = "Moon") -> String { 
return "\(givenName) \(middleName) \(familyName)"
}

Defining a default value looks similar to assigning a value to the familyName: String = "Moon" parameter. When calling the function, the parameter with the default value does not have to be given:

let keith = fullName(givenName: "Keith", middleName: "David") 
let alissa = fullName(givenName: "Alissa", middleName: "May")
let laura = fullName(givenName: "Laura", middleName: "May", familyName: "Jones")
print(keith) // Keith David Moon
print(alissa) // Alissa May Moon
print(laura) // Laura May Jones

Parameter overloading

Unlike Objective-C, Swift supports parameter overloading, which allows for functions to have the same name and only be differentiated by the parameters that they take.

Let's learn more about parameter overloading by entering the following code into a playground:

func combine(_ givenName: String, _ familyName: String) -> String { 
return "\(givenName) \(familyName)"
}

func combine(_ integer1: Int, _ integer2: Int) -> Int {
return integer1+integer2
}

let combinedString = combine("Finnley", "Moon")
let combinedInt = combine(5, 10)
print(combinedString) // Finnley Moon
print(combinedInt) // 15

Both the preceding functions have the name combine, but one takes two Strings as parameters and the other takes two Ints. Therefore, when we come to call the function, the compiler knows which implementation we intended by the values we pass as parameters.

We've introduced something new in the preceding function declarations--anonymous parameter labels, _ givenName: String.

When we declare the parameters, we use an underscore _ for the parameter label; this indicates that we don't want a parameter name shown when calling the function. This should only be used if the purpose of the parameters is clear without the labels, as is the case in the preceding example--combine("Finnley", "Moon").

See also

 

Object classes

Object-oriented programming is currently the dominant programming paradigm. At the core of this paradigm is the object class. Objects allow us to encapsulate data and functionality, which can then be stored and passed around.

Getting ready

Let's build some class objects. Then, we'll break down the components of the class to understand how it is defined and used.

How to do it...

Let's start by entering the following code into the playground:

class Person { 

let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"

init(givenName: String, middleName: String, familyName: String) {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
}

var displayString: String {
return "\(fullName()) - Location: \(countryOfResidence)"
}

func fullName() -> String {
return "\(givenName) \(middleName) \(familyName)"
}
}

final class Friend: Person {
var whereWeMet: String?

override var displayString: String {
return "\(super.displayString) - \(whereWeMet ?? "Don't know where we met")"
}
}

final class Family: Person {
let relationship: String

init(givenName: String, middleName: String, familyName: String = "Moon",
relationship: String) {
self.relationship = relationship
super.init(givenName: givenName, middleName: middleName, familyName:
familyName)
}

override var displayString: String {
return "\(super.displayString) - \(relationship)"
}
}

let steve = Person(givenName: "Steven", middleName: "Paul", familyName: "Jobs")
let dan = Friend(givenName: "Daniel", middleName: "James", familyName: "Woodel")
dan.whereWeMet = "Worked together at BBC News"
let finnley = Family(givenName: "Finnley", middleName: "David", relationship: "Son")
let dave = Family(givenName: "Dave", middleName: "deRidder", familyName: "Jones", relationship: "Father-In-Law")
dave.countryOfResidence = "US"

print(steve.displayString) // Steven Paul Jobs
print(dan.displayString) // Daniel James Woodel - Worked together at BBC News
print(finnley.displayString) // Finnley David Moon - Son

How it works...

Classes are defined with the class keyword, class names start with a capital letter by convention, and the implementation of the class is contained, or "scoped", within curly brackets:

class Person {
//...
}

An object can have property values, which are contained within the object. These properties can have initial values, as countryOfResidence does in the following code, although bear in mind that constants (defined with let) cannot be changed once the initial value has been set:

class Person {
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"
//...
}

If your class were to just have the preceding property definitions, the compiler would raise a warning, as givenName, middleName, and familyName are defined as non-optional strings, but we have not provided any way to populate those values.

The compiler needs to know how the object will be initialized, so that we can be sure that all the non-optional properties will indeed have values:

class Person { 
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"

init(givenName: String, middleName: String, familyName: String) {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
}
//...
}

The init is a special method (functions defined within objects are called methods) that's called when the object is initialized. In the Person object of the preceding code, we expect givenName, middleName, and familyName to be passed in when the object is initialized, and we assign those provided values to the object's properties. The self. prefix is used to differentiate between the property and the value passed in as they have the same name.

We do not need to pass in a value for countryOfResidence as we defined an initial value when the property was defined. This isn't ideal, though, as when we initialize a Person object, it will always have the countryOfResidence variable set to "UK", and we will have to change that value after initializing. Another way to do this would be to use a default parameter value, as seen in the previous recipe. Amend the Person object initialization to the following:

class Person { 
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String

init(givenName: String, middleName: String, familyName: String, countryOfResidence:
String = "UK") {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
self.countryOfResidence = countryOfResidence
}
//...
}

Now, you can provide a country of residence in the initialization or omit it to use the default value:

class Person { 
//...
var displayString: String {
return "\(fullName()) - Location: \(countryOfResidence)"
}
//...
}

The property declaration for displayString is different from the others. Rather than having a value assigned to it, it is followed by an expression contained within curly braces. This is a computed property; its value is not static, but is determined by the given expression every time the property is accessed. Any valid expressions can be used to compute the property, but must return a value that matches the property type that is declared. The compiler will enforce this, and you can't omit the variable type for computed properties.

Since the value of the property is determined at the time of access, it follows that computed properties are read-only:

  
class Person {
//...
func fullName() -> String {
return "\(givenName) \(middleName) \(familyName))"
}
//...
}

Objects can do work based on the information they contain, and this work can be defined in methods. Methods are just functions that are contained within classes and have access to all the object's properties. All the abilities of a function are available, which we explored in the last recipe, including optional inputs and outputs, default parameter values, and parameter overloading:

  
final class Friend: Person {
var whereWeMet: String?
//...
}

Having defined a Person object, we want to extend the concept of Person to define a friend. A friend is also a person, so it stands to reason that anything a Person object can do, a Friend object can also do. We model this inherited behavior by defining Friend as a subclass of Person. We define the class that our Friend class inherits from after the class name, separated by :.

By inheriting from Person, our Friend object inherits all the properties and methods from its superclass. We can add any extra functionality we require--in this case, a property holding details of where we met this friend.

The final prefix tells the compiler that we don't intend for this class to be subclassed; it is the final class in the inheritance hierarchy. This allows the compiler to make some optimizations as we know it won't be extended:

final class Friend: Person { 
//...
override var displayString: String {
return "\(super.displayString) - \(whereWeMet ?? "Don't know where we met")"
}
}

In addition to implementing new functionalities, we can override functionalities from the superclass using the override keyword. In the preceding code, we override the displayString computed property from Person as we want to add the "where we met" information. Within the computed property, we want to get the superclass's implementation; we do this by referencing super and ., and then referencing the property. We can do the same to access the superclass's methods:

final class Family: Person {
let relationship: String
init(givenName: String, middleName: String, familyName: String = "Moon", relationship: String) {
self.relationship = relationship
super.init(givenName: givenName, middleName: middleName, familyName: familyName)
}
//...
}

Our Family class also inherits from Person, and we want to add a relationship property, which we want to form part of the initialization, so we can declare a new init that also takes a relationship value.

There's more...

Class objects are reference types that refer to the way they are stored and referenced internally. To see how these reference type semantics work, consider the following code:

class MovieReview { 
let movieTitle: String
var starRating: Int // Rating out of 5
init(movieTitle: String, starRating: Int) {
self.movieTitle = movieTitle
self.starRating = starRating
}
}

// Write a review
let shawshankReviewOnYourWebsite = MovieReview(movieTitle: "Shawshank Redemption", starRating: 3)

// Post it to social media
let reviewLinkOnTwitter = shawshankReviewOnYourWebsite
let reviewLinkOnFacebook = shawshankReviewOnYourWebsite

print(reviewLinkOnTwitter.starRating) // 3
print(reviewLinkOnFacebook.starRating) // 3

// Reconsider my review
shawshankReviewOnYourWebsite.starRating = 5

// The change visible from anywhere with a reference to the object
print(reviewLinkOnTwitter.starRating) // 5
print(reviewLinkOnFacebook.starRating) // 5

We created a review object and assigned that review to two separate constants. As an object is a reference type, it is a reference to the object that is stored in the constant, rather than a new copy of the object. Therefore, when we reconsider our review and rightly give The Shawshank Redemption five stars, we are changing the underlying object, and all references that access that underlying object will see that the starRating property has changed.

See also

  • In Chapter 8, Performance and Responsiveness in Swift, we will examine reference semantics in more detail, and see how it affects performance.
 

Structs

Objects are great for encapsulating data and functionality behind a unifying and referenceable concept, such as a person. However, not everything is an object; we may have a set of data that is logically grouped together, but that isn't much more than that. It's not more than the sum of its parts--it is the sum of its parts.

For this, there are structs. Short for structure, structs can be found in the C programming language and were, therefore, available in Objective-C, which was built on top of C. If you are familiar with iOS/macOS development, CGRect is an example of a C struct.

Structs are value types, as opposed to classes, which are reference types, and as such behave differently when passed around. In this recipe, we will examine how structs work in Swift, and learn when and how to use them.

Getting ready

This recipe will build on top of the previous recipes, so open the playground you have used for the previous recipes. Don't worry if you haven't tried out the previous recipes; this one will contain all the code you need.

How to do it...

We have already defined a Person object as having three separate string properties relating to the person's name; however, these three separate strings don't exist in isolation from each other--they together define a person's name. Currently, to get a person's name, you have to access three separate properties and combine them. Let's tidy this up by defining a person's name as its own struct. Enter the following code into the playground and run the playground:

struct PersonName { 
let givenName: String
let middleName: String
var familyName: String

func fullName() -> String {
return "\(givenName) \(middleName) \(familyName)"
}

mutating func change(familyName: String) {
self.familyName = familyName
}
}
var alissasName = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones")

How it works...

Defining a struct is very similar to defining an object class, and that is intentional. Much of the functionality available to a class is also available to a struct.

Within the PersonName struct, we have properties for the three components of the name and the fullName method we saw earlier to combine the three name components into a full name string.

Next, we have a method to change the family name property, which is why we defined the familyName property as a var variable instead of a let constant. This method assigns a new value to a property of the struct; it is mutating, or changing, the struct, and therefore needs to be marked with the mutating keyword. This keyword is enforced by the compiler to remind us that when we mutate a struct, a new copy of the original struct is created with the new value. This is known as value-type semantics.

To see this in action, consider the following code:

let alissasBirthName = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones") 
print(alissasName.fullName()) // Alissa May Jones
var alissasCurrentName = alissasBirthName
print(alissasName.fullName()) // Alissa May Jones

So far, so good. We have created a PersonName struct and assigned it to a constant called alissasBirthName and a variable called alissasCurrentName.

When we change or "mutate" the alissasCurrentName variable, only this variable is changed; alissasBirthName is a copy, and so it doesn't have the amended family name, even though they were assigned from the same source:

alissasCurrentName.change(familyName: "Moon") 
print(alissasBirthName.fullName()) // Alissa May Jones
print(alissasCurrentName.fullName()) // Alissa May Moon

There's more...

Now that we have a PersonName struct, let's amend our Person class so that it can use it:

class Person { 

let birthName: PersonName
var currentName: PersonName
var countryOfResidence: String

init(name: PersonName, countryOfResidence: String = "UK") {
birthName = name
currentName = name
self.countryOfResidence = countryOfResidence
}

var displayString: String {
return "\(currentName.fullName()) - Location: \(countryOfResidence)"
}
}

We've added the birthName and currentName properties of our new PersonName struct type, and we initiate them with the same value when the Person object is initiated. Since a person's birth name won't change, we define it as a constant, but their current name can change, so it's defined as a variable.

Now, let's create a new Person object:

var name = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones") 
let alissa = Person(name: name)
print(alissa.currentName.fullName()) // Alissa May Jones

Since our PersonName struct has value semantics, we can use this to enforce the behavior that we expect our model to have. We would expect to not be able to change a person's birth name, and if you try, you will find that the compiler won't let you.

As we discussed earlier, changing the family name mutates the struct, and so a new copy is made. However, we defined birthName as a constant, which can't be changed, so the only way we would be able to change the family name would be to change our definition of birthName from let to var:

alissa.birthName.change(familyName: "Moon") // Does not compile. Compiler tells you to change let to var 

When we change the currentName to have a new family name, which we can do, since we defined it as a var, it changes the currentName property, but not the birthName property, even though these were assigned from the same source:

print(alissa.birthName.fullName()) // Alissa May Jones 
print(alissa.currentName.fullName()) // Alissa May Jones
alissa.currentName.change(familyName: "Moon")
print(alissa.birthName.fullName()) // Alissa May Jones
print(alissa.currentName.fullName()) // Alissa May Moon

We have used a combination of objects and structs to create a model that enforces our expected behavior.

See also

 

Enumerations

Enumerations are a programming construct that let you define a value type with a finite set of options. Most languages have enumerations (usually abbreviated to enums), including C and, by extension, Objective-C.

An example of an enum from the iOS/macOS SDK is NSComparisonResult, which you would use when sorting items. When comparing for the purposes of sorting, there are only three possible results from a comparison:

  • ascending : The items are ordered in ascending order
  • descending : The items are ordered in descending order
  • same : The items are the same

There are a finite number of possible options for a comparison result; therefore, it's a perfect candidate for being represented by an enum:

enum ComparisonResult : Int { 
case orderedAscending
case orderedSame
case orderedDescending
}

Swift takes the enum concept and elevates it to a first class type. As we will see, this makes enums a very powerful tool for modeling your information.

This recipe will examine how and when to use enums in Swift.

Getting ready

This recipe will build on top of the earlier recipes, so open the playground you have used for the previous recipes. Don't worry if you haven't tried out the previous recipes; this one will contain all the code you need.

How to do it...

Earlier, we created a Person object to represent people in our model and a PersonName struct to hold information about a person's name. Now, let's turn our attention to a person's title, for example, Mr., Mrs., and so on, which precede someone's full name. There are a small and finite number of common titles that a person may have; therefore, an enum is a great way to model this information.

Enter the following into the playground:

enum Title { 
case mr
case mrs
case mister
case miss
case dr
case prof
case other
}

We define our enumeration with the enum keyword and provide a name for the enum. As with classes and structs, the convention is that this starts with a capital letter, and the implementation is defined within curly brackets. We define each enum option with the case keyword, and the convention since Swift 3 is that these start with a lowercase character. Now that we have defined our enum, let's see how to assign it:

let title1 = Title.mr 

Enums can be assigned by specifying the enum type, then a dot, and then the case. However, if the compiler can infer the enum type, we can omit the type and just provide the case, preceded by a dot:

let title2: Title 
title2 = .mr

How it works...

When used in C and Objective-C, enums are defined as a type definition on top of an integer, with each case being given a defined integer value. In Swift, enums do not need to represent integers under the hood; in fact, they do not need to be backed by any type, and can exist as their own abstract concepts. Consider the following example:

enum CompassPoint { 
case North, South, East, West
}
Note that we can define multiple cases on the same line by separating them with commas.

For Title, an integer-based enum doesn't seem appropriate; however, a string-based one may be. So, let's declare our enum to be string based:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"
}

The enum's raw type is declared after its name and a : separator. The raw types that can be used to back the enum are limited to types that can be represented as a literal; this includes Swift base types--String, Int, Float, and Bool.

These types can be used to back an enum because they conform to a protocol, called RawRepresentable. We will cover protocols later in the chapter.

Cases can be assigned a value of the raw type; however, certain types can be inferred, and so do not need to be explicitly declared. For Int-backed enums, the inferred values are sequentially assigned starting at 0:

enum Rating: Int { 
case worst // Infered as 0
case bad // Infered as 1
case average // Infered as 2
case good // Infered as 3
case best // Infered as 4
}

For string-based enums, the inferred value is the name of the case, so the other case in our Title enum is inferred to be other.

We can get the underlying value of the enum in its raw type by accessing its rawValue property:

let title1 = Title.mr 
print(title1.rawValue) // "Mr"

There's more...

As I mentioned in the introduction to this recipe, Swift treats enums as a first-class type, and therefore they can have functionalities that are not available to enums in most programming languages. These functionalities include having computed variables and methods.

Methods and computed variables

Say that it is important for us to know whether a person's title relates to a professional qualification that the person holds. Let's add a method to our enum to provide that information:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"
func isProfessional() -> Bool {
return self == Title.dr || self == Title.prof
}
}

For the list of titles that we have defined, Dr and Prof relate to professional qualifications, so we have our method return true if self (the instance of the enum type this method is called on) is equal to the dr case, or equal to the prof case.

This functionality feels more appropriate as a computed property since whether it isProfessional or not is intrinsic to the enum itself, and we don't need to do much work to determine the answer. So, let's change this into a property:

enum Title: String { 
case mr = "Mr"
case mrs = "Mrs"
case mister = "Master"
case miss = "Miss"
case dr = "Dr"
case prof = "Prof"
case other // Inferred as "other"

var isProfessional: Bool {
return self == Title.dr || self == Title.prof
}
}

Now, we can determine whether a title is a professional title by accessing the computed property on it:

let loganTitle = Title.mr
let xavierTitle = Title.prof
print(loganTitle.isProfessional) // false
print(xavierTitle.isProfessional) // true

We can't store new information on an enum, but being able to define methods and computed properties that provide extra information about the enum is really powerful.

Associated values

Our string-based enum seems perfect for our title information, except that we have a case called other. If the person has a title that we hadn't considered when defining the enum, we can select other, but that doesn't capture what the other title is. We will need to define a whole other property to hold the value of the other title, but there would be nothing to force it to be used when the other case is selected, and nothing to stop it from being used when a different Title case is selected.

Swift enums have a solution for this situation--associated values; we can choose to associate a value with each enum case, allowing us to bind a non-optional string to our other case.

Let's rewrite our Title enum to use an associated value:

enum Title { 
case mr
case mrs
case mister
case miss
case dr
case prof
case other(String)
}

We have defined the other case to have an associated value by putting the value's type in brackets after the case declaration. We do not need to add associated values wholesale to every case; other case declarations can have associated values of a different type, or none at all.

Enums containing associated values cannot have a raw type as they are now too complex to be represented by one of these base types, so our Title enum is no longer string-based.

Let's look at how we assign an enum case with an associated type:

let mister: Title = .mr
let dame: Title = .other("Dame")

The associated value is declared in brackets after the case, and the compiler enforces that the type matches the type declared in our enum definition. As we declared the other case to have a non-optional string, we are ensuring that a title of other cannot be selected without providing details of what the other title is, and we don't have an unneeded property hanging around when anything else is selected.

See also

 

Closures

Closures are also referred to as anonymous functions, and this is the best way to explain them. Closures are functions that optionally take a set of input parameters, and they optionally return an output, the same as other functions. However, rather than having a function name, closures behave like other primary types--they can be assigned, stored, passed around, and used as the input and output to functions and other closures.

Getting ready

We will continue to build on our contacts app example from earlier in this chapter, so you should use the same playground as in the previous recipes. Don't worry if you haven't followed the previous recipes; all the code needed is listed here, so you can enter this in a new playground.

How to do it...

If you are implementing this in a new playground, enter the following:

struct PersonName { 
let givenName: String
let middleName: String
var familyName: String

func fullName() -> String {
return "\(givenName) \(middleName) \(familyName)"
}

mutating func change(familyName: String) {
self.familyName = familyName
}
}

class Person {

let birthName: PersonName
var currentName: PersonName
var countryOfResidence: String

init(name: PersonName, countryOfResidence: String = "UK") {
birthName = name
currentName = name
self.countryOfResidence = countryOfResidence
}

var displayString: String {
return "\(currentName.fullName()) - Location: \(countryOfResidence)"
}
}

You will need to go back over the previous recipes to see how these were created.

Now, let's put a number of closure types in the playground so we examine them:

// No input, no output 
let printAuthorsDetails: () -> Void = {
let name = PersonName(givenName: "Keith", middleName: "David", familyName: "Moon")
let author = Person(name: name)
print(author.displayString)
}
printAuthorsDetails() // "Keith David Moon - Location: UK"

// No input, Person output
let createAuthor: () -> Person = {
let name = PersonName(givenName: "Keith", middleName: "David", familyName: "Moon")
let author = Person(name: name)
return author
}
let author = createAuthor()
print(author.displayString) // "Keith David Moon - Location: UK"

// String inputs, no output
let printPersonsDetails: (String, String, String) -> Void = { givenName, middleName, familyName in
let name = PersonName(givenName: givenName, middleName: middleName, familyName: familyName)
let author = Person(name: name)
print(author.displayString)
}
printPersonsDetails("Kathyleen", "Mary", "Moon") // "Kathleen Mary Moon - Location: UK"

// String inputs, Person output
let createPerson: (String, String, String) -> Person = { givenName, middleName, familyName in
let name = PersonName(givenName: givenName, middleName: middleName, familyName: familyName)
let person = Person(name: name)
return person
}
let melody = createPerson("Melody", "Margaret", "Moon")
print(melody.displayString) // "Melody Margaret Moon - Location: UK"

How it works...

Let's take a look at the different types of closures we just implemented:

// No input, no output 
let printAuthorsDetails: () -> Void = {
let name = PersonName(givenName: "Keith", middleName: "David", familyName: "Moon")
let author = Person(name: name)
print(author.displayString)
}

As a first-class type in Swift, closures can be assigned to constants or variables, and constants and variables need a type. To define a closure's type, we need to specify the input parameter types and the output type, and for the closure in the preceding code, the type is () -> Void. The Void type is another way of saying nothing, so this closure takes no inputs and returns nothing, and the closure's functionality is defined within the curly brackets, as with other functions.

Now that we have this closure defined and assigned to the printAuthorsDetails constant, we can execute it as follows, which will cause this author's details to be printed:

printAuthorsDetails() // "Keith David Moon - Location: UK" 

The next closure type takes no input parameters, but returns a Person object, as you can see with the () -> Person type definition:

// No input, Person output 
let createAuthor: () -> Person = {
let name = PersonName(givenName: "Keith", middleName: "David", familyName: "Moon")
let author = Person(name: name)
return author
}
let author: Person = createAuthor()
print(author.displayString) // "Keith David Moon - Location: UK"

Since it has an output, the execution of the closure returns a value and can be assigned to a variable or constant. In the preceding code, we execute the createAuthor closure and assign the output to the author constant. Since we defined the closure type as () -> Person, the compiler knows that the output type is a Person and therefore the type of the constant can be inferred, so we don't need to explicitly declare it. Let's remove the explicit type declaration:

let author = createAuthor() 
print(author.displayString) // "Keith David Moon - Location: UK"

Next, let's take a look at a closure that takes input parameters:

// String inputs, no output 
let printPersonsDetails: (String, String, String) -> Void = { given, middle, family in
let name = PersonName(givenName: given, middleName: middle, familyName: family)
let author = Person(name: name)
print(author.displayString)
}
printPersonsDetails("Kathleen", "Mary", "Moon") // "Kathleen Mary Moon - Location: UK"

You will remember, from the recipe on functions, that we can define parameter labels, which define how the parameters are referenced when the function is used, and parameter names, which define how the parameter is referenced from within the function. In closures, these are defined a bit differently:

  • Parameter labels cannot be defined for closures, so, when calling a closure, the order and parameter type have to be used to determine what values should be provided as parameters:
(String, String, String) -> Void 
  • Parameter names are defined inside the curly brackets, followed by the in keyword:
given, middle, family in 

Putting it all together, we can define and execute a closure with inputs and an output, as follows:

// String inputs, Person output 
let createPerson: (String, String, String) -> Person = { given, middle, family in
let name = PersonName(givenName: given, middleName: middle, familyName: family)
let person = Person(name: name)
return person
}
let melody = createPerson("Melody", "Margaret", "Moon")
print(melody.displayString) // "Melody Margaret Moon - Location: UK"

There's more...

We've seen how we can store closures, but we can also use them as method parameters. This pattern can be really useful when we want to be notified when a long-running task is completed.

Let's imagine that we want to save the details of our person object to a remote database, maybe for backup or use on other devices. We'll amend our Person class to include this save functionality, and in the process, see how we can pass in a closure, store it, and execute it at a later time.

Add the following code to our Person class:

class Person { 
//....
var saveHandler: ((Bool) -> Void)?

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

We define an optional variable to hold onto the save handler during the long-running save operation. Our closure will take a Bool to indicate whether the save was a success:

var saveHandler: ((Bool) -> Void)? 

Let's define a method to save our Person object, which takes a closure as a parameter:

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) { 
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

Our function stores the given closure in the variable, and then starts the process to save to the remote database (the actual implementation of this is outside the scope of this recipe). This save process will call the saveComplete method when completed. We added a modifier, @escaping, just before the closure type definition. This tells the compiler that, rather than using the closure within this method, we intend to store the closure and use it later. The closure will be escaping the scope of this method. This modifier is needed to prevent the compiler from doing certain optimizations that would be possible if the closure was nonescaping.

With the save operation complete, we can execute the saveHandler variable, passing in the success boolean. However, since we stored the closure as optional, we need to unwrap it by adding a ? after the variable name. If saveHandler has a value, the closure will be executed; if it is nil, the expression is ignored:

func saveComplete(success: Bool) { 
saveHandler?(success)
}

Now that we have a function that takes a closure, let's see how we call it:

let dave = createPerson("David", "Ernest", "Moon") 
dave.saveToRemoteDatabase(handler: { success in
print("Saved finished. Successful: \(success))")
})

Swift provides a more concise way to provide closures to functions. When a closure is the last (or only) parameter, Swift allows it to be provided as a trailing closure--that is, the parameter name can be dropped and the closure can be specified after the parameter brackets. So we can, instead, do the following:

dave.saveToRemoteDatabase { success in 
print("Saved finished. Successful: \(success))")
}

See also

 

Protocols

Protocols are a way to describe the interface that a type provides; they can be thought of as a contract, defining how you can interact with instances of that type. They are a great way to abstract the "what" something does from "how" it does it. As we will see in the subsequent chapters, Swift adds functionalities to protocols, which make them even more useful and powerful.

Getting ready

We will continue to build on examples from the previous recipes, but don't worry if you haven't followed these yet as all the code you need is listed in the upcoming sections.

How to do it...

In the last recipe, we added a method to our Person class to save it to a remote database. This is a very useful functionality, and as we add more and more features to our app, we will likely have more types where we will want to save instances of that type to a remote database. Let's create a protocol to define how we will interface with anything that can be saved in this way:

protocol Saveable {
var saveNeeded: Bool { get set }
func saveToRemoteDatabase(handler: @escaping (Bool) -> Void)
}

How it works...

Protocols are defined with the protocol keyword, and the implementation is contained within curly brackets. It is conventional to begin a protocol name with a capital letter, and to name a protocol as either something that the type is or something that it does; in this protocol, we are declaring that any type implementing it is saveable.

Types implementing this protocol have two parts of the interface to implement.

The Saveable protocol declares that anything implementing it needs to have a variable called saveNeeded, which is a Bool. This property will indicate that the information held in the remote database is out of date and a save is needed. In addition to the usual property declaration, a protocol requires us to define whether the property can be accessed (get) and changed (set), which is added in curly brackets after the type declaration. Removing the set keywords makes it a read-only variable. Consider the given example:

var saveNeeded: Bool { get set } 
Defining a protocol property as read-only doesn't prevent an implementing type from allowing the property to be set, just that the setting of that property isn't defined in the interface.

The second part of our protocol definition is to describe the method we can call to save the information to the remote database. This func declaration is exactly the same as other function declarations we have seen; however, the implementation of the function, usually contained in the curly brackets, is omitted as this is provided by the implementing type:

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) 

Now that we have defined our protocol, we need to implement the Saveable protocol on our Person class that we have been using throughout this chapter:

class Person: Saveable { 
//....
var saveHandler: ((Bool) -> Void)?

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

We conform to a protocol, and in a similar way, we declare that an object inherits from another object, by adding the protocol name after the type name, separated by :. By adding this conformance, the compiler will complain that our Person object doesn't implement part of the protocol as we haven't declared a saveNeeded property, so let's add that:

class Person: Saveable { 
//....
var saveHandler: ((Bool) -> Void)?
var saveNeeded: Bool = true

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

We'll add a default value of true since when an instance of this object is created, it won't be in the remote database, and so it will need saving.

There's more...

Protocol conformance can be applied to classes, structs, enums, and even other protocols, allowing an instance to be stored and passed without needing to know how it's implemented under the hood. This provides many benefits, including testing using mock objects and changing implementations without changing how and where the implementations are used.

Let's add a feature to our app that lets us set a reminder for a contact's birthday, which we will also want to save to our remote database.

We can use class inheritance to give our reminder the save functionality, but a reminder should not have the same features and functionality as a person, and our process for saving a reminder may be different to that used for a person.

Instead, we can create our Reminder object and have it conform to the Saveable protocol:

class Reminder: Saveable { 

var dateOfReminder: String // There is a better to store dates, but this suffice currently.
var reminderDetail: String // eg. Alissa's birthday

init(date: String, detail: String) {
dateOfReminder = date
reminderDetail = detail
}
var saveHandler: ((Bool) -> Void)?
var saveNeeded: Bool = true

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send reminder information to remove database
// Once remote save is complete, it calls saveComplete(success: Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

Our Reminder object conforms to Saveable and implements all the requirements.

We now have two objects that represent very different things and have different functionalities, but they both implement Saveable, and therefore we can treat them in a common way.

To see this in action, let's create an object that will manage the saving of information in our app:

class SaveManager { 
func save(_ thingToSave: Saveable) {
thingToSave.saveToRemoteDatabase { success in
print("Saved! Success: \(success))")
}
}
}
let colin = createPerson("Colin", "Alfred", "Moon") // This closure was covered in the previous recipe
let birthdayReminder = Reminder(date: "27/11/1982", detail: "Colin's Birthday")
let saveManager = SaveManager()
saveManager.save(colin)
saveManager.save(birthdayReminder)

In the preceding example, our SaveManager doesn't know the underlying types that it is being passed, but it doesn't need to. It receives instances that conform to the Saveable protocol, and can, therefore, use the interface provided by Saveable to save each object.

See also

About the Author

  • Keith Moon

    Keith Moon is an award-winning iOS developer, author and speaker based in London. He has worked with some of the biggest companies in the world to create engaging and personal mobile experiences. Keith has been developing in Swift since its release, working on projects both fully Swift, and mixed Swift and Objective-C. Keith has been invited to speak about Swift development in conferences from Moscow to Minsk and London.

    Browse publications by this author

Latest Reviews

(3 reviews total)
Clear, concise, and useable for both study and reference. I found this book to provide a good balance of basic and advanced material for someone with enough programming experience to not need extremely detailed explanations of concepts. The writing style is easy to read and not long-winded.
Pact publishing publishes anything under the sun so it’s not far fetched to find low quality publications available. I certainly didn’t want to feel this way entirely but after purchasing over 7 different titles, I have determined the authors write fluff and I wouldn’t recommend anyone else to purchase from here if they are looking to actually learn content. They will have a better chance learning those concepts on free blogs of advanced developers.
I guess I do not like a cookbook...
Swift 4 Programming Cookbook
Unlock this book and the full library for FREE
Start free trial