Jump to content

Swift Introduction/Printable version

From Wikibooks, open books for an open world


Swift Introduction

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Swift_Introduction

Permission is granted to copy, distribute, and/or modify this document under the terms of the Creative Commons Attribution-ShareAlike 3.0 License.

Introduction

Introduction

[edit | edit source]

What is Swift?

[edit | edit source]

Swift is an open source programming language, introduced by Apple in 2014. It is used for their operating systems macOS for desktops and iOS for mobile devices, for instance iPhone or iPad. Since 2015 Swift has also been available on Linux under the Apache Licence 2.0[1]. It is a modern language that includes multiple paradigms like object orientation, functional- and imperative programming. Apple included useful features from other languages like C#, Ruby, Python or Haskell.

Swift on Linux

[edit | edit source]

Install

[edit | edit source]

Before you can start with Swift, you first have to download and install the compiler and other components[2]. Those can be downloaded from Swift's download page. On Linux, clang[3] has to be installed too using following command:

$ sudo apt-get install clang

IDE for Linux

[edit | edit source]

Xcode is only available on macOS. However, there are alternatives which provide similar functionality:

  • JetBrain's CLion offeres a Swift extension which features code highlighting and completion and also a debugger[4].
  • The Open-Source Editor Atom offeres a plugin that works similar to Xcode's playground where you can easily try new code and immediatly see what it does[5].

First example

[edit | edit source]

Before we dive right into Swift's basic and advanced features, let's have a look at the world-famous "Hello World" example.

print("Hello World")

Now, let's have a first glance at what variables and constants look like.

var name: String = "John"
let greeting: String = "Welcome to the world of Swift, "
print(greeting + name)
// Welcome to the world of Swift, John

References

[edit | edit source]
  1. Apache Software Foundation [online][accessed: 18.09.2017] | https://www.apache.org/licenses/LICENSE-2.0
  2. Swift.org [online][accessed: 18.09.2017] | https://swift.org/getting-started/
  3. clang [online][accessed: 18.09.2017] | https://clang.llvm.org
  4. JetBrain Swift Plugin [online][accessed: 18.09.2017] | https://blog.jetbrains.com/clion/2015/12/swift-plugin-for-clion/
  5. Atom.io Swift Playground [online][accessed: 18.09.2017] | https://atom.io/packages/swift-playground


SwiftBasics

Swift Basics

[edit | edit source]

In this chapter, you will learn how to use the basic concepts of Swift[1], including variables, constants and arrays. Another important topic covered in this chapter is how functions and classes are written in Swift.

Variables and Constants

[edit | edit source]

Like in most other languages, Swift uses variables to store values and to refer to them by an unique name. The values of these variables can be mutable or immutable. It's good practice to use constants whenever the value of it does not have to be changed in the code later. This also makes the code you writer safer.

Declaring a variable

[edit | edit source]

In Swift, variables are declared with the keyword var. It is recommended to use var only if you need to change the value of the variable later.

var greeting = "Good morning!"

Declaring a constant

[edit | edit source]

If it is not necessary to change a variable's value it can be declared with the keyword let.

let answerToEverything = 42

Type Annotations

[edit | edit source]

Type annotations can be used to be clear about what kind of values a constant or a variable can store. This is necessary if there is no initial value. If an initial value is provided, Swift infers which type the variable should have.

var greeting: String				
//Declaring a single variable	

let alpha, beta, gamma: Double 		
//Declaring 3 variables in one line

Changing Values

[edit | edit source]

If a variables value should be changed, it has to be of the same type of the original value. In this example, the value of greeting is changed to another String "Good evening".

var greeting = "Good morning!"				
greeting = "Good evening!"	
//greeting has now the value "Good evening!"		
let pi = 3.1415
pi = 3.2
// Compile-time error: This value cannot be changed


Datatypes

[edit | edit source]

Type Safety and Type Inference

[edit | edit source]

Swift is a type-safe language, which means for example it is not possible to assign a number to a variable that stores a String or to call a function with parameters of the wrong type. At compile-time Swift performs type checks and finishes without error only when there are no mismatched types.

If you declare a variable or a constant without specifying the type Swift uses Type Inference to find out the appropriate type. If you provide a floating-point number when declaring a variable, Swift inferes it to be a value of the type Double.

let absolutZero = -273.15				
// absolutZero is inferred to be of type Double

Integer

[edit | edit source]

Integers are whole numbers like 45 or -37. They do not have a fractional component and are signed or unsigned. If an Integer is signed it can be zero, a positive or a negative number. Unsigned Integers can only be zero or positive. Swift provides Integer Types in 8, 16, 32 and 64 bit form. Although it is possible to pick a specific size, for example UInt8 or Int32, in most cases the Int type is used. It has the same size as the platform's native word size. In modern Operating Systems it has a size of 64 Bit. Uint is an unsigned Integer Type which also has the size of the platform's word size.

Floating Point Numbers

[edit | edit source]

