Swift Introduction/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
Set
[edit | edit source]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!")
}
}
If
[edit | edit source]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]- ↑ 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
- ↑ 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