Swift Background

  • Apple first released Swift in 2014 to replace Objective-C in iOS app and MAC app development.
  • According to Apple, Swift is “friendly” to new programmers.

You can use Playground in Xcode for learning purpose, which is only available in MacOS.

Swift Syntax

  • Variables are always initialized before use.
  • Array indices are checked for out-of-bounds errors.
  • Integers are checked for overflow.
  • Optionals ensure that nil values are handled explicitly
  • Memory is managed automatically.
1
2
import UIKit
print ("Hello World")

Basic Syntax

Semicolon

  • Swift does not require a semicolon after each statement
  • Require semicolon only for multiple statements in the same line

Comments

  • Comments are ignored by the compiler
  • Single-line //, Multi-line /* */, same as most of the languages

Identifiers

An identifier is to identify a variable, function, or user-defined item.

  • case sensitive
  • starts with an alphabet A to Z or a to z or an underscore _ followed by letters, underscore and digits
  • does not allow special characters within identifiers

Keywords

Data Types

  • Int or UInt
  • Float
  • Double
  • Bool
  • String
  • Character
  • Optional
  • Tuples

Variable declaration

1
var num:Int = 0
  • :Int is to state the type of variable (not necessary)
  • var is to define a variable
1
var <variable name>:<data type> = <value> 

Constants

Constants are like variables except their values cannot be modified after declaration

1
let fixed_num = 0
  • let is the keyword to make variable cannot be modified after declaration

Operators

  • Same as C++, nothing special
  • Operators can only work with operands of the same type
    • You need to convert values before applying to operands

To Join Two Strings together:

1
2
let age = 20
var myAge = "My age is " + (String(age)) + "!"

another way:

String interpolation is done by typing the name of a constant or variable between “(” and “)” in a string.

1
2
let age = 20
var myAge = "My age is \(String(age))!"

Whitespaces

Yeah, this is dumb

Controls

If Else statement

  • No need to use ( ) for the condition
1
2
3
4
5
6
7
var str:String? = "Hello!"

if let newStr = str {
print("Say \(newStr)")
}else{
print("Nothing to say")
}

Switch statement

  • No need to use break after each case
  • Every switch statement consists of multiple possible cases, each of which begins with the case keyword
  • Every switch statement must be exhaustive
    • Consider every element in the case

Loops

For-in loop

1
2
3
4
5
6
7
for _ in 0...5 {
print(i) // 0 1 2 3 4 5
}

for _ in 0..<5 {
print(i) // 0 1 2 3 4
}
  • The _ is used to define that the parameter is not named

While loop

1
2
3
4
var sum = 1
while sum < 1000 {
sum = sum + (sum+1)
}

Repeat-while loop

1
2
3
4
var sum = 1
repeat {
sum = sum + (sum+1)
} while sum < 1000

The Optional Type

Every variable needs to be initialized, optional variables are automatically initialized as nil (not set).

Swift allow you to declare a variable without a value only when the type is optional.

  • Denote optional with ?
    • the value of the variable will be nil if no value given
    • you need to unwrap the associated value with !
1
2
3
4
5
6
7
8
9
var str:String?

if str == nil {
print("str is nil")
} else {
print(str!)
}

//str is nil
1
2
3
4
5
6
7
8
9
var str:String? = "Something Here"

if str == nil {
print("str is nil")
} else {
print(str!)
}

//Something Here

Automatic Unwrapping

  • or you can Declare an Optional with ! instead of ? for automatic unwrapping
1
2
3
4
5
6
7
8
9
var str:String!

if str == nil {
print("str is nil")
} else {
print(str)
}

//str is nil
1
2
3
4
5
6
7
8
9
var str:String! = "Something Here"

if str == nil {
print("str is nil")
} else {
print(str)
}

//Something Here
  • Note it crashes your app if you try to unwrap an optional in the not set state (the value is nil)

Conditional Binding

Optional Binding uses if statement to find out whether an optional contains a value or not

1
2
3
4
5
6
7
8
9
var str:String? = "Hello!"

if let newStr = str { // If newStr successfully gets a value
print("Say \(newStr)")
} else {
print("Nothing to say")
}

//Say Hello!
1
2
3
4
5
6
7
8
9
var str:String?

if let newStr = str {
print("Say \(newStr)")
} else { // newStr does not get a value
print("Nothing to say")
}

//Nothing to say

With this, you can see whether an optional contains a value or not.

Optional operator ??

There is also an Optional “defaulting” operator ??

The aim to provide a default value if the Optional is nil

1
2
3
4
5
let s: String? //might be nil

//If s is nil, print "I am nil"
//otherwise print `s`
display.text = s ?? "I am nil"

Array

An array stores values of the same type in an ordered list

The same value can appear in an array multiple times at different positions

1
2
3
var arr1 = Array<String>()
// or
var arr2 = [String]()

Common Array Accessing Methods

  • .append()
  • .insert()
  • .count
  • .first
  • .last
  • .isEmpty