Floating-point numbers which have a fractional component can represent values which are much larger or much smaller than the possible values of an Int. Numbers like -0.0001 or 19.99 can be stored in variables of the type Double or Float. Double, a 64 Bit floating-point number, has a precision of at least 15 decimal digits. Float provides a precision of 6 decimal digits.

Booleans

[edit | edit source]

In Swift, the Boolean datatype is called Bool and can have the constant values true or false.

var switchedOn = true				
// switchedOn is now true and of type Boolean
switchedOn = false

Boolean values work great with control flow operations like if statements:

if switchedOn{				
    print("The light is on!")
    // Will be executed if switchedOn = true
}
else{
    print("It's dark as a dungeon!")
}

Optionals

[edit | edit source]

Optional variables have two possible states: There is a value or there is no value. For declaring a variable with an optional datatype a ? is used.

var salary: Double?				
// salary is set to nil automatically and contains no value
salary = 1923.12
// salary now has a value but is still of type Double?
salary = nil
// salary is set back to nil and contains no value

To find out whether a value has already been set or not you can use an if statement and the == (equal to) or != (not equal to) operators.

if salary == nil{				
    print("Salary for this employee has not been set yet!")
} else {
    print("This employee's salary is \(salary!)")
}


In the example above you can see an exclamation mark at the end of the optionals name. It is used to unwrap the value of the optional to use it. If a ! is used on an optional which contains no value a runtime error will be triggered.

Optional Binding

[edit | edit source]

Optional binding checks whether an optional is set, and if so, its value is set to a new variable or constant which is temporarily available.

if let netSalary = salary{				
    print("This emplyee's net salary is \(netSalary*0.85)")
} else {
    print("Salary for this employee has not been set yet!")
}

The new constant netSalary is only available within the if-Statement and does not have to be unwrapped.

Collection Types

[edit | edit source]

In Swift there are three primary collection types.

  • Array for an ordered collection of values
  • Set for an unordered collection of unique values
  • Dictionary for unordered key - value pairs

Just like variables and constants, all three collection types are type safe, which means it is not possible to insert a value of the wrong type. The positive aspect of this type safety is, that you always know which type the value you get from a collection has.

Arrays, sets and dictionaries that are assigned to a variable are mutable, which means values can be added, changed or deleted after they were created. If you need a immutable collection type it has to be assigned to a constant.

Array

[edit | edit source]

Arrays provide an ordered list of elements of the same type. There are two ways to declare an array, Array<Element> or [Element]. Element is the type of the values that should be stored in the array. Arrays can be created empty or filled with values. It's also possible to create the Array with a default value.

var intArray = Array<Int>()				
// creates an empty Integer Array
var preinitialized = Array(repeating: 1, count: 5)
print(preinitialized)
// prints "[1, 1, 1, 1, 1]"

Arrays can also be initialized with array literals. This is a list of values, separated by commas.

let fibonacci = [1,1,2,3,5,8,13]				
// creates an immutable Integer Array

Properties and Methods of Arrays

[edit | edit source]
intArray.append(9)				
// Adds '9' to intArray

intArray += [1,2,3]
// emptyInt now contains 9, 1, 2 and 3

print(intArray.count)				
// prints the number of elements in the array, 4

