Swift Introduction/SwiftBasics

Swift Basics

edit

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

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

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

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

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

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

Type Safety and Type Inference

edit

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

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

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

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

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

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

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

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
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

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

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

For-In Loop

edit

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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
  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