Looping through an array

1
2
3
4
var animals = ["Giraffe", "Cow", "Doggie", "Bird"]
for animal in animals{
print (animal)
}

One Tricky Example

1
2
3
4
5
6
var animals = ["Giraffe", "Cow", "Doggle", "Bird"]
animals += ["Lion", "Tiger"]
var newAnimals = animals[3...5]
print("\(newAnimals.indices)") // 3..<6
newAnimals.insert("Duck", at: 4) // ["Bird", "Duck", "Lion", "Tiger"]
let animal = newAnimals.removeLast() // ["Tiger"]

Tuples

  • a group of values
  • The tuple elements can be named when the tuple is declared
1
2
3
4
5
6
let x: (w: String, i:Int, v: Double) = ("hello", 5, 0.85)
print(x.w)
print(x.i)
print(x.v)
//this is also legal (renames the tuple's elements on access)
let (wrd, num, val) = x

Dictionary

  • used to store unordered lists of values of the same type
  • use unique identifier known as a key to store a value which later can be referenced and looked up through the same key

Creating Dictionary

1
2
3
var UniRankings = Dictionary<String, Int>()
//or
var UniRankings = [String:Int]()

Assign key and values in Dictionary

1
UniRankings = ["HKU":1, "PolyU":99]

Set

A Set is an unordered collection of unique elements

1
2
//Creating a set
var Aset: Set = ["A","B","C"]

Common Set Accessing Methods

  • .count
  • .isEmpty
  • .insert()
  • .contains()
  • .remove()
  • .union()
  • .intersection()
  • .subtracting()
  • .symmetricDifference()
  • .isSubset(of: )
  • .isSuperset(of: )
  • .isDisjoint(with: )

Function

A function is a block of code with a given name. It can be executed by calling its name.

Function Declaration

1
2
3
func functionName(parameter(s)) -> returnParameter(s) {
//code here
}

All parameters to all functions have an internal name and an external name

  • The internal name is the name of the local variable you use inside the method
  • The external name is what callers use when they call the method

Example:

1
2
3
4
5
6
7
8
9
10
func foo(external_a internal_a: Int, external_b internal_b: Double) -> (Double){
var sum = 0.0
for _ in 0..<internal_a {
sum += internal_b
}
return sum
}

let result = foo(external_a: 123, external_b: 5.5)
print(result)

You can put _ if you don’t want callers to use an external name for a given parameter.

1
2
3
4
5
6
7
8
9
10
func foo(_ internal_a: Int, _ internal_b: Double) -> (Double){
var sum = 0.0
for _ in 0..<internal_a {
sum += internal_b
}
return sum
}

let result = foo(123, 5.5)
print(result)

Class

A class is a group of variables with functions that can manipulate the variables

The name of the class become a type

1
2
3
4
5
class Point {
init(){

}
}
  • init function is the constructor

Class Inheritance

A class may inherits from another class

  • The new class (derived class) have the same properties (data and functions) of the parent class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Use : and followed by a class to inherit from that class
class Point3D : Point {
var z = 0

init(_ x:Int, _ y:Int, _ z:Int){
//...
}

func setXYZ(_ x:Int, _ y:Int, _ z:Int){
//...
}

func printZ(){
//...
}

}

Function Overriding

  • Derived class can have its own version of function inherited from the parent class
1
2
3
override func printX(){
print ("x: \(x)")
}
  • Keyword super is to use the parent class’s function and provide additional functionality within the overriding function

Enum

enum in general is a discrete set of values

  • enums are like class and can have their own methods, but cannot have inheritance, and enum is “pass by value
1
2
3
4
5
6
7
8
9
10
11
12
13
enum Operation {
case Constant(Double)
case UnnaryOperation((Double) -> Double)
case BinaryOperation((Double, Double) -> Double)
case Equals
}

var operations: Dictionary<String, Operation> = [
"π": Operation.Constant(Double.pi),
"√": Operation.UnaryOperation(sqrt),
"x": Operation.BinaryOperation(*),
"=": Operation.Equals
]

An Optional is actually just an enum.

1
2
3
4
enum Optional<T>{ //the <T> is a generic like as in Array<T>
case None // the not set case
case Some(T) // the set case
}

Closure

There is a Swift feature called Closure. Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you. In other words, functions can be embedded in line in Swift.

A closure, like a function, contains a sequence of instructions and can take parameters and return values.

  • Closures don’t have names
  • The sequence of instructions in a closure is surrounded by curly braces { }
  • Use $0, $1, $2, etc. to represent the parameters

Closure with Type Inference

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Operation {
case Constant(Double)
case UnnaryOperation((Double) -> Double)
case BinaryOperation((Double, Double) -> Double)
case Equals
}