if(intArray.isEmpty){
    print("I'm an empty Array! :( ")
} else {{
    print("I'm storing \(intArray.count) elements!")
}

Subscript syntax is used to access the elements in an array. The index starts at zero, which means for accessing the first element of your array you have to add [0] to the array's name.

var firstValue = intArray[0]				
// firstValue is now 9

A set stores values of the same type and is used when the order of the items is not important. Sets also ensure, that no duplicate values appear. Values can only be stored in a set, if they are hashable. Swift's basic types like String, Int and Bool are all hashable by default.

	var devices = Set<String>()	
	// creates a new empty set	

	devices.insert("iPhone")	
	print(devices)	
	// prints ["iPhone"]	

	var webSuffixes: Set<String> = [".html", ".js", ".css"]	
	print(webSuffixes)	
	// prints [".js", ".html", ".css"]	

	print("There are \(webSuffixes.count) common suffixes in web development.")	
	// prints There are 3 common suffixes in web development.	

	if webSuffixes.contains(".py"){	
	    print("Yeah, Python made it to the top 3 :)")	
	} else {	
	    print("Python is not in the top 3 :( ")	
	}	
	// prints "Python is not in the top 3 :( "	

	for suffix in webSuffixes{	
	    print ("\(suffix)")	
	}	
	// .css	
	// .js	
	// .html

Sets offer a huge amount of set operations. These operations implement the most important rules of the mathematical set theory.

  • intersection
  • symmetricDifference
  • union
  • subtracting

The following snippet shows how these operations work.

let favSongs: Set = ["Enter Sandman", "Bohemian Rapsody", "Blitzkrieg Bop", "Painkiller"]
let songsFrom90s: Set = ["Raining Blood","Enter Sandman","Painkiller","Wonderwall"]

var playList = Set<String>()
playList = favSongs.union(songsFrom90s)
/* union combines the values of both sets, values which are in both sets will be displayed once.
 
["Blitzkrieg Bop", "Raining Blood", "Bohemian Rapsody", "Enter Sandman", "Painkiller", "Wonderwall"]*/

playList = favSongs.intersection(songsFrom90s)
/* intersect creates a new list which contains all values that exist in both Sets ["Enter Sandman", "Painkiller"] */

playList = favSongs.symmetricDifference(songsFrom90s)
/* symmetricDifference stores all values of both sets into a new set, except those which were in both sets.
["Blitzkrieg Bop", "Raining Blood", "Bohemian Rapsody", "Wonderwall"]*/

playList = favSongs.subtracting(songsFrom90s)
/*subtracting creates a new set which only includes values which are not in the second set. ["Blitzkrieg Bop", "Bohemian Rapsody"] */

Dictionary

[edit | edit source]

Dictionaries store Key - Value pairs in an unordered collection. The key is an unique identifier for the value. Similar to arrays, there are two ways to declare a dictionary. Dictionary<Key, Value> or [Key: Value]. Moreover dictionaries can also be created using dictionary literals.

var offeredStudies = [String: String]()
// creates an empty [String: String] dictionary

offeredStudies["SWD"] = "Software-Design"
offeredStudies["ITM"] = "Internettechnik"
// adds two key-value pairs to the dictionary

offeredStudies["ITM"] = "Internettechnik und Mediendesign"
// changes the value of "ITM"

print(offeredStudies)
// prints ["SWD": "Software-Design", "ITM": "Internettechnik und Mediendesign"]

var locations: [String: String] = ["K": "Kapfenberg", "G": "Graz", "B": "Bad Gleichenberg"]
// creates a dictionary with a dictionary literal

print("FH Joanneum has \(locations.count) locations in Styria.")
// prints "FH Joanneum has 3 locations in Styria."


for (shortcut, location) in locations{
    print("\(shortcut) is a shortcut for \(location)")
// G is a shortcut for Graz
// K is a shortcut for Kapfenberg
// B is a shortcut for Bad Gleichenberg
}


Control Flow

[edit | edit source]

Swift provides a lot of ways to control the way your code is executed.

For-In Loop

[edit | edit source]

Using a For-In Loop is an easy way to iterate over an array, a set or any other type of sequence or range.

let grades: [Int: String] = [1: "Sehr Gut", 2: "Gut", 3: "Befriedigend", 4: "Genuegend", 5: "Nicht genuegend"]for (grade, word) in grades.sorted(by: <){
    print("\(grade) is a \"\(word)\"")
}
// 1 is a "Sehr Gut"
// 2 is a "Gut"
// 3 is a "Befriedigend"
// 4 is a "Genuegend"
// 5 is a "Nicht genuegend"

In a typical For-In Loop the counter always increases by 1. If you want to make smaller or bigger steps, you have to use stride.

let max = 100
for quarters in stride(from: 25, to: max, by: 25){
    print(quarters)
}
// 25
// 50
// 75

While Loop

[edit | edit source]

While Loops are very useful if you do not know how many iterations you need. A while loop executes statements as long as a condition is true. There are two types of while loops.

  • while checks whether the condition is true before the statements are executed.
  • repeat-while executes the statements and checks if the condition is still true at the end.
var cardValues = [Int]()
for value in 1...10{
    cardValues.append(value)
// creates an array with values 1 to 10 - simulates a card deck
}

var bankCount = 0
while bankCount < 21 {
    var randomIndex = Int(arc4random_uniform(UInt32(cardValues.count)))
    // creates a random number - simulates picking a random card
    bankCount += cardValues[randomIndex]
    print(bankCount)
    
    if(bankCount > 21){
        print("The bank loses!")
    }
    else if(bankCount == 21){
        print("The bank wins!")
    }
}

The if statement is the simplest way to decide which statements should be executed, based on certain conditions. It is used if there are just a few possible conditions. The if statemant can stand alone, but it's best practice to always provide an else statement as well, because it makes the code more readable and understandable. === Switch ===

var switchedOn = true

if(switchedOn == true){
    print("All lights are on")
} else {
    print("Can someone please turn on the light?")
}

It is also possible to provide different code paths for more than just one condition:

var score = 88;

if(score > 90 && score <= 100){
    print("Perfect!")
} else if(score > 80 && score <= 90){
    print("Good Job!")
} else if(score > 70 && score <= 80){
    print("Not Bad!")
} else if(score > 60 && score <= 70){
    print("Puh, that was close!")
} else{
    print("Good luck next time")
}

As you can see, a growing number of conditions leads to a lot of duplicated code. A switch statement can be used to reduce the amount of duplicated code.

Switch

[edit | edit source]

A switch statement usually has several possible cases for the condition it checks. All switch statements have to be exhaustive, which means every possibe value of the condition has to have a case. Therefor a default statement should be provided which is executed if none of these cases fit.

var grade = "A"
switch grade{
case "A":
    print("Excellent")
case "B":
    print("Above average")
case "C":
    print("Satisfactory")
case "D":
    print("Below Average")
case "F":
    print("Failure")
default:
    print("Test not attempted")
}


Functions

[edit | edit source]

Functions are an important part of your code. They have an identifying name - it's best practice to use a name which describes what the function does - which is used when you call the function. They can have zero to many parameters. Those input values are passed in as soon as you call the function.

Defining and Calling Functions

[edit | edit source]

In the code snippet below, you can see the definition of a function begins with fund followed by the name and an optional parameter list. The -> operator specifies the return type of this function. A function which does not have the arrow in the definition does not have a return value.

func combineStrings(begin: String, end: String) -> String{
    let combinedString = begin + end
    return combinedString
}

print(combineStrings(begin: "Let's get ", end: "swifty!"))
// prints "Let's get swifty!"

Parameters and Return Values

[edit | edit source]

Functions can have zero to many parameters and also zero to many return values. In the example below, you can see a function which takes two integer values as arguments and returns two integer values. The second function has no arguments and no return value.

func sumAndDifference(value1: Int, value2: Int) -> (sum: Int, difference: Int){
    let sum = value1 + value2
    let difference = value1 - value2
    return (sum, difference)
}

func printTimestamp(){
    let date = Date() //gets the current date
    let calendar = Calendar.current
    let hour = calendar.component(.hour, from: date)
    let minutes = calendar.component(.minute, from: date)
    print(String(hour) + ":" + String(minutes))
}

printTimestamp()
// prints hours and minutes whenever it is called
print(sumAndDifference(value1: 10, value2: 5))
// prints "(sum: 15, difference: 5)"

Argument Labels and Parameter Names

[edit | edit source]

In Swift, parameters have an argument label which is used when calling the function and a parameter name, which is used in the implementation.

func getsFined(allowed speed: Double, measured value: Double) -> String {
    if(speed < value){
        return "Driver was too fast - that's gonna be expensive"
    }
    else{
        return "Good boy"
    }
}

print(getsFined(allowed: 100, measured: 120))
// prints "Driver was too fast - that's gonna be expensive"

It is also possible to write functions without argument labels.

func add2Numbers(_ number1: Int, _ number2: Int) ->Int{
    return number1 + number2
    
}
print(add2Numbers(4,8))
// 12

Variadic Parameters

[edit | edit source]

These parameters accept a variable number of arguments of the same type. It is especially useful when you do not know the exact number of arguments you want to pass to the function or the number of required arguments changes from one function call to the next.

func calcCart(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price
    }
    return String(sum)
}

print("The items in your cart cost " + calcCart(10.99, 9.99, 5.69))
// prints "The items in your cart cost 26.67"

Function Types

[edit | edit source]

A function's type consists of the parameters types and its return type. Let's take a look at the function type of one of the functions from the snippets above.

func getsFined(allowed speed: Double, measured value: Double) -> String

This function is made up from two arguments of type Double and a return type String. Therefor the function type is (Double, Double) -> String

Functions can also have function types as return values.

func calcTaxFood(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price*0.1
    }
    return String(sum)
}

func calcTaxNonFood(_ prices: Double...) -> String {
    var sum: Double = 0
    for price in prices{
        sum += price*0.2
    }
    return String(sum)
}

func chooseTaxCalculator(isFood: Bool) ->(Double...) -> String {
    return isFood ? calcTaxFood : calcTaxNonFood
    // if isFood is true, calcTaxFood will be returned 
    // it it is false, calcTaxNonFood will be returned
}
let taxFood = chooseTaxCalculator(isFood: true)(19.99, 12.99, 6.79)
let taxNonFood = chooseTaxCalculator(isFood: false)(9.99, 1.99, 14.99)
print("You paid " + taxFood + "Euro taxes for your food and " + taxNonFood + "Euro for the rest.")

Nested Functions

[edit | edit source]

Functions which are defined inside another function are called nested functions. They are not visible outside the function. However, they can be used outside the function if the enclosing function returns it.

func itemCounter(incoming: Bool) -> (Int) -> Int {
    
    func increaseCount(count: Int) -> Int{
        print("\(count) items were added to our current stock")
        return itemsOnStock + count
    }
    
    func decreaseCount(count: Int) -> Int{
        print("\(count) items were shipped to customers")
        return itemsOnStock - count
    }
    
    return incoming ? increaseCount : decreaseCount
}

var itemsOnStock = 8
let itemsIncoming = itemCounter(incoming: true)
let itemsOutgoing = itemCounter(incoming: false)


print("There are \(itemsOnStock) items in the warehouse")
// There are 8 items in the warehouse
itemsOnStock = itemsIncoming(10)
// 10 items were added to our current stock
itemsOnStock = itemsOutgoing(7)
// 7 items were shipped to customers
print("There are \(itemsOnStock) items in the warehouse")
// There are 11 items in the warehouse

Classes and Structures

[edit | edit source]

As an object-oriented language, Swift also provides classes, the construction plan for objects or instances, and structures, a similar construct. Interfaces, which are used to make the class or the structure available for other code parts, are available automatically.

Classes and structures share a lot of features, for example:

  • Properties to store values
  • Methods which provide functionality
  • Both can be extended
  • Initializers to set up their initial state

However, there are some features only classes provide:

  • Inheritance
  • Check and interpret a classes type at runtime
  • Class can free up ressources using Deinitializers

Class or Structure?

[edit | edit source]

Before deciding whether a class or a structure suits better for your needs, a few characteristics of both constructs have to be considered. One of the most important differences is, that class are always passed by reference whereas structures are passed by value.

Apple suggests[2] using structs in these situations:

  • The primary purpose of the structure is to encapsulate few simple data values.
  • It is reasonable to expect that the values will be copied and not referenced.
  • All properties within the structure are value types.
  • The structure does not need to inherit properties or behavior from other existing types.

In the snippet below you can see two structures, SoftTyre and HardTyre, which store values that describe the characteristics of a tyre. As you can see, only simple values like Integers and Bools are stored. The class Racecar also includes some simple data values like weight or teamName, but also an instance of the SlickTyre structure.

struct DryTyre{
    var forWetCondition = false
    var grip = 3
    var durability = 3
    
}
struct WetTyre{
    var forWetCondition = true
    var grip = 4
    var durability = 2
}

class Racecar{
    let teamName = "Red Bull Racing"
    var tyre = DryTyre()
    var weightEmpty = 650
    var weightWithDriver = 728
}

Accessing Properties

[edit | edit source]

Properties of classes and structures can be accessed using dot syntax.

var car = Racecar()
// create an instance
print("\(car.weightEmpty)")
// prints "650"
car.weightWithDriver = 732
// assign a new value using dot syntax

print("This tyre suits for wet conditions: \(car.tyre.forWetCondition)\nand has a durability value of: \(car.tyre.durability)")
// This tyre suits for wet conditions: false
// and has a durability value of: 3

Memberwise initialization of Structure Types

[edit | edit source]

The properties of a new structure instance can be initialized using memberwise initializers which are automatically generated.

let superSoft = SoftTyre(forWetCondition: false, grip: 4, durability: 2)
// create and initialize a new instance of the SoftTyre struct
car.tyre = superSoft
print("This tyre has a durability value of: \(car.tyre.durability)")
// This tyre has a durability value of: 2

Value Type vs. Reference Type

[edit | edit source]

Structures, Enumerations and all basic types in Swift, for example integers, strings and arrays, are value types, which means the value is copied when it is passed to a function. Changes to a copied value of an integer inside a function do not affect the original value outside.

let ultraSoft = SoftTyre(forWetCondition: false, grip: 5, durability: 1)

var tyre = ultraSoft
tyre.durability = 4
print("Durability of tyre is now \(tyre.durability)")
// Durability of tyre is now 4
print("Durability of ultraSoft ist still \(ultraSoft.durability)")
// Durability of ultraSoft ist still 1

Classes are reference types, which means they are not copied when they are passed to a function. Instead, references to already existing instances are used. In the snippet below, an instance of Racecar is assigned constant called rb13. After assigning the properties raceWins and weightEmpty rb13 is assigned to a new constant rb14. As the instance of the Racecar class is passed by reference, changes in rb14 automatically affect the properties in rb13.

let rb13 = Racecar()
rb13.raceWins = 39
rb13.weightEmpty = 680

let rb14 = rb13
rb14.raceWins = 42
rb14.weightEmpty = 700

print("rb13 now also has \(rb13.weightEmpty) kg and \(rb13.raceWins) wins")
// rb13 now also has 700 kg and 42 wins

References

[edit | edit source]
  1. Apple Inc. | 2017 | Swift programming language | [online][accessed: 18.09.2017] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0
  2. Apple Inc. | 2017 | Swift - Classes and Structs | [online][accessed: 18.09.2017] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82


SwiftAdvanced

Swift Advanced

[edit | edit source]

Closure Expressions

[edit | edit source]

Closure expressions are unnamed, anonymous functions which can interact with values from their surrounding contextref name="error">Apple Inc. | 2017 | Closure Expressions | [online][accessed: 18.09.2017] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID94</ref>. They are short function-like constructs without a declaration or name. In this snippet, you can see the general syntax of a closure expression. It starts with opening curly brackets, a parameter list and a return value. They keyword in marks the beginning of the closure's body. After the return statement the expression is closed with a curly bracket.

// basic syntax
let add = {(int1: Int, int2: Int) -> Int in return int1 + int2}
var result = add(20,5)      // 25

If a closure is assigned to a function type, a shorthand syntax can be used because Swift can infer the data types from the context. In the shortest version, you only have to tell Swift what to do with the parameters. $0 is short syntax for the first passed in argument, $1 for the second, and so on.

let subtract: (Int, Int) -> Int = { int1, int2 in return int1 - int2 }

let multiply: (Int, Int) -> Int = { $0 * $1 }

result = subtract(20,5)     // 15
result = multiply(20,5)     // 100

Trailing Closures

[edit | edit source]

Closures can be passed to functions as arguments. A trailing closure can be used as the function's final argument. It's not written within the functions parentheses but right after them. In the snippet below, you can see how a closure is passed to Swift's map function. The map function can be called on an array, the closure is then executed on every item of the array. In this example, the map functions checks whether a year is a leapyear or not. All years are converted to a string and " is a leap year" is appended if the prerequisites are fulfilled.

// create an Array with values from 1950 to 2020
var years = [Int]()
for year in stride(from: 1950, to: 2020, by: 1){
    years.append(year)
}

let leapYears = years.map{ (year) -> String in
    var output = ""
    let year = year
    if(year%400 == 0){
        output.append(String(year)+" is a leap year")
    }
    else if(year % 100 != 0 && year % 4 == 0){
        output.append(String(year)  + " is a leap year")
    }
    else{
        output.append(String(year) + " is not a leap year")
    }
    return output
}

for year in leapYears{
    print(year)
}

Properties

[edit | edit source]

In Swift, there are two main types of properties. Stored properties are used to store values of variables and constants associated with a class or a structure. Computed properties are not used to store, but to compute values.

Stored Properties

[edit | edit source]

These properties are a part of an instance of a class or a structure. It can have a variable or a constant value.

struct Animal{
    let name: String
    let legs: Int
    var weight: Double
}

var sloth = Animal(name: "slothy", legs: 4, weight: 8.5)
print("Hi my name is \(sloth.name) and i have \(sloth.weight) kilos!")

sloth.weight = 9.2
print("Put one some weight... now i have \(sloth.weight) kilos :) !")

Computed Properties

[edit | edit source]

These properties provide getters and setters to retrieve or set the value of a variable. In this example, the structure Circle has a computed property called diameter. It has a getter which returns the doubled value of the radius. The setter changes the value of the radius to the half of the new diameter. area is a read-only property, which means it does not have a setter.

struct Circle {
    var radius: Double
    var diameter: Double{
        get {
            let diameter = radius * 2
        return diameter
        }
        set(newDiameter){
            radius = newDiameter/2
        }
    }
    var area: Double{
        get{
            return radius * radius * Double.pi
        }
    }
}

var smallCircle = Circle(radius: 3)

let initialDiameter = smallCircle.diameter
smallCircle.diameter = 10
print("The circle now has a diameter of \(smallCircle.diameter), a radius of \(smallCircle.radius) and a area of \(smallCircle.area)")
// prints "The circle now has a diameter of 10.0, a radius of 5.0 and a area of 78.53"

Property Observers

[edit | edit source]

Property observers can be used to observe the state of a property and respond to changes. They are called whenever the value of a property is set. There are two types of observers. willSet is called before a value is stored, didSet is called after a value is stored.

class EctsCounter{
    var ectsCount: Int = 0{
        willSet(newCount){
            print("About to set your count to \(newCount) points!")
        }
        didSet{
            print("Added \(ectsCount - oldValue) points!")
        }
    }
}

let counter = EctsCounter()
counter.ectsCount += 10
// About to set your count to 10 points!
// Added 10 points!
counter.ectsCount += 4
// About to set your count to 14 points!
// Added 4 points!

Concurrency with GCD

[edit | edit source]

The dispatch framework includes a lot of language features, runtime libraries and system enhancements which improve the support for concurrent code execution on hardware with more than one core. The Grand Central Dispatch (GCD) submittes work to dispatch queues managed by the system.

Concurrency vs. Parallelism

[edit | edit source]

Parallelism is the term which describes the concept of executing two or more threads at the same time on multi-core processors. Devices with a single core can achieve concurrency with time-slicing. This is the process of switching between multiple threads through context switches. GCD manages a pool of threads. Codeblocks can be added to dispatch queue and the GCD decides what to execute.

Queues

[edit | edit source]

DispatchQueue represents the dispatch queues provided by GCD[1]. The tasks, which you submit to the queues, are executed in a FIFO order, which means the first submitted task will always be the first one that gets started. There are two types of queues. Serial queues can only execute one task at any time. Concurrent queues can start multiple tasks at the same time. They are started in the order they were added and can finish in any order. The scheduling is managed by GCD, which means it controls when tasks are started. GCD provides a main queue, which is a serial queue and runs on the main thread. Tasks on the main queue are executed immediately. It is good practice to put all tasks, which change or update the UI, on the main queue. This makes sure the UI is always responsive. The four global queues are concurrent, shared by the entire system and divided in priorities - high, default, low and background. Queues can also be created by the user and can be serial or concurrent.