var operations: Dictionary<String, Operation> = [
"π": Operation.Constant(Double.pi),
"√": Operation.UnaryOperation(sqrt),
"cos":Operation.UnaryOperation(cos),
"+": Operation.BinaryOperation({$0+$1}),
"-": Operation.BinaryOperation({$0-$1}),
"x": Operation.BinaryOperation({$0*$1}),
"/": Operation.BinaryOperation({$0/$1}),
"=": Operation.Equals
]

Closure with Arrays

  • filter(includeElement: (T) -> Bool) -> [T]
  • This one creates a new array with any “undesirables” filtered out
  • The function passed as the argument returns false if an element is undesirable
1
2
let bigNumbers = [2,47,118,5,9].filter({ $0 > 20 }) //bigNumbers = [47, 118]
let l = [2,47,118,75,9].filter({ $0 > 20 && $0 < 100 }) // [47,75]

Image example from https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

  • map(transform: (T)->U) -> [U]
  • Create a new array by transforming each element to something different
  • The thing it is transformed to can be of a different type than what is in the Array
1
2
let multiplied: [Int] = [1,2,3].map { $0*3 } // [3,6,9]
let stringified: [String] = [1,2,3].map { String($0) } // ["1","2","3"]

Image example from https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

  • reduce(initial: U, combine: (U, T) -> U ->) U
  • Reduce an entire array to a single value
1
2
3
4
let sum: Int = [1,2,3].reduce(0) { $0 + $1 } // adds up the numbers in the Array = 6
let i: Int = [1,2,3].reduce(2) { $0 + $1 } // 2 + 1+2+3 = 8
let j: Int = [1,2,3].reduce(6) { $0 - $1 } // 6 - 1-2-3 = 0
let k: Int = [5,4,3].reduce(3) { $0 - $1 } // 3 - 5-4-3 = -9

Image example from https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

Lazy Initialization

Lazy initialization (also sometimes called lazy instantiation, or lazy loading) is a technique for delaying the creation of an object or some other expensive process until it’s needed. When programming for iOS, this is helpful to make sure you utilize only the memory you need when you need it.

  • Swift requires that “you must initialize all of your properties”
  • A lazy property does not get initialized until someone accesses it
  • You can allocate an object, execute a closure, or call a method if you want
  • Things initialized this way can’t be constants (i.e., var ok, let not ok)
  • This can be used to get around some initialization dependency issues

Example:

1
2
3
4
5
6
7
8
9
10
lazy var brain = CalculatorBrain()
// nice if CalculatorBrain used lots of resources

lazy var myProperty = self.initializeMyProperty()
// not valid without lazy

lazy var someProperty: Type = {
//Closure: construct the value of someProperty here
return <the constructed value>
}()

Property Observers

You can observe changes to any property with willSet and didSet.

  • Very common thing to do with didSet/willSet in a Controller to update the UI
  • Will also be invoked if you mutate/change a struct (or value type)
    • (e.g., add something to a dictionary)

Example

1
2
3
4
5
6
7
8
9
10
var myData: Int = 0{
willSet{
print ("myData before change is \(myData)")
}
didSet{
print ("myData is changed to \(myData)")
}
}

myData = 10

Computed Properties of a Variable

Automatic detection of getters and setters

  • Using keyword get and set

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
var displayValue: Double{
get{
return Double(display.text!)!
// ! needed as display.text could be a string that cannot be converted to double
}
set{
display.text = String(newValue)
}
}

brain.setOperand(operand: displayValue) //calling get

displayValue = brain.result //calling set

Struct

  • they are like class and similar to enum
  • But It is pass by value instead of pass by reference (class)
  • Pass by value means that when you pass it, it makes a copy of it
  • For pass by reference, it is done through pointer

Example:

1
2
3
4
struct PendingBinaryOperationInfo{
var binaryfunction: (Double, Double) -> Double
var firstOperand: Double
}

Fun Facts

You can code with emoji.

Data Structures in Swift

These are the 3 fundamental building blocks of data structures in Swift:

  • Classes
  • Structs
  • Enums

Similarities

Classes, Structs and Enums :

  • have the similar Declaration syntax
  • can have Properties and Functions

Differences

  • Initializers (not for enum)
  • Inheritance (class only)
  • Value type (struct, enum) vs Reference type (class)

Value vs Reference

Pass by Value (struct and enum)

  • Copied when passed as an argument to a function
  • Copied when assigned to a different variable
  • Immutable if assigned to a variable with let
  • function parameters are constants

You must note any func that can mutate a struct/enum with the keyword mutating (e.g., mutating func …)

Pass by Reference (class)

  • Stored in the heap and reference counted (automatically)
  • Constant pointers to a class (let) still can mutate by calling methods and changing properties (what the pointer pointed to could be changed)
  • When a pointer is passed as an argument, it does not make a copy (just passing a pointer to same instance)

Choosing which to use?

Usually you will choose class over struct. struct tends to be more for fundamental types (e.g., String, Double, Int, Array, Dictionary)

Use of enum is situational (any time you have a type of data with discrete values)