The priority is not specified directly, but by using Quality of Service (QoS) classes.

.user-interactive describes tasks which have to be done immediately, because otherwise the user experience would be bad. This QoS is used for UI updates and event handling.

.user-initiated represents tasks, which can be performed asynchronously and are started from the UI. These tasks are mapped into the high priority queue, because it is used when users are waiting for immediate results or tasks require user interaction to continue.

.utility represents long-running tasks. This class is used for I/O, networking and computations. Usually a progress indicator is used to make sure the user knows something is going on.

.background describes tasks, which the user usually is not aware of. It is used for tasks that do not need user interaction and are not time-sensitive, for example maintenance or prefetchting.

Using queues in iOS

[edit | edit source]

The following snipped can be found on Github and can be imported as Xcode project. As soon as the "Start" button is pressed, the function download(size: Int, label: UILabel, timeout: Int) is called twice with different input parameters. After they are put onto a global queue they asynchronously execute the simulation. After each iteration the amount is increased by 1. Next, the label which displays the progress of the download has to be updated. To do this, it is necessary to put the task back on the main queue, which makes sure that it is executed immediately. After a short timeout the next iteration starts.

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var task1: UILabel!
    @IBOutlet weak var task2: UILabel!
    
    @IBAction func start(_ sender: UIButton) {
        download(size: 70, label: task1, timeout: 10)
        download(size: 50, label: task2, timeout: 7)
    }
    
    func download(size: Int, label: UILabel, timeout: Int) -> Void{
    
        DispatchQueue.global(qos: .userInitiated).async {
            // puts the download simulation on a global queue
            var amount = 0
            for _ in stride(from: 0, to: size, by: 1){
                amount += 1
                
                DispatchQueue.main.async {
                /* All actions which change the UI have to be put back on the main queue. */
                    label.text = String(amount)
                }
                // sets a timeout
                usleep(useconds_t(timeout*10000))
            }
        }
    }
}

Error Handling

[edit | edit source]

There are a lot of situations where errors can occure during the execution of your code. For example, when trying to read a file from your harddisk errors can occur due to missing permission or non-existing files. Swift provides couple of ways[2] to deal with errors during code execution.

  • Errors in a function can be propagated to the code calling the function
  • do-catch statements
  • optional values
  • assert the error won't occur

Propagating Errors with throwing Functions

[edit | edit source]

The keyword throws is used in a function's declaration after the parameterlist. This function is then called a "throwing function". In the example below, an enum is used to define two types of possible errors. Whenever the makeCoffee() function is called, the number of beans in the machine is decreased and a counter is increased. As soon as the beans are empty, an outOfBeans error is thrown. If a certain number of cups was served, the needsMaintenance error is thrown.

enum CoffeeMachineError: Error {
    case outOfBeans
    case needsMaintenance
}

class CoffeeMachine{
    var beans = 20
    var count = 1
    
    func makeCoffee() throws{
        if(count < 6){
            
            if(beans > 0){
                print("Enjoy your cup of coffee!")
                beans -= 1
                count += 1
            } else{
                throw CoffeeMachineError.outOfBeans
            }
        } else{
            throw CoffeeMachineError.needsMaintenance
        }
    }
}
var machine = CoffeeMachine()
for _ in stride(from: 0, to: 7, by: 1){
    try machine.makeCoffee()
}

Do-Catch

[edit | edit source]

Do-Catch statements are used to execute different statements depending on what type of error is thrown. For example, here a needsMaintenance error is caught, the machine asks for maintenance and the counter is set back to 0.

var coffeeMachine = CoffeeMachine()
for run in stride(from: 0, to: 25, by: 1){
    do{
        try coffeeMachine.makeCoffee()
    } catch CoffeeMachineError.outOfBeans{
        print("Out of Beans!")
		
    } catch CoffeeMachineError.needsMaintenance{
        print("Machine needs Maintenance!")
        // Machine is maintained, counter gets set back to 0
        coffeeMachine.count = 0
    }
}

Converting Errors to Optional Values

[edit | edit source]

With the try? keyword, an error is converted to an optional value. The value of an expression is nil whenever an error is thrown during the execution. In the snippet below, an error is thrown as soon as the digit that should be returned is no longer greater than zero.

enum DigitError: Error{
    case outOfDigitsError(String)
}

var currentDigit = 9
func  getDigit() throws -> Int {
    if(currentDigit > 0){
        let tmp = currentDigit
        currentDigit -= 1
        return tmp
    }
    else{
        throw DigitError.outOfDigitsError("Sorry, no digits left...")
    }
}

for _ in stride(from: 0, to: 10, by: 1){
    if let digit = try? getDigit(){
        print(digit)
    }
}


Accessing data from Sensors

[edit | edit source]

Apple's mobile devices include a lot of sensors, including an Accelerometer, a Barometer, an Ambient light sensor and a Pedometer. iOS developers can access these sensors data in their projects with Swift.

Pedometer

[edit | edit source]

As an example, the snippet below shows how the Pedometer can be accessed and how data can be retrieved[3]. This sensor is used for instance to count a persons steps. This project can be downloaded from GitHub.

import UIKit
import CoreMotion

@IBDesignable
class ViewController: UIViewController {
   
    
    @IBOutlet weak var stepsDisplay: UILabel!
    @IBOutlet weak var stepsToComplete: UILabel!
    
    let calendar = Calendar.current
    let todayDate = Date()
    var stepsToday: Int = 0
    let pedometer = CMPedometer()
    
    @IBInspectable
    var targetSteps = 10000
    
    func getStartOfDay(from date: Date) -> Date{
        return Calendar.current.startOfDay(for: date)
    }
    
    func handler (_ data: CMPedometerData?, _ error: Error?) -> Void{
        let steps = data?.numberOfSteps
        stepsToday = steps as! Int
        DispatchQueue.main.async(execute: {
            // puts the closure into the Main Queue so it will be executed immediatly
            self.stepsDisplay.text = String(self.stepsToday)
            self.stepsToComplete.text = String(self.getStepsToGoal(target: self.targetSteps, actual: self.stepsToday))
        })   
    }
    
    func getStepsToGoal(target steps: Int, actual count: Int) -> Int{
        return steps - count
    }
    
    @IBAction func getSteps(_ sender: UIButton){
        if CMMotionActivityManager.isActivityAvailable(){
            //checks if Activity Data is available
            pedometer.queryPedometerData(from: getStartOfDay(from: todayDate), to: todayDate, withHandler: handler)
            //queries data from the pedometer.
        }
    }   
}


Unit Testing

[edit | edit source]

In this section we will have a look at how simple unit tests can be implemented in Swift[4]. For this purpose a simple class with three functions will be tested. The class contains variables for two integer values and three functions which can add, subtract or multiply those values.

import Foundation

class Calculator {
    
    var a: Int
    var b: Int
    
    init(a:Int, b:Int){
        self.a = a
        self.b = b
     }
    
    func add(a:Int, b:Int) -> Int {
        return a + b
    }
    
    func sub(a:Int, b:Int) -> Int {
        return a - b
    }
    
    func mul(a:Int, b:Int) -> Int {
        return a * b
    }
}

Next, let's have a look at how this class can be tested. First of all we have to import the XCTest testing framework and the Calculator. In the test functions testAdd(), testSub() and testMul() an instance of the Calculator class is used to call the methods add, subtract and multiply to compare the result to an expected value.

import XCTest
@testable import Calculator

class CalculatorTests: XCTestCase {
    
    let calc = Calculator(a:0, b:0)
    
    override func setUp() {
        // called before every test method
        super.setUp()
    }
    
    override func tearDown() {
        // called at the end of every test method
        super.tearDown()
    }
    
    func testAdd() {
        XCTAssertEqual(calc.add(a: 1, b: 1), 2)
        XCTAssertEqual(calc.add(a: 1, b: 2), 3)
        XCTAssertEqual(calc.add(a: 5, b: 4), 9)
    }
    
    func testSub(){
        XCTAssertEqual(calc.sub(a: 5, b: 2), 3)
        XCTAssertEqual(calc.sub(a: 3, b: 3), 0)
        XCTAssertEqual(calc.sub(a: 6, b: 7), -1)
    }
    
    func testMul(){
        XCTAssertEqual(calc.mul(a: 2, b: 4), 8)
        XCTAssertEqual(calc.mul(a: 9, b: 9), 81)
        XCTAssertEqual(calc.mul(a: 0, b: 4), 0)
    }
}

References

[edit | edit source]
  1. Kodeco | 2017 | Grand Central Dispatch Tutorial | [online][accessed: 18.09.2017] | https://www.kodeco.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2
  2. Apple Inc. | 2017 | Error Handling | [online][accessed: 18.09.2017] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508
  3. Apple Inc. | 2017 | [online][accessed: 18.09.2017] | https://developer.apple.com/documentation/coremotion/cmpedometer
  4. Nolan, G. (2017), Agile Swift: Swift Programming Using Agile Tools and Tech- niques, Springer Science+Business Media New York, New York


Conclusion

Conclusion

[edit | edit source]

Open Issues

[edit | edit source]

This book includes many of the most important concepts of Swift. It explains the basics like datatypes, control flow and collection types. It also deals with advanced topics like concurrency and unit testing. Of course there are a lot of other language features which are not described here, for instance inheritance, generics and protocols.

Consequences and Suggestions

[edit | edit source]

To take advantage of all language features I recommend reading Apple's Language Guide[1] which describes all basic features as well as a lot of advanced topics. Moreover, I can suggest video blogs or video courses from Stanford University . There you can find a lot of free tutorials for iOS development.

References

[edit | edit source]
  1. Apple Inc. | 2017 | Swift programming language | [online][accessed: 18.09.2017] | https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0