Swift Study Notes

2020/09/06

Swift Study Notes

The source of this file is available on Github where you can find the XCode playground file.

The generated markdown is currently used as a Hugo generated content hosted on my website haozhexu.me for better readability.

This is the playground I used during learning Swift, I mainly read The Swift Programming Language from Apple, took notes and wrote code as I read.

Soon I realised the notes could be organized in a way so that a human readable markdown could be generated with some tweaks, and I can even use Hugo to generate an HTML from it and host it somewhere.

As a result, this markdown was directly generated from the code and comments in the playground.

To make this possible, comments in the code have to follow certain conventions in order to have the right content format for markdown as well as keeping Playground able to compile. The convention looks like:

// This is a comment, will become text content of **markdown**.
// Below will become a fragment of code:

// ```swift
// let name = "Noname"
// ```

Simple way to generate markdown from above code:

sed 's/^\/\/ //g' SwiftBasicNotes.playground/Contents.swift > ~/Documents/SwiftBasicNotes.md
(strips leading double slashes followed by a space from each line)

(PS: the details of how to generate markdown from the source code is written in the markdown itself, embedded as comments in the source code, this recursion feels weird, doesn’t it? It feels like a hungry snake swallows down itself from its own tail.)

“Hello, world!” Printing

The classic “Hello, world!” print out illustrates a few things of the language: no semicolon needed to end a line, how a function/method call looks like, how string literal is represented in Swift which doesn’t need an @ sign as in Objective-C.

print("Science may someday discover what faith has always known.")

Table of Contents

Constants and Variables

constant:

Something mysterious is formed, born in the silent void. waiting alone and unmoving, it is at once still and yet in constant motion. It is the source of all programs. I do not know its name, so I will call it the Tao of Programming. - The Tao of Programming

variable:

Cloudy; variable winds, with local showers; cooler; snow. - Halcyon Jones

let <constant name>: <type> = <expression>
var <variable name>: <type> = <expression>

Historians say the Egyptians revered the number nine because they associated it with their sun god, Atum-Ra. According to one version, Ra gave birth to eight other gods. Since Ra often took the form of a cat, people began associating the nine lives (Ra plus eight) with feline longevity.

let catsMaximumNumberOfLives = 9 // constant
var catsCurrentDeathCount = 0 // variable
catsCurrentDeathCount = 3
catsMaximumNumberOfLives = 10 // this would generate a compiler error

// multiple constants or variables on a single line:
var variable1 = 0.0, variable2 = 1.2, variable3 = 5.6

ToC

Types and Operations

Type Annotations

var welcomeMessage: String = "Welcome!"
var red: Double = 1.0
var someNumber: Int = 9

Semicolon (;) isn’t required after each statement although you can; Required for separating statements on the same line:

let sameLineGuru = "Same Liner"; print("This is Mr. \(sameLineGuru)")

string interpolation was used above to include the name of a constant or variable as a placeholder in a longer string, and to prompt Swift to replace it with the current value of that constant or variable.

Integers

God made the integers; all else is the work of Man. – Kronecker

// Int is usually used, it's the same size as `Int32` on 32-bit platform and same size as `Int64` on 64-bit platform
let integer: Int
let integer8: Int8 = 8 // signed 8-bit integer, UInt8 for unsigned
let integer16: Int16 = 16 // signed 16-bit integer, UInt16 for unsigned
let integer32: Int32 = 32 // signed 32-bit integer, UInt32 for unsigned
let integer64: Int64 = 64 // signed 64-bit integer, UInt64 for unsigned

Integer Bounds

General rule of the bounds of integers:

print("maximum value of UInt16 is \(UInt16.max) which is (2^16)-1 = \(Int(pow(2.0, 16.0) - 1))")
print("minimum value of Int8 is \(Int8.min) which is -(2^(8-1)) = \(-Int(pow(2.0, 8.0 - 1.0)))")
print("maximum value of Int32 is \(Int32.max) which is (2^(32-1))-1 = \(Int(pow(2.0, 32.0 - 1.0)) - 1)")

Floating Points

Type Safe and Type Inferences

The biggest regret in life is that one cannot have youth and the feeling of youth at the same time.

let youth = 3
// inferred to be type `Int`

let feelingOfYouth = 0.14
// inferred to be type `Double`

let pi = 3 + 0.1415926
// Inferred to be type `Double`

let anotherPi = youth + feelingOfYouth
// error: binary operator '+' cannot be applied to operands of type 'Int' and 'Double'

Type Conversion

let alsoPi = Double(youth) + feelingOfYouth
let werePi = Int(alsoPi) // lost precision

Type Alias

typealias Mood = UInt8
print("mood: happy = \(Mood.max) sad = \(Mood.min)")

Numeric Literals

Apart from decimals, numbers with base 8, 16, or 2 (ie. binary) are sometimes used in programming. Each of these can be written as integer literals.

Booleans

Boolean type (Bool) in Swift is often referred to as logical with values of either true or false.

The following statement is true.

The previous statement is false.

let fake = false
let truth = true

Assignment operator

var b = 10
var a = 5
a = b // a is now equal to 10
let (x, y) = (1, 2) // x is 1 and y is 2

Arithmetic operator

Arithmetic is being able to count up to twenty without taking off your shoes. - Mickey Mouse

1 + 2       // addition (+)
5 - 3       // subtraction (-)
2 * 3       // multiplication (*)
10.0 / 2.5  // division (/)
9 % 4       // remainder (%)
-5          // unary minus
+6          // unary plus

Compound assignemnt operators

a = 1
a += 2
a -= 5
a *= 3
a /= 2

Comparison Operators

1 == 1  // equal to
2 != 1  // not equal to
2 > 1   // greater than
1 < 2   // less than
1 >= 1  // greater than or equal to
2 <= 1  // less than or equal to

You can compare two tuples if they have the same type and the same number of values, tuples are compared from left to right, and you can compare “Apple” with “Orange”

(1, "zebra") < (2, "apple")
// 1 is less than 2 and "zebra" and "apple" are not compared

(3, "apple") < (3, "orange")
// 3 is equal to 3 and "apple" is less than "orange"

(4, "dog") == (4, "dog")
// all equal

Tuples

let someCoordinates: (Int, Int) = (3, 6)
let someOtherCoordinates = (21, 97)
// type inference without type `(Int, Int)`

// access value by index:
print("some coordinate is (\(someCoordinates.0), \(someCoordinates.1))");

// access value by name:
let someCoordinatesNamed = (x: 71, y: 89)
print("some coordinate has x = \(someCoordinatesNamed.x) and y = \(someCoordinatesNamed.y)")

Range operators

closed range operator: (a...b) defines a range from a to b inclusive, a must not be greater than b.

half-open range operator: (a..<b) defines a range from a up to, but not include b.

one-sided range: a range that continue as far as possible, e.g. array[3...] from 3 to the end, array[...9] from beginning up to 9

let someArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(someArray[5...]) // [6, 7, 8, 9]
print(someArray[...3]) // [1, 2, 3, 4]

Terminology

ToC

Control Flow

Comparison Operators let you compare values and get an answer of either true or false

if-else statement

If all else fails, immortality can always be assured by spectacular error. – John Kenneth Galbraith

if <condition> {
    <statement>
}
if <condition1> {
    <statement1>
} else if <condition2> {
    <statement2>
} else {
    <statement3>
}
if <condition> {
    <statement1>
} else {
    <statement2>
}
let isOnePlusOneEqualThree = (1 + 1) == 3
false, `==` equality comparison

let isFake = isOnePlusOneEqualThree == false

if isFake == true {
    print("The truth is fake.")
}

Short Circuiting

let isGovernmentGreaterThanItsPeople = "government" > "people"
if isGovernmentGreaterThanItsPeople && 1 + 1 == 2 {
    // because the first condition is false
    // there's no need to check the second condition even if it's true
    print("People should be afraid of their government.")
} else {
    print("Government should be afraid of its people.")
}
// Result: Government should be afraid of its people.
let someoneIsDrunk = true
let divorceAgreementReadyToSign = true

if someoneIsDrunk || divorceAgreementReadyToSign == false {
    // even if the divorce agreement is ready to sign
    // since the person is drunk, ie. first condition is true
    // there's no need to check the second condition
    print("Let's decide later.")
}

Ternary Conditional Operator

(<CONDITION>) ? <TRUE VALUE> : <FALSE VALUE>
let weightOfMountain = 9
let weightOfFeather = 1
let onesLife = 0
let anotherOnesLife = 10

var lifeDescription = onesLife < weightOfFeather ? "lighter than a feather" : "more than a feather"
print("Someone's life is \(lifeDescription).")
// Someone's life is lighter than a feather.

lifeDescription = anotherOnesLife > weightOfMountain ? "heavier than a mountain" : "less than a mountain"
print("Someone's life is \(lifeDescription).")
// Someone's life is heavier than a mountain.

Loops

Endless Loop: n., see Loop, Endless.

Loop, Endless: n., see Endless Loop.

while Loop

while <CONDITION> {
    <LOOP CODE>
}
let weekendStarts = 6
var today = 1
while today < weekendStarts {
    print("Day \(today): start the day with a smile, after that you can be your nasty lazy but true self again.")
    today += 1
}
print("Day \(today): finally weekend")
repeat {
    <LOOP CODE>
} while <CONDITION>

Children seldom misquote you. In fact, they usually repeat word for word what you shouldn’t have said.

var waterInCup = 5

repeat {
    if (waterInCup > 0) {
        print("Like this cup, you are full of opinions and speculations.\nTo see the light of wisdom, you first must empty your cup before adding more.")
        waterInCup -= 1
    }
} while waterInCup > 0

for Loop

for <CONSTANT> in <COUNTABLE RANGE> {
    <LOOP CODE>
}
let totalVersions = 5
for currentVersion in 1...totalVersions {
    print("Current version: \(currentVersion)")
}

where

C++ : Where friends have access to your private members. – Gavin Russell Baker

let versionNumberOfBadLuck = 4
for currentVersion in 1...totalVersions where currentVersion != versionNumberOfBadLuck {
    print("Current version: \(currentVersion)")
}

continue and Labeld Statements

Trust in what you love, continue to do it, and it will take you where you need to go. – Natalie Goldberg

for currentVersion in 1...totalVersions {
    if currentVersion == versionNumberOfBadLuck {
        print("We skip version \(currentVersion) to avoid bad luck.")
        continue // start next loop and ignore the remaining loop code
    }
    print("Current version: \(currentVersion)")
}
// output:
// Current version: 1
// Current version: 2
// Current version: 3
// We skip version 4 to avoid bad luck.
// Current version: 5

Labeled statement lets you continue or break specific loop, this is useful for multi-layer loops.

Example: dating with different people:

let numberOfLovers = 3
let datesPerLover = 2
let wifesFriend = 2
let datingWithWife = 1

lover: for currentLover in 1...numberOfLovers {
    date: for currentDate in 1...datesPerLover {
        print("lover \(currentLover) date \(currentDate)")
        if currentLover == wifesFriend {
            print("Do not date if lover is also wife's friend.")
            continue lover
        } else if currentDate == datingWithWife {
            print("Go dating with wife instead.")
            continue date
        }
        print("Dating with lover.")
    }
}

// output:
// lover 1 date 1
// Go dating with wife instead.
// lover 1 date 2
// Dating with lover.
// lover 2 date 1
// Do not date if lover is also wife's friend.
// lover 3 date 1
// Go dating with wife instead.
// lover 3 date 2
// Dating with lover.

Switch

There’s no kill switch on awesome. – Dilbert (Scott Adams)

switch <control expression> {
case <pattern 1>:
    <statements>
case <pattern 2> where <condition>:
    <statements>
case <pattern 3> where <condition>,
     <pattern 4> where <condition>:
    <statements>
default:
    <statements>
}
let occupied = true, unoccupied = false
var gender = "boy" // try changing it to "girl"
var toiletOccupancy = (male: occupied, female: unoccupied) // try changing occupied and unoccupied

switch toiletOccupancy {
case let (male, female) where male != female:
    print("At least one toilet is empty")
fallthrough // continue the statements below
case (male: true, _) where gender == "boy":
    // male toilet is empty
    // and boys do not care about female toilet
    print("boy can use toilet now")
case (_, female: true) where gender == "girl":
    // female toilet is empty
    // and girls do not care about male toilet
    print("girl can use toilet now")
case (male: true, female: false) where gender == "girl":
    print("girl has to use boy's toilet if urgent")
case (male: false, female: true) where gender == "boy":
    print("boy has to use girl's toilet if urgent")
default:
    // **exhaustive**: every possible value of the control expression’s type
    // must match the value of at least one pattern of a case
    // `default case` can be used to satisfy this if it's not feasible.
    break;
}
var schoolYear = 3 // try changing it to something within 1...12
switch schoolYear {
case 1...3:
    print("You don't know you don't know.")
case 4...6:
    print("You know you don't know.")
case 7...9:
    print("You don't know you know.")
case 10...12:
    print("You know you know.")
default:
    // exhaustive: “every possible value of the control expression’s type
    // must match the value of at least one pattern of a case”
    // `default case` can be used to satisfy this if it's not feasible.
    break
}

ToC

Functions

Character is higher than intellect. Thinking is the function. Living is the functionary. The stream retreats to its source. A great soul will be strong to live, as well as strong to think. – Ralph Waldo Emerson

func <function name>(<parameters>) -> <return type> {
    statements
}

If the function has a return type of Void (ie. no return value):

func <function name>(<parameters>) {
    statements
}

Forms of a parameter:

<parameter name>: <parameter type>
<argument label> <parameter name>: <parameter type>
_ <parameter name>: <parameter type>
func add(a: Int, b: Int) -> Int {
    return x + y
}

func sayHi(to person: String) {
    print("Hi, \(person)!")
}

sayHi(to: "future")

func sayHi(_ person: String) {
    print("Hi, \(person)!")
}

sayHi("people")

Change a parameter directly

Terminology: copy-in copy-out, call by value result

let vampiresAppetite = 0.4
func payTax(for income: inout Double) {
    income *= (1.0 - vampiresAppetite)
}

var income = 100.00
payTax(for: &income)

print("After tax, income becomes \(income)")
// Prints "After tax, income becomes 60.0"

Functions as variables

A function that takes a parameter, and returns another function that uses the parameter.

Example: define a generic function that returns a function mimicing money deduction behaviour with specified deduction rate

func makeMoneyDeductor(with percentage: Double) -> ((Double) -> Double) {
    // define and return another function:
    func bloodSuckingVampire(bloodQuantity: Double) -> Double {
        return bloodQuantity * (1 - percentage)
    }
    return bloodSuckingVampire
}

let taxOffice = makeMoneyDeductor(with: 0.3)
let laywer = makeMoneyDeductor(with: 0.2)
let dentist = makeMoneyDeductor(with: 0.1)

income = 10_000.00
print("Start with income \(income)")
income = taxOffice(income)
income = laywer(income)
income = dentist(income)
print("After tax, laywer and dentist, now it becomes \(income)")
// output:
// Start with income 10000.0
// After tax, laywer and dentist, now it becomes 5040.0

Function that Never Returns

func oneInfiniteLoop() -> Never {
    print("DO NOT CALL THIS FUNCTION OR:")
    while true {
        print("Falling apple on your head.")
    }
}

Function Overloading

Function overloading is having several functions with the same name but different signatures, after all, the compiler must be able to know which function is invoked, at least one of the difference must exist:

Function Documentation

Doxygen format is often used to document functions:

/// Returns a substitution of something.
/// - Parameters:
///   - something: a string value of something
/// - Returns: "lie" if something is "truth", a very poor substitute, but the only one discovered to date; returns the same thing otherwise
func substitution(of something: String) -> String {
    if something == "truth" {
        return "lie"
    }
    return something
}

ToC

Optionals

A variable holding either some value, or nothing.

Money matters, but less than we think and not in the way we think. Family is important. So are friends. Envy is toxic. So is excessive thinking. Beaches are optional. Trust is not. Neither is gratitude. – Eric Weiner

<Type>?

nil is the name given to the absense of a value.

Accident, n.: a condition in which presence of mind is good, but absence of body is better.

var errorCode: Int?
errorCode = 404
errorCode = nil

unwrap optional

To use the value that optional represents, it first need to be unwrapped. Optional binding binds the value in an optional into a constant or variable, if the optional doesn’t contain a avalue, the condition for if is false.

let optionalDrink: String? = "Beer"
print("\(String(describing: optionalDrink))")
// output: Optional("Beer")
if let drink = optionalDrink {
    print("\(drink)") // Beer
}

force unwrap

errorCode = 500
print("Error code is \(errorCode!)")

Both reference type and value type can be optional, they both represent the situation when the variable has no value. There’s no such a concept in Objective-C, although a variable of reference type can be assigned nil, to indicate the variable has no (referenced) value.

ToC

Collection Types

Array[Element]
[Element] // short form

Array

var someNumbers = [Int]()

someNumbers.append(9)
someNumbers = [] // type has been provided as `Int`

Array with default value

var threeFloats = Array(repeating: 1.2, count: 3)
result: [1.2, 1.2, 1.2]

Adding two array together

let threePowers = ["Executive", "Legislative", "Judicial"]
let twoPowers = ["Supervision Audit", "Examination"]
let fivePowers = threePowers + twoPowers

Accessing and modifying array

var shoppingList = ["Wine", "Coffee", "Cigarette"]
print("Shopping list has \(shoppingList.count) items")

if shoppingList.isEmpty {
    print("Shopping list is empty")
} else {
    print("Shopping list isn't empty")
}

shoppingList.append("Panado")
shoppingList += ["Meat", "Tea"]

var firstItem = shoppingList[0]

shoppingList[3] = "Egg"
shoppingList[3...4] = ["Energy Drink"]
shoppingList: ["Wine", "Coffee", "Cigarette", "Energy Drink", "Tea"]

shoppingList.insert("Honey", at: 0)
let honey = shoppingList.remove(at: 0)

Iterating Over an Array

for item in shoppingList {
    print("Shopping list item: \(item)")
}

// iterating with index
for (index, item) in shoppingList.enumerated() {
    print("Shopping list item \(index): \(item)")
}

Implementation

Array in Swift combines and replaces NSArray and NSMutalbeArray in Objective-C, there are several kinds of arrays.

copy-on-write

tip: When value-type is copied, the copied version still points to the same memory location as the original version, only when copied version is modified, a new memory region is allocated for the copied version. This means that memory footprint of copied value type only increases when copied value changes, this ensures maximum efficiency of memory usage.

ToC

Set

Hash Value for Set Types

Set can only contain hashable values if a == b, then a.hashValue == b.hashValue

Creating Sets

var letters = Set<Character>()
letters.insert("a")
letters = []
var someColors: Set<String> = ["Blue", "White", "Red"]
var someOtherColors: Set = ["Sunset", "Autumn", "Zen"]

Operations

Count

print("Other colors have \(someOtherColors.count) colors")

Check Empty

if someOtherColors.isEmpty {
    print("No color, is the real color.")
} else {
    print("There are a few colors in it.")
}

Insert and Remove

someColors.insert("Cyan")
if let removedColor = someColors.remove("Yellow") {
    print("\(removedColor) removed from someColors")
} else {
    print("someColors does not have Yellow in it")
}

Union and Intersection

let openDays: Set = [1, 2, 3, 4, 5]
let closedDays: Set = [6, 7]
openDays.union(closedDays)
openDays.intersection(closedDays)

Example: given an array of numbers and a target value, determine whether the array has two numbers whose sum equals to the target value

func twoSum(numbers: [Int], _ target: Int) -> Bool {
    var set = Set<Int>()

    for number in numbers {
        if set.contains(target - number) {
            return true
        }
        set.insert(number)
    }
    
    return false
}

func twoSumIndices(numbers: [Int], _ target: Int) -> (Int, Int)? {
    var dict = [Int: Int]()
    for (index, number) in numbers.enumerated() {
        if let lastIndex = dict[target - number] {
            return (lastIndex, index)
        } else {
            dict[number] = index
        }
    }
    return nil
}

Many collection types as well as other types (e.g. String) are reference type in Objective-C but value type in Swift, the reasons are:

ToC

Dictionary

Creating a Dictionary

Dictionary in Swift can be created using dictionary literal, which is a list of key-value pairs separated by commas, inside square brackets.

// creating empty dictionary:
var meaningsOfAges = [String: String]()
meaningsOfAges["eighteen"] = "By which the acquired collection of prejudices become common sense"
// creating with dictionary literal:
var meaningOfWords = ["agreement": "when people are tired of thinking"]
// `String` type inferred for `var meaningOfWords: [String: String]`

Dictionary operations

if meaningsOfAges.isEmpty == false {
    print("At least one age has some meaning")
}

meaningsOfAges["four"] = "at which we know all the questions"
meaningsOfAges["eighteen"] = "at which we know all the answers"

if let meaningOf18 = meaningsOfAges["eighteen"] {
    print("The meaning of age 18:\n\(meaningOf18)")
}
// output:
// Meaning of eighteen: at which we know all the answers

Iterating Over a Dictionary

for (age, meaning) in meaningsOfAges {
    print("Meaning of \(age): \(meaning)")
}
// output:
// Meaning of four: at which we know all the questions
// Meaning of eighteen: at which we know all the answers

ToC

Closures

Function without name:

{ (<parameters>) -> <return type> in
    statement
}

I go on working for the same reason a hen goes on laying eggs. – H. L. Mencken

For a long time the question exists that whether egg came first or chicken came first, we can figure it out using closure:

let matters = ["🥚", "🐓"]
let sortedMatters = matters.sorted(by: { (m1: String, m2: String) -> Bool in
    m1 < m2
})
print("first there's \(String(describing: sortedMatters.first!))")
print("then there comes \(String(describing: sortedMatters.last!))")

Inferring type from context

without parameter type:

matters.sorted { (m1, m2) -> Bool in
    m1 > m2
}

without return type:

matters.sorted { (m1, m2) in m1 > m2 }

shorthand argument names:

matters.sorted { $0 > $1 }

operator methods:

matters.sorted(by: >)

As a result, chicken and egg must exist at the same time for the comparison to work!

Escaping closures

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.

var executionGround: [() -> Void] = []

func executionSuspended(prisonerExecution: @escaping () -> Void) {
    executionGround.append(prisonerExecution)
}

func executionImmediate(prisonerExecution: () -> Void) {
    prisonerExecution() // execute immediately
}

executionSuspended {
    print("Summer is here, will autumn be far away?")
}

executionImmediate {
    print("Don't laugh if I lay drunken on the battleground, how many warriors ever came back safe and sound?")
    // only this is executed
}

Autoclosures

var toBeInDinnerPlate = ["Chicken", "Duck", "Fish", "Pork"]
print(toBeInDinnerPlate.count) // Prints "4"

let dinnerProvider = { toBeInDinnerPlate.remove(at: 0) }
print(toBeInDinnerPlate.count) // Prints "4"

print("Now serving \(dinnerProvider())!")
// Prints "Now serving Chicken!"
print(toBeInDinnerPlate.count) // Prints "3"

// parameter: () -> String
// argument is auto converted to a closure
func serve(dinner provider: @autoclosure () -> String) {
    print("Now serving \(provider())!")
}

// now can pass a String
serve(dinner: "Vegetable")
// "Vegetable" will be converted to a closure that returns this string
// which is only evaluated when being called

Example of autoclosure: implement OR (||) operation.

Flawed version:

func ||(left: Bool, right: Bool) -> Bool {
    if left {
        return true
    } else {
        return right
    }
}

The problem with above implementation is that both left and right sides are evaluated when left is true, so there’s a better implementation using autoclosure:

func ||(left: Bool, right: @autoclosure () -> Bool) -> Bool {
    if left {
        return true
    } else {
        return right()
    }
}

Higher Order Functions

Closures in Swift enable higher order functions, which are functions that can either accept functions or closures as arguments, and/or return a function/closure.

Swift has built-in support for things that are common in functional programming, such like map, reduce, filter and compactMap, which allows developer to focus only on results without having to write boiler-plate code. In this regard, Swift is both object-oriented and functional programming language.

In this regard, Swift is both object-oriented and functional programming language.

Example: due to the support of functional closures, many problems can be solved funtionally. For example, get all numbers between 0 to 100 that are 1. even and 2. squares of other numbers

(0...10).map { $0 * $0 }.filter { $0 % 2 == 0 }

Functional composition in mathematical term is to combine multiple functions into one function, the idea behind functional composition in Swift is applying one function to the result of another function.

func unique(_ array: [Int]) -> [Int] {
    return array.reduce(into: []) { (result, number) in
        if result.contains(number) == false {
            result.append(number)
        }
    }
}

func even(_ array: [Int]) -> [Int] {
    return array.filter { $0 % 2 == 0 }
}

func square(_ array: [Int]) -> [Int] {
    return array.map { $0 * $0 }
}

precedencegroup Group { associativity: left }
infix operator >>> : Group
func >>><T, U, V>(left: @escaping (T) -> U, right: @escaping (U) -> V) -> ((T) -> V) {
    return { right(left($0)) }
}

let testIntArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var resultArray = (unique >>> even >>> square)(testIntArray)
print("result: \(resultArray)")

map

Map can be used to loop over a collection and apply the same operation to each element in the collection.

let numberOfMonks = [1, 2, 3]
let hasWaterToDrink: [(Int, Bool)] = numberOfMonks.map { monks in
    return (monks, monks < 3)
}
hasWaterToDrink.forEach { (numberOfMonks, hasWater) in
    print("\(numberOfMonks) monks, water to drink? \(hasWater ? "YES" : "NO")")
}
// output:
// 1 monks, water to drink? YES
// 2 monks, water to drink? YES
// 3 monks, water to drink? NO

compactMap / flatMap

compactMap is the same as map but also handles optionals.

let couldBeNumbers = ["1", "3", "five", "12"]
let compactedNumbers = couldBeNumbers.compactMap { Int($0) }
print(compactedNumbers)
// result: [1, 3, 12]
let mappedNumbers = couldBeNumbers.map { Int($0) }
print(mappedNumbers)
// result: [Optional(1), Optional(3), nil, Optional(12)]

flatMap is to receive a single-level collection when each element is a sequence or collection:

let bloodTemperatureByGroup = ["A": [0, 36, 48], "B": [12, 32, 39]]
let temperaturesMapped = bloodTemperatureByGroup.map { $0.value }
print(temperaturesMapped)
// output: [[12, 32, 39], [0, 36, 48]]
let temperaturesFlatened = bloodTemperatureByGroup.flatMap { $0.value }
print(temperaturesFlatened)
// output: [12, 32, 39, 0, 36, 48]

General rule: use compactMap for mapping a sequence and mapped result could be an optional value; If not, use either map or flatMap depending on the form of result expected.

ToC

Strings and Characters

let quotationSingleLine = "There be light."
let quotationMultiLine = """

And God said, \
Let there be light:
and there was light.

"""

final string:

And God said, Let there be light:
and there was light.

Create emtpy string

var emptyString = ""
var anotherEmptyString = String()
if emptyString.isEmpty {
    print("Unfortunately the emptiness of a string can't be derived from its name.")
}

You can do this:

emptyString += "no longer empty"

Strings are sequence of characters

for character in "Dog🐶Inside!" {
    print(character)
}

let nethackCharacters: [Character] = ["T", "h", "e", " ", "n", "e", "w", "t", " ", "b", "i", "t", "e", "s"]
var nethackString = String(nethackCharacters)
print(nethackString)
// Prints "The newt bites"

nethackString += "!"

Unicode

Unicode is an international standard for encoding text in different languages, it covers much more than the original ASCII character set, however the underlying representation are still integers. Integers within certain ranges (U+0000 to U+D7FF inclusive, U+E000 to U+10FFFF inclusive) are called Unicode Scalars.

Extended grapheme clusters

Every instance of Swift’s Character type represents a single extended grapheme cluster. An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.

let eAcute: Character = "\u{E9}"
// é

let combinedEAcute: Character = "\u{65}\u{301}"
// e followed by  ́ (the thing above e)

Accessing and Modifying a String

Because a character in Unicode may consist of more than one number, anything related to indices, inculding counting the number of characters in a string, becomes tricky. A swift string can’t be indexed by integer values, the type of index is String.Index

var word = "cafe"
word.count // 4
word += "\u{301}"
word.count // word is now "café" but count is still 4
let science = "ABCDEFGHIJKMNPQRTVWXYZ"
print(science[science.startIndex]) // A
print(science[science.index(before: science.endIndex)]) // Z
print(science[science.index(after: science.startIndex)]) // B
let index7 = science.index(science.startIndex, offsetBy: 7)
print(science[index7]) // H
var indexS: String.Index? = science.firstIndex(of: "S")
var indexO: String.Index? = science.firstIndex(of: "O")
var indexU: String.Index? = science.firstIndex(of: "U")
var indexL: String.Index? = science.firstIndex(of: "L")

if (indexS == nil && indexO == nil && indexU == nil && indexL == nil) {
    print("science has no SOUL")
}

inserting

var sentence = "you understand"
sentence.insert(contentsOf: "could ", at: sentence.startIndex)
sentence.insert(contentsOf: "?", at: sentence.endIndex)
print(sentence)
// Print: "could you understand?"

replacing

let range1 = sentence.range(of: "oul")!
sentence.replaceSubrange(range1, with: "ulo")
let range2 = sentence.range(of: "nderstan")!
sentence.replaceSubrange(range2, with: "redntsna")
print(sentence)
// Prints: "culod you uredntsnad?"

substrings

let englishForDummy = "therapist"
let indexOfR = englishForDummy.firstIndex(of: "r")!
let firstWord = englishForDummy[..<indexOfR]
let lastWord = englishForDummy[indexOfR...]
print("Have you ever realised the word \"\(englishForDummy)\" is made up by \"\(String(firstWord))\" and \"\(String(lastWord))\"?")
// output:
// Have you ever realised the word "therapist" is made up by "the" and "rapist"?

Exercise: given a string, return another string which is a reverse of given string by words, e.g. what the f**k becomes f**k the what

func reverse<T>(_ things: inout [T], _ start: Int, _ end: Int) {
    var startIndex = start, endIndex = end
    while startIndex < endIndex {
        (things[startIndex], things[endIndex]) = (things[endIndex], things[startIndex])
        startIndex += 1
        endIndex -= 1
    }
}

func reverseWords(_ s: String?) -> String? {
    guard let s = s else {
        return nil
    }
    
    var mutableString = Array(s), start = 0
    reverse(&mutableString, 0, mutableString.count - 1)
    
    for i in 0..<mutableString.count {
        if i == mutableString.count - 1 || mutableString[i + 1] == " " {
            reverse(&mutableString, start, i)
            start = i + 2
        }
    }
    
    return String(mutableString)
}

reverseWords("what the f**k")
// output:
// f**k the what

Note: a simpler way to solve above problem is to use components(separatedBy:) and joined(separator:).

Raw strings

To avoid special characters or string interpolation, use raw string.

There is a tendency to mistake data for wisdom, just as there has always been a tendency to confuse logic with values, intelligence with insight. Unobstructed access to facts can produce unlimited good only if it is matched by the desire and ability to find out what they mean and where they lead. Facts are terrible things if left sprawling and unattended. They are too easily regarded as evaluated certainties rather than as the rawest of raw materials crying to be processed into the texture of logic. – Norman Cousins

let raw1 = #"Eating "raw" fish will not \(interpolate) the string including slash \"#
let raw2 = ##"Use # in raw string is also possible as long as the open and close hashes match!"##
let raw3 = #"You can also use raw in raw: \#(raw1)"#

ToC

Enumerations

the average pencil is seven inches long, with just a half-inch eraser-in case you thought optimism was dead. – Robert Brault

Some history regarding switch-case - in case you need to show off your knowledge to friends: // In his 1952 text Introduction to Metamathematics, Stephen Kleene formally proved that the CASE function (the IF-THEN-ELSE function being its simplest form) is a primitive recursive function. //

Syntax

enum <enumeration name> {
    <enumeration definitions>
}

each case can be a single line:

enum PlanetVertical {
    case mercury
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune
}

or multiple cases can appear on a single line:

enum PlanetHorizontal {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

enumeration type can be inferred

enum Direction {
    case north
    case west
    case south
    case east
}

var lostDirection: Direction = .north
var nextDirection = Direction.east

lostDirection = .north
nextDirection = .west

Switch matching

switch lostDirection {
case .north, .west, .south:
    print("Lost in the wild")
case .east:
    print("You need a pilot")
}

Remember switch must be exhaustive

switch nextDirection {
case .east:
    print("Be prepared.")
default:
    print("Why not heading east?")
}

Associated Values

Example:

Define an enumeration for daily attendence, if unattended, give and execuse as well as a real reason, define three functions, evaluate the execuse and reason for unattendence, from perspectives of teacher, manager and self

enum DailyAttendence {
    case attended
    case unattended(execuse: String, realReason: String)
}

func teacherEvaluate(_ attendence: DailyAttendence) {
    switch attendence {
    case .attended:
        print("Keep up")
    case .unattended(_, let realReason):
        // teacher know your execuse and only care about real reason
        print("I need to talk to your parents regarding \(realReason)")
    }
}

func managerEvaluate(_ attendence: DailyAttendence) {
    switch attendence {
    case .attended:
        print("Keep up")
    case .unattended(let execuse, _):
        print("I'm fine with \(execuse)")
    }
}

func selfEvaluate(_ attendence: DailyAttendence) {
    switch attendence {
    case .attended:
        print("A boring day")
    case let .unattended(execuse, realReason):
        print("I have \"\(execuse)\" as execuse for the real reason of \"\(realReason)\"")
    }
}

let jackyAttendence: DailyAttendence = DailyAttendence.unattended(execuse: "alarm clock stopped working", realReason: "late sleep watching TV")
teacherEvaluate(jackyAttendence)
// I need to talk to your parents regarding late sleep watching TV

let goodsonAttendence: DailyAttendence = DailyAttendence.attended
teacherEvaluate(goodsonAttendence)
// keep up

let mrUnsatisiable: DailyAttendence = DailyAttendence.unattended(execuse: "sick", realReason: "job interview")
managerEvaluate(mrUnsatisiable)
// I'm fine with sick

selfEvaluate(mrUnsatisiable)
// I have "sick" as execuse for the real reason of "job interview"

Raw Values

Implicit:

enum Month : Int {
    case january = 1
    case feburary // 2
    case march // 3
    case april // 4
    case may // 5
    case june // 6
    case july // 7
    case august // 8
    case september // 9
    case october // 10
    case november // 11
    case december // 12
}

print("September has a value of \(Month.september.rawValue)")
// Print "September has a value of 9"
enum SomeNames : String {
    case michael
    case tom
    case jack
}

print("case tom has raw value of \(SomeNames.tom.rawValue)")
// Print "case tom has raw value of tom"

Initializing from raw value

var monthNumber = 7
if let month = Month(rawValue: monthNumber) {
    switch month {
    case .july:
        print("Month of examination, graduation and boy/girl-friend breaking up.")
    default:
        print("Some other month")
    }
} else {
    print("Cannot recognize month number")
}

Recursive enumerations

A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases

enum Trap {
    case rightWayOut
    indirect case wayOut(Trap)
}

let eternity = Trap.wayOut(Trap.rightWayOut)
let love = Trap.wayOut(eternity)

func escape(from trap: Trap) {
    switch trap {
    case .rightWayOut:
        print("Found the right way out")
    case .wayOut(let anotherTrap):
        print("Fall into another trap")
        escape(from: anotherTrap)
    }
}

escape(from: love)
// Fall into another trap
// Fall into another trap
// Found the right way out

Different Uses of Enum

Example: an enum representing seasons with method that evolves to next season

enum Season: String, CustomStringConvertible {

    case spring
    case summer
    case autumn
    case winter

    mutating func next() {
        switch self {
        case .spring:
            self = .summer
        case .summer:
            self = .autumn
        case .autumn:
            self = .winter
        case .winter:
            self = .spring
        }
    }
    
    var description: String {
        return "It's \(self.rawValue) now."
    }
}

var currentSeason = Season.spring
print(currentSeason)
currentSeason.next()
print(currentSeason)

// output:
// It's spring now.
// It's autumn now.

Example: an ’enum’ representing stages of love and with ability to evolve

enum StageOfLove: String {
    case acquaint
    case inLove
    case marriage
    case accustomed
    case bored
    case stranger
    case separate
    case lost
}

extension StageOfLove {
    func next() -> StageOfLove {
        guard self != .lost else {
            preconditionFailure("love ends here!")
        }
        
        switch self {
        case .acquaint:
            return .inLove
        case .inLove:
            return .marriage
        case .marriage:
            return .accustomed
        case .accustomed:
            return .bored
        case .bored:
            return .stranger
        case .stranger:
            return .separate
        case .separate:
            return .lost
        default:
            preconditionFailure()
        }
    }
}

extension StageOfLove: CustomStringConvertible {

    var description: String {
        switch self {
        case .acquaint, .inLove, .marriage, .accustomed:
            return "You are the one"
        case .bored, .stranger, .separate:
            return "Memory fades as time goes by, we got seperated as we were pacing."
        case .lost:
            return "The moment I turned round, I found that you were not there. Suddenly, I was flustered."
        }
    }
}

var currentStage = StageOfLove.acquaint
var nextStage = currentStage.next()
print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print("\(currentStage.rawValue) becomes \(nextStage.rawValue)")

currentStage = nextStage
nextStage = currentStage.next()

print(currentStage)
print(nextStage)

// output:
// acquaint becomes inLove
// inLove becomes marriage
// marriage becomes accustomed
// accustomed becomes bored
// bored becomes stranger
// stranger becomes separate
// Memory fades as time goes by, we got seperated as we were pacing.
// The moment I turned round, I found that you were not there. Suddenly, I was flustered.

Enum with Generic parameters

Enum can be defined with generic parameters for associated values.

Example: define an enum representing success and error response with generic response and error type

enum Response<SuccessType, ErrorType> {
    case success(SuccessType)
    case failure(ErrorType)
}

let response1 = Response<String, Int>.success("valid response")
let response2 = Response<String, Int>.failure(404)

func print(response: Response<String, Int>) {
    switch response {
    case .success(let message):
        print("success message: \(message)")
    case .failure(let errorCode):
        print("error code: \(errorCode)")
    }
}

print(response1)
print(response2)

ToC

Classes and Structures

Declare a class:

class Love { // class name
    // <statements>
    // }

Class/Type constant:

    // by default, love cannot persist
    static let defaultPersistency = false

Stored properties

var name: String?
let thorny = true // constant
    
// inferred boolean variable
var isPersistent = defaultPersistency
var looksPretty: Bool // variable

Computed property:

// computed property must be variable
var tastesGood: Bool {
    return !looksPretty
}

Lazy property:

lazy var complexity: Int = { [unowned self] in
    if let name = self.name {
        return name.lengthOfBytes(using: .utf8)
    } else {
        return 0
    }
}()

Initializer:

// non-optional properties must be initialized before use
init(looksPretty: Bool, name: String? = nil) {
    self.looksPretty = looksPretty
    self.name = name
}

Instance method

func printDescription() {
    Love.printNotes(about: self)
}

Class/Type method:

// `final` indicates cannot be overwritten
final func displayName() -> String {
    return self.name ?? self.defaultName()
}

func defaultName() -> String {
    return "love"
}
// called when no reference to the instance, ie. reference counter reaches 0
deinit {
    print("\(self.displayName()) disappeared in a puff of logic")
}

static func printNotes(about love: Love) {
    let displayName = love.displayName()
    print("Notes about \(love.defaultName()):")

    if let name = love.name {
        print("\(love.defaultName()) has a name of \"\(name)\"")
    } else {
        print("\(love.defaultName()) has noname")
    }

    if love.thorny {
        print("\(displayName) is thorny")
    }

    if love.isPersistent {
        print("\(displayName) lasts forever")
    } else {
        print("\(displayName) \(Love.defaultPersistency ? "lasts forever" : "doesn't last long") by default")
    }

    let look = love.looksPretty ? "looks pretty" : "looks ugly"
    let taste = love.tastesGood ? "tastes good" : "tastes bad"

    if love.looksPretty == love.tastesGood {
        print("\(displayName) \(look) and \(taste)")
    } else {
        print("\(displayName) \(look) but \(taste)")
    }
}

Property Observer

    var notes: String? {
        willSet {
            if let newNotes = newValue {
                print("Notes will be \(newNotes)")
            }
        }
        didSet {
            if let notes = self.notes, let oldNotes = oldValue {
                print("Notes has been set to \(notes) from \(oldNotes)")
            }
        }
    }

Tip: setting a property in init, willSet and didSet won’t trigger property observer.

make some loves:

let someLove = Love(looksPretty: false)
someLove.printDescription()
let someHate = Love(looksPretty: true, name: "Hate")
someHate.isPersistent = true
someHate.printDescription()

someLove prints

Notes about love:
love has noname
love is thorny
love doesn't last long by default
love looks ugly but tastes good

hate prints

Notes about love:
love has a name of "Hate"
Hate is thorny
Hate lasts forever
Hate looks pretty but tastes bad

static vs class function:

Both static and class can associate method of class, subclass can overrie class methods but not static methods.

struct Address {
    
    // type constant
    static let format = "British"
    
    var nickName: String
    var streetNumber: String
    var streetName: String
    var suburb: String
    var state: String
    var postcode: String
    var country: String
    
    var fullAddress: String {
        return "\(streetNumber) \(streetName), \(suburb), \(state) \(postcode), \(country)"
    }
    
    mutating func updateNickName(_ nickName: String) {
        self.nickName = nickName
    }
}

tip: mutating prefix is need for struct and enum to modify its own value.

print("Using \(Address.format) format")
// default struct-wise initializer
let address = Address(nickName: "Beehive", streetNumber: "123", streetName: "Straight Street", suburb: "Curveless", state: "XYZ", postcode: "1234", country: "Unobtainable")
print(address.fullAddress)
// output:
// Using British format
// 123 Straight Street, Curveless, XYZ 1234, Unobtainable

Inheritance

// hate is a kind of love
class Hate: Love {

    init() {
        super.init(looksPretty: false, name: "exclusive love")
        self.isPersistent = true
    }
    
    // `override` keyword used here
    override func defaultName() -> String {
        return "hate"
    }
}

let moreHate = Hate()
moreHate.printDescription()

moreHate prints:

Notes about hate:
hate has a name of "exclusive love"
exclusive love is thorny
exclusive love lasts forever
exclusive love looks ugly but tastes good

required and convenience initializers

Identify operators

Check whether two constants or variables refer to the same single instance:

let fakeLove = someLove
print("someLove is fakeLove? \(someLove === fakeLove)")

Deinitialization

var shortTermLove: Love? = Love(looksPretty: true)
shortTermLove = nil
// output:
// love disappears in a puff of logic

class vs struct

Tip: there are several differences between class and struct:

class:

struct:

ToC

Protocols

Define a protocol

protocol Rescuable {
    
    // method:
    func rescue()
    
    // property:
    var distanceToGod: UInt { get set }
    var hasBeenSaved: Bool { get }
    
    // initializer
    init?(initialDistanceToGod: UInt)
}

Implementing a protocol

class Person: Rescuable {
    
    var distanceToGod: UInt
    
    // satisfied by a `required` initializer in non-final class
    required init?(initialDistanceToGod: UInt) {
        if initialDistanceToGod == 0 {
            print("Already rescued, not a human anymore")
            return nil
        }
        self.distanceToGod = initialDistanceToGod
    }
    
    func rescue() {
        if distanceToGod > 0 {
            distanceToGod -= 1
        }
    }
    
    var hasBeenSaved: Bool {
        return distanceToGod == 0
    }
}

Swift doesn’t care how you implement a protocol, as long as you implemnet it. Choices for implementing a get property in protocol:

Implementing multiple protocols

We define an Evil protocol:

protocol Evil {
    func fulfilDesire()
}

Adopt protocol using extension

Make Person also implements Evil

extension Person: Evil {
    func fulfilDesire() {
        self.distanceToGod += 1
    }
}
func commentary(of thing: Rescuable & Evil) {
    print("Something that is evil but rescuable")
    
    if thing.hasBeenSaved {
        print("God saved you")
    } else {
        print("distance to God: \(thing.distanceToGod)")
        thing.fulfilDesire()
        print("desire makes the distance to God becomes \(thing.distanceToGod)")
        thing.rescue()
        print("after rescue, distance to God becomes \(thing.distanceToGod)")
    }
}

let person = Person(initialDistanceToGod: 20)!
commentary(of: person)

let distance = person.distanceToGod
for _ in 1...distance {
    person.rescue()
}

commentary(of: person)
// output: "God saved you"

Tip: in Swift, by default all the functions in protocol are required, unlike in Objective-C where @optional and @required can be used to annotate methods in a protocol. There are two workarounds:

  1. prefix protocol and optional function with @objc, so function can be made @optional
  2. provide a default implementation of protocol function via extension

Class Only Protocol

Define a class only protocol:

protocol ClassOnlyProtocol: AnyObject {
    func someMethodHere()
}

So that ClassOnlyProtocol can be implemented by only class, which is reference type, other than value types like struct. One of the reason for needing this is for having delegate which is usually weak, thus value type cannot be used.

Prior to Swift 4, class is used in place of AnyObject, now it’s made more clear: they represent an existential for classes.

ToC

Generics

It is generally agreed that “Hello” is an appropriate greeting because if you entered a room and said “Goodbye,” it could confuse a lot of people. – Dolph Sharp, “I’m O.K., You’re Not So Hot”

Generic Function

Suppose you want to create a function that swaps the values of its parameters, and the function has to work for different types. Instead of having one function per data type, you can have one single function with generic type. It is like a super-type of different types.

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var humanWorld = ""
var hell = "full of evil"
swapValues(&humanWorld, &hell)
// `hell` is now empty, `humanWorld` is now "full of evil"

Generic Types

Suppose we want to describe the universe with matters.

struct Universe<Matter> {
    var matters = [Matter]()
    mutating func create(_ matter: Matter) {
        self.matters.append(matter)
    }
    mutating func reject() -> Matter {
        return matters.removeLast()
    }
}

Matter is a generic type - there’s no need to hard code any real type (concrete or protocol) in the Universe definition; When we create a Universe instance, we need to specify a type that replaces Matter:

struct Planet {
    let name: String
}

var solarSystem = Universe<Planet>()
solarSystem.create(Planet(name: "Mercury"))
solarSystem.create(Planet(name: "Pluto"))
solarSystem.create(Planet(name: "Venus"))

print("Last planet being rejected by solar system: \(solarSystem.reject())")
// output:
// Last planet being rejected by solar system: Planet(name: "Venus")

Type Constraints

func fight<G: GoodProtocol, E: EvilProtocol>(somethingGood: G, somethingEvil: E)

The <G: GoodProtocol, E: EvilProtocol> syntax adds a type constraint to the placeholder. It defines that G and E needs to conform to the protocol GoodProtocol and EvilProtocol, respectively.

You cannot call fight(somethingGood:somethingEvil:) with two parameters that do not comform to something that respectively implement GoodProtocol and EvilProtocol.

Question: why the following doesn’t work?

func fight(somethingGood: GoodProtocol, somethingEvil: EvilProtocol)

Because the compiler must be able to figure out at compile time what types are used, if either GoodProtocol or EvilProtocol has associatedtype or defined in a way that certain constraints it declares must be satisfied, using the protocol directly won’t compile.

For example, the code below would not work:

func exists(object: Equatable, in array: [Equatable]) -> Bool {
    return array.contains(object)
}

while this does:

func exists<T: Equatable>(object: T, in array: [T]) -> Bool {
    return array.contains(object)
}

Associated Types

Sometimes it can be useful to specify associatedtype in protocol, they behave like placeholder.

Imagine we have a very generic Generator which generates output of OutputType, from input of InputType:

protocol Generator {
    associatedtype InputType
    associatedtype OutputType
    func generate(input: InputType) -> OutputType
}

And we also define Transformable protocol:

protocol Transformable {
    associatedtype TransformType
    func transform() -> TransformType
}

Associated Types with Constraints

Define a ChainedGenerator which has has InputProvider and OutputConsumer of type Generator, that generates input and consumes output

protocol ChainedGenerator: Generator {
    associatedtype InputProvider: Generator where InputProvider.OutputType == InputType
    associatedtype OutputConsumer: Generator where OutputConsumer.InputType == OutputType
    func generate(input: InputProvider, consumer: OutputConsumer) -> OutputConsumer.OutputType
}

Conforming to the Generator protocol, we want to describe the photosynthesis of green plants that absorbs carbon dioxide and generates oxygen.

There are some micro-organisms that exhibit characteristics of both plants and animals. When exposed to light they undergo photosynthesis; and when the lights go out, they turn into animals. But then again, don’t we all?

protocol Photosynthate {
    // photosynthate: n. the product of photosynthesis
    init()
    func emit()
}

Now we can define the generator:

class Photosynthesizer<T: Transformable, P: Photosynthate>: Generator where T.TransformType == P {
    func generate(input: T) -> P {
        return input.transform()
    }
}

struct CO2<P: Photosynthate>: Transformable {
    func transform() -> P {
        print("transforming CO2…")
        return P()
    }
}

struct Oxygen: Photosynthate {
    init() {}
    func emit() {
        print("emitting oxygen…")
    }
}

Now we can have our green plant:

// Define a green plant that is a `Photosynthesizer` (thus a `Generator`), its input is CO2 (a `Transformable`) that can transform to `Oxygen`
let greenPlant = Photosynthesizer<CO2<Oxygen>, Oxygen>()
greenPlant.generate(input: CO2<Oxygen>()).emit()
// print:
// transforming CO2…
// emitting oxygen…

We can abstract the idea of Photosynthesizer and create a DefaultGenerator that semantically works for any Transformable (Photosynthesizer requires the Transformable’s TransformType conforms to Photosynthate):

class DefaultGenerator<T: Transformable, O>: Generator where O == T.TransformType {
    func generate(input: T) -> O {
        return input.transform()
    }
}

It could be further simplified:

class TransformBasedGenerator<T: Transformable>: Generator {
    func generate(input: T) -> T.TransformType {
        return input.transform()
    }
}

the world we live in / is like a junkyard / people are like bugs / fighting against each other for food / all we eat is conscience / all we shit is opinions “Junkyard” - He Yong

struct Conscience: Transformable {
    func transform() -> String {
        return "transforming conscience into opinions"
    }
}

let junkyard = TransformBasedGenerator<Conscience>()
print(junkyard.generate(input: Conscience()))
// print: transforming conscience into opinions

Generics with where clause

func compareTransform<T1: Transformable, T2: Transformable>(_ transformable1: T1, _ transformable2: T2) -> Bool where T1.TransformType == T2.TransformType, T1.TransformType: Equatable {
    let transformed1 = transformable1.transform()
    let transformed2 = transformable2.transform()
    return transformed1 == transformed2
}

Extension with generics

Create an extension of Array for Statement to check whether a true statement exists.

struct Statement: CustomStringConvertible {
    let sentence: String
    let isTruth: Bool
    var description: String {
        return "This is a \(isTruth ? "true" : "false") statement."
    }
}

let statement1 = Statement(sentence: "The following statement is true", isTruth: true)
let statement2 = Statement(sentence: "The previous statement is false", isTruth: false)

let statements = [statement1, statement2]

extension Array where Element == Statement {
    var hasTrueStatement: Bool {
        return self.contains(where: { $0.isTruth })
    }
}

print("Has true statement? \(statements.hasTrueStatement ? "Y" : "N")")

Generics subscript

extension Array where Element == Statement {
    subscript<Indices: Sequence>(indices: Indices) -> [Element]
        where Indices.Iterator.Element == Int {
            var result = [Element]()
            for index in indices {
                result.append(self[index])
            }
            return result
    }
}

let partialStatements = statements[[1, 0]]
print(partialStatements)

ToC

Access Control

A module is a single unit of code distribution, can be imported by another module using import.

Syntax

public class PublicPool {}
internal class InternalChangeRoom {}
fileprivate class MaleFilePrivateBath {}
private class PrivateLocker {}

public var publicFeedback = "Good"
internal let internalComments = "Not great"
fileprivate func someFilePrivateFunction() {}
private func privateFunctionRoom() {}

Access control levels

open vs public

No entities can be defined in terms of another entity that has more restrictive access level

an open property defined in a private class, or a public function with private type parameters is like saying you can get a gift without buying our product and details are written on a note inside our product package.

Unit tests targets

A unit test target can access any internal entity, if you mark the import declaration for a product module with the @testable attribute and compile that produce module with testing enabled.

Custom types

The access control level of a type also affects the default access level of the type’s members

Other types

You can skip to the end of the below list for simple way to remember.

analog way to remember: the volume of a timber bucket depends on the shorted piece of timber.

ToC

Advanced Operators

Bitwise operators

one and zero looked just right, made for each other: 1, the definite, upright line; and 0, the diagram of nothing at all - Sadie Plant, zeros + ones

Bitwise NOT

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // equals 11110000

Bitwise AND

let partialTruth: UInt8 = 0b11111000
let missingTruth: UInt8 = 0b00111001
let theRealTruth = partialTruth & missingTruth // equals 00111000

Bitwise OR

let upperTeeth: UInt8 = 0b01010101
let lowerTeeth: UInt8 = 0b10101010
let bite = upperTeeth | lowerTeeth // equals 11111111

Bitwise XOR

let boysAndGirls: UInt8 = 0b00010100
let girlsAndBoys: UInt8 = 0b00000101
let nextGenerationExistence = boysAndGirls ^ girlsAndBoys
 // equals 00010001

Bitwise shift

unsigned integers:

  1. existing bits are moved to the left or right by the requested number of places
  2. any bits moved beyond the bounds of integer’s storage are discarded
  3. zeros are inserted in the spaces left behind

signed integers: when shifting signed integers to the right, apply the same rule as unsigned integers, but fill any empty bits on the left with the sign bit, rather than with a zero.

Overflow operators

var unsignedOverflow = UInt8.max // 255
// unsignedOverflow += 1 would cause an error
unsignedOverflow = unsignedOverflow &+ 1 // 0

Precedence and Associativity

2 + 3 % 4 * 5 // equals 17
2 + ((3 % 4) * 5) // equals 17

Operator Methods

Classes and structures can provide their own implementation of existing operators (ie. overloading).

Example

The universe was born with two concepts: spirit and material. Characteristic is a structure with spirit and material as boolean properties. Use this as a start, define custom operators and derive the follows: human, evil and divine.

struct Characteristic {
    var spirit = false
    var material = false
}

infix operator &|
infix operator |&
prefix operator ...

extension Characteristic {
    
    static func && (left: Characteristic, right: Characteristic) -> Characteristic {
        return Characteristic(spirit: left.spirit && right.spirit, material: left.material && right.material)
    }
    
    static func || (left: Characteristic, right: Characteristic) -> Characteristic {
        return Characteristic(spirit: left.spirit || right.spirit, material: left.material || right.material)
    }
    
    static func &| (left: Characteristic, right: Characteristic) -> Characteristic {
        return Characteristic(spirit: left.spirit && right.spirit, material: left.material || right.material)
    }
    
    static func |& (left: Characteristic, right: Characteristic) -> Characteristic {
        return Characteristic(spirit: left.spirit || right.spirit, material: left.material && right.material)
    }
    
    static prefix func ... (c: Characteristic) -> String {
        switch (c.spirit, c.material) {
        case (true, false):
            return "Spirit without matter"
        case (false, true):
            return "Souless materialist"
        case (false, false):
            return "Neither spirit or material, initial void"
        case (true, true):
            return "Ordinary people, mind and body"
        }
    }
}

let spiritual = Characteristic(spirit: true, material: false)
let material = Characteristic(spirit: false, material: true)

let human = spiritual || material
let evil = spiritual &| material
let divine = spiritual |& material

print("Human: \(...human)")
print("Evil: \(...evil)")
print("Divine: \(...divine)")

ToC

Patterns

A pattern represents the structure of a single value or a composite value. e.g. (1, 2) is a comma-separated list of two elements, matched by the pattern (x, y).

Wildcard Pattern

let somethingImportant = "Don't struggle which side of bread you spread butter on - you eat both sides."
for _ in 1...3 {
    // repeat three times
    print(somethingImportant)
}

Identifier pattern

An identifier pattern is the variable or constant itself, matched by any value.

let someValue = 123

Value binding pattern

Binds matched values to variable or constant names

let point = (1, 9, 3)
switch point {
case let (x, 9, _):
    print("At y-coordinate 9, x-coordinates of \(x), there's something on the z-plane.")
default:
    break
}

Tuple pattern

Comma-separated list of zero or more patterns, enclosed by parentheses.

let (goodNews, badNews): (Int, Int) = (2, 3)
let newsGoodBadPerDay = [(1, 0), (3, 2), (5, 7)]
for (goodNews, _) in newsGoodBadPerDay {print("ignore bad news, number of good news on the day: \(goodNews)")}

The parentheses around a single element pattern has no effect, the followings are equivalent:

let someonesAge = 1
let (sometowsAge) = 2
let (somethreesAge): Int = 3

Optional pattern

An optional pattern matches values wrapped in a some(Wrapped) case of an Optional<Wrapped> enumeration.

let kingsClothes: Int? = nil
if case .some(let x) = kingsClothes {
    print(x)
} else {
    print("Kid: the King has no clothes!")
}

if case let x? = kingsClothes {
    print(x)
} else {
    print("Kid: the King has no clothes!")
}

Type-casting patterns

is <type>
<pattern> as <type>

Expression pattern

Value of expression. Appears only in switch statement case label.

let spaceDust = (102, 57, 81)
switch spaceDust {
case (0, 0, 0):
    print("Origin")
case (-10...10, -10...10, -10...10):
    print("Around origin")
default:
    print("Somewhere else")
}

You can overload ~= operator to provide custom expression matching behaviour

func ~=(pattern: String, value: Int) -> Bool {
    return pattern == "\(value)"
}

switch spaceDust {
case ("0", "0", "0"):
    print("Space dust starts from origin")
default:
    print("Space dust has gone from origin")
}

ToC

Error Handling

If you have Java background, error handling is the similar trick as Java’s try...catch... and throw. In Swift, errors are represented by values of types that conform to the empty Error protocol which indicates that type can be used for error handling.

Enumerations are well suited to modelling a group of related error conditions.

func functionThatThrowsErrors() throws -> String
func functionThatDoesntThrowError() -> String

Handling errors using do-catch

do {
    try <expression>
    <statements>
} catch <pattern 1> {
    <statements>
} catch <pattern 2> where <condition> {
    <statements>
}

Example

In the classic of Buddhism, there are seven types of sorrows in life: birth, aging, sickness, death, separation of lovers, hatred and unsatisfiable desire. Represents each type of sorrows as an Error, define a function that can throw these errors, and a piece of code that try the function and catch possible errors.

enum SorrowOfLife: Error {
    case birth(msg: String)
    case aging(msg: String)
    case sickness(msg: String)
    case death(msg: String)
    case separationOfLovers(msg: String)
    case hatred(msg: String)
    case unsatisfiableDesire(msg: String)
}

enum LifeTarget {
    case human, god
}

func rollDiceOfLife(for target: LifeTarget) throws -> String {
    guard target == .human else {
        return "silent, calm and harmony"
    }
    
    let sorrows: [SorrowOfLife] = [.birth(msg: "sin entered the world through one's birth"),
                                   .aging(msg: "there was a face in the mirror like a face out of time"),
                                   .sickness(msg: "if we got sick, at least we didn't die"),
                                   .death(msg: "death through sin, so death came to all people, because all sinned"),
                                   .separationOfLovers(msg: "over the sea grows the moon bright; We gaze on it far, far apart. Lovers complain of long, long night."),
                                   .hatred(msg: "why then when people part, is the moon full and bright?"),
                                   .unsatisfiableDesire(msg: "happiness lies in contentment")]
    
    let number = arc4random_uniform(UInt32(sorrows.count + 1))
    if number < sorrows.count {
        throw sorrows[Int(number)]
    }
    
    return "there is still hope"
}

do {
    let diceResult = try rollDiceOfLife(for: .human)
    print("dice result: \(diceResult)")
} catch SorrowOfLife.birth(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.aging(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.sickness(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.death(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.separationOfLovers(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.hatred(let msg) {
    print("sorrow of life: \(msg)")
} catch SorrowOfLife.unsatisfiableDesire(let msg) {
    print("sorrow of life: \(msg)")
} catch _ {
    print("something unexpected in life")
}

Error to optional values

if let onesLife = try? rollDiceOfLife(for: .human) {
    print("One's life: \(onesLife)")
} else {
    print("One's life thrown error.")
}

Disabling error propagation

sometimes you know there won’t be an error thrown

let divineLife = try! rollDiceOfLife(for: .god)
print("Divine's life: \(divineLife)")

Cleanup with defer

I expect to pass through life but once. If therefore, there be any kindness I can show, or any good thing I can do to any fellow being, let me do it now, and not defer or neglect it, as I shall not pass this way again. – William Penn

excuted when current scope exists, in reverse order; I guess it is to have a logical way of cleanup from the most recent to the least recent changes; It’s like when you go upstairs from level 1 to level 3, and you want to go downstairs, you have to follow the reversed way as when you went up, that is level 3, 2, 1.

func printInstructions() {
    print("Velilind's Laws of Experientation:")
    print("1. If reproducibility may be a problem, conduct the test only once.")
    defer {
        print("cleanup step 1")
    }
    print("2. If a straight line fit is required, obtain only two data points.")
    defer {
        print("cleanup step 2")
    }
}

printInstructions()

// prints:
// Velilind's Laws of Experientation:
// 1. If reproducibility may be a problem, conduct the test only once.
// 2. If a straight line fit is required, obtain only two data points.
// cleanup step 2
// cleanup step 1

Guess what happens if you put defer inside defer?

func quoteMarkTwain() {
    defer { print("wisdom never to use either") }
    defer { defer { print("freedom of thought") } }
    defer { defer { defer { print("freedom of speech") } } }
    print("It is by the fortune of God that, in this country, we have three benefits:")
}

quoteMarkTwain()

// prints:
// It is by the fortune of God that, in this country, we have three benefits:
// freedom of speech
// freedom of thought
// wisdom never to use either

defer blocks don’t capture the current value of a variable.

func tasteOfLove() {
    var taste = "sweet"
    defer { print("a bit \(taste)") }
    taste = "sour"
    defer { print("a bit \(taste)") }
    print("Taste of the first time one falls in love:")
}

tasteOfLove()

We want to express that sweet and sour are the taste of the first time one falls in love, however there’s only sour printed.

ToC

Encoding and Decoding

Protocols

Encodable

A type that can encode itself into a different representation.

func encode(to: Encoder) throws

Decodable

A type that can decode itself from a different representation.

// creates a new instance by decoding from given decoder
init(from decoder: Decoder) throws

Codable

typealias Codable = Encodable & Decodable

Automatic coding

Conforming to Codable and make sure all stored properties are also codable.

Example:

a fool with a tool (is still a fool).

struct Fool: Codable {
    var id: String
    var name: String
    var tool: Tool
    
    // renaming properties
    enum CodingKeys: String, CodingKey {
        case id = "identifier"
        case name
        case tool
    }
}

struct Tool: Codable {
    var name: String
}

Coding custom types

var tool = Tool(name: "Too")
var fool = Fool(id: "007", name: "Foo", tool: tool)

let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(fool)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)

let jsonDecoder = JSONDecoder()
fool = try! jsonDecoder.decode(Fool.self, from: jsonData)

Coding manually

Suppose the JSON looks like: {“identifier”: “Some One”, name: “Foo”, toolInHand: “Hammer”}

struct AnotherFool {
    var id: String
    var name: String
    var tool: Tool
    
    // renaming properties
    enum CodingKeys: String, CodingKey {
        case id = "identifier"
        case name
        case toolInHand
    }
}

extension AnotherFool: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(tool.name, forKey: .toolInHand)
    }
}

extension AnotherFool: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(String.self, forKey: .id)
        name = try values.decode(String.self, forKey: .name)
        let toolInHand = try values.decode(String.self, forKey: .toolInHand)
        tool = Tool(name: toolInHand)
    }
}

Limitation

ToC

Memory Safety

Similar to Objective-C, memory management in Swift is also based on ARC (Automatic Reference Counting), when there’s no reference to an object, its allocated memory will be released, otherwise, if there’s at least one reference to the object, it will stay in memory until further notice.

Capture list

A capture list is an array of variables captured by a closure

Example

Pokemon runs fast, we want to catch Pikachu when it appears.

var pokemon = "Pikachu"
var closure = { print("Let's catch \(pokemon)") }
pokemon = "Zoobat"
closure()
// Print: "Let's catch Zoobat"

pokemon = "Pikachu"
closure = { [pokemon] in print("Let's catch \(pokemon)") }
pokemon = "Zoobat"
closure()
// Print: "Let's catch Pikachu"

With reference types, a capture list makes the closure to capture and store the current reference stored inside the captured variable.

Unowned self

class Book {
    
    var name = "Book"
    
    lazy var sageRetrieveContent: () -> String = {
        [unowned self] in
        return "\(self.name): reading helps us learn so much about beauty and truth that we can live a better life in our own ways."
    }
    
    lazy var ordinaryRetrieveContent: () -> String = {
        [unowned self] in
        return "\(self.name): as long as one studies hard, wealth and beautiful women will all come his way."
    }
}

let book = Book()
print(book.sageRetrieveContent())
print(book.ordinaryRetrieveContent())

The strong weak pattern

When self could be nil

extension Book {
    func checkWorm() {
        DispatchQueue.main.async {
            [weak self] in
            guard let strongSelf = self else {
                print("The book no longer exists.")
                return
            }
            let hasWorm = strongSelf.name.contains("worm")
            if hasWorm {
                print("The book has worm.")
            }
        }
    }
}

ToC

Data Structure in Swift

Stack

class Stack<T> {

    var stack = [T]()
    var isEmpty: Bool {
        return self.stack.isEmpty
    }
    var peek: T? {
        return self.stack.last
    }
    var size: Int {
        return self.stack.count
    }

    func push(_ thing: T) {
        self.stack.append(thing)
    }

    func pop() -> T? {
        guard self.isEmpty == false else {
            return nil
        }
        return self.stack.removeLast()
    }
}

Linked List

class ListNode<T> {
    var value: T
    var next: ListNode<T>?

    init(_ value: T) {
        self.value = value
    }
}

class LinkedList<T> {
    var head: ListNode<T>?
    var tail: ListNode<T>?
}

extension LinkedList {
    func appendToTail(_ value: T) {
        if let tail = self.tail {
            tail.next = ListNode(value)
            self.tail = tail.next
        } else {
            self.tail = ListNode(value)
            self.head = self.tail
        }
    }
    
    func appendToHead(_ value: T) {
        if let head = self.head {
            let newHead = ListNode(value)
            newHead.next = head
            self.head = newHead
        } else {
            self.head = ListNode(value)
            self.tail = self.head
        }
    }
}

func printNode<T: CustomStringConvertible>(_ node: ListNode<T>?) {
    var current = node
    var values = [T]()
    while current != nil {
        values.append(current!.value)
        current = current!.next
    }
    print("Values in list nodes: \(values)")
}

let node1 = ListNode(2)
let node2 = ListNode(9)
let node3 = ListNode(7)
let node4 = ListNode(3)
node1.next = node2
node2.next = node3
node3.next = node4

printNode(node1)

Given a linked list and a value X, return a new linked list with values less than X on left and values greater than X on right

func partition<T: Comparable>(_ head: ListNode<T>?, _ x: T) -> ListNode<T>? {
    var leftHead: ListNode<T>?
    var rightHead: ListNode<T>?
    var left: ListNode<T>?
    var right: ListNode<T>?
    var node = head

    while node != nil {
        if node!.value < x {
            if leftHead == nil {
                leftHead = node
                left = leftHead
            } else {
                left!.next = node
                left = node!
            }
        } else {
            if rightHead == nil {
                rightHead = node
                right = rightHead
            } else {
                right!.next = node
                right = node!
            }
        }
        node = node!.next
    }
    
    if let right = right {
        right.next = nil
    }
    if let left = left {
        left.next = rightHead
    }
    if leftHead != nil {
        return leftHead
    } else {
        return rightHead
    }
}

if let partitionedList = partition(node1, 5) {
    printNode(partitionedList)
}

// prints: Values in list nodes: [2, 3, 9, 7]

Given a head node, determine whether a cycle exists.

func hasCycle<T>(_ head: ListNode<T>?) -> Bool {
    var slow = head
    var fast = head

    while fast != nil && fast!.next != nil {
        slow = slow!.next
        fast = fast!.next!.next
        
        if slow === fast {
            return true
        }
    }

    return false
}

Given a head node, remove the nth last node.

func removeNthFromEnd<T>(head: ListNode<T>?, _ n: Int) -> ListNode<T>? {
    guard let head = head else {
        return nil
    }
    
    var first: ListNode<T>? = head
    var second: ListNode<T>? = head

    for _ in 0 ..< n {
        if second == nil {
            break
        }
        second = second!.next
    }
    
    while second != nil && second!.next != nil {
        first = first!.next
        second = second!.next
    }
    
    first!.next = first!.next!.next
    return head
}

print("Existing list:")
printNode(node1)

print("List with 2nd last node removed:")
if let newHead = removeNthFromEnd(head: node1, 2) {
    printNode(newHead)
}

Queue

protocol Queue {
    associatedtype Element

    var isEmpty: Bool { get }
    var size: Int { get }
    var peek: Element? { get }

    mutating func enqueue(_ newElement: Element)
    mutating func dequeue() -> Element?
}

struct ArrayQueue<T>: Queue {

    var isEmpty: Bool {
        return self.left.isEmpty && self.right.isEmpty
    }
    
    var size: Int {
        return self.left.count + self.right.count
    }
    
    var peek: T? {
        return self.left.isEmpty ? self.right.first : self.left.last
    }
    
    private var left = [T]()
    private var right = [T]()
    
    mutating func enqueue(_ newElement: T) {
        self.right.append(newElement)
    }
    
    mutating func dequeue() -> T? {
        if self.left.isEmpty {
            self.left = self.right.reversed()
            self.right.removeAll()
        }
        return self.left.popLast()
    }
}

struct StackQueue<T>: Queue {

    var isEmpty: Bool {
        return self.stackA.isEmpty && self.stackB.isEmpty
    }
    
    var peek: T? {
        self.shift()
        return self.stackB.peek
    }
    
    var size: Int {
        return self.stackA.size + self.stackB.size
    }
    
    mutating func enqueue(_ newElement: T) {
        self.stackA.push(newElement)
    }
    
    func dequeue() -> T? {
        self.shift()
        return self.stackB.pop()
    }
    
    private func shift() {
        if self.stackB.isEmpty {
            while let fromA = self.stackA.pop() {
                self.stackB.push(fromA)
            }
        }
    }

    private var stackA = Stack<T>()
    private var stackB = Stack<T>()
}

Simplify a file path.

func simplifyPath(_ path: String) -> String {
    var simplifiedPath = [String]()
    let pathComponents = path.components(separatedBy: "/")
    for component in pathComponents {
        guard component != "." else {
            continue
        }
        if component == ".." {
            if simplifiedPath.isEmpty == false {
                simplifiedPath.removeLast()
            }
        } else if component.isEmpty == false {
            simplifiedPath.append(component)
        }
    }
    
    let fullPath = simplifiedPath.reduce("") { (base, path) -> String in
        return "\(base)/\(path)"
    }
    
    return fullPath.isEmpty ? "/" : fullPath
}

let shortPath = "/home/username/Documents/../Picture/./Travel/"
let simplifiedPath = simplifyPath(shortPath)
print("Simplified path: \(simplifiedPath)")

Binary Tree

class TreeNode<T> {
    var value: T
    var left: TreeNode<T>?
    var right: TreeNode<T>?

    init(_ value: T) {
        self.value = value
    }
}

func depthOfTree<T>(_ root: TreeNode<T>?) -> Int {
    guard let root = root else {
        return 0
    }
    return max(depthOfTree(root.left), depthOfTree(root.right)) + 1
}

Check whether a binary tree is a valid search tree. (BST)

func isValidBST<T: Comparable>(root: TreeNode<T>?) -> Bool {
    return isValidBSTNode(root, nil, nil)
}

func isValidBSTNode<T: Comparable>(_ node: TreeNode<T>?, _ min: T?, _ max: T?) -> Bool {
    guard let node = node else {
        return true
    }
    if let min = min, node.value <= min {
        return false
    }
    if let max = max, node.value >= max {
        return false
    }
    return isValidBSTNode(node.left, min, node.value) && isValidBSTNode(node.right, node.value, max)
}

Traversal Binary Tree

func preorderTraversal<T>(root: TreeNode<T>?) -> [T] {
    var result = [T]()
    var stack = [TreeNode<T>]()
    var node = root
    while node != nil || stack.isEmpty == false {
        if let nonNilNode = node {
            result.append(nonNilNode.value)
            stack.append(nonNilNode)
            node = nonNilNode.left
        } else {
            node = stack.removeLast().right
        }
    }
    return result
}

let tnode1 = TreeNode(1)
let tnode2 = TreeNode(2)
let tnode3 = TreeNode(3)
let tnode4 = TreeNode(4)
let tnode5 = TreeNode(5)
let tnode6 = TreeNode(6)
let tnode7 = TreeNode(7)
let tnode8 = TreeNode(8)
let tnode9 = TreeNode(9)
let tnode10 = TreeNode(10)
let tnode11 = TreeNode(11)
let tnode12 = TreeNode(12)
let tnode13 = TreeNode(13)
let tnode14 = TreeNode(14)
let tnode15 = TreeNode(15)

tnode1.left = tnode2
tnode1.right = tnode3
tnode2.left = tnode4
tnode2.right = tnode5
tnode3.left = tnode6
tnode3.right = tnode7
tnode4.left = tnode8
tnode4.right = tnode9
tnode5.left = tnode10
tnode5.right = tnode11
tnode6.left = tnode12
tnode6.right = tnode13
tnode7.left = tnode14
tnode7.right = tnode15

let result = preorderTraversal(root: tnode1)
print("Preorder traversal result:")
result.forEach { print("\($0)") }

// Preorder traversal result:
// 1
// 2
// 4
// 8
// 9
// 5
// 10
// 11
// 3
// 6
// 12
// 13
// 7
// 14
// 15

ToC

Sorting and Searching

Merge Sort

func mergeSort(_ array: [Int]) -> [Int] {
    var helper = Array(repeating: 0, count: array.count), array = array
    mergeSort(&array, &helper, 0, array.count - 1)
    return array
}

func mergeSort(_ array: inout [Int], _ helper: inout [Int], _ low: Int, _ high: Int) {
    guard low < high else {
        return
    }
    let middle = (high - low) / 2 + low
    mergeSort(&array, &helper, low, middle)
    mergeSort(&array, &helper, middle + 1, high)
    merge(&array, &helper, low, middle, high)
}

func merge(_ array: inout [Int], _ helper: inout [Int], _ low: Int, _ middle: Int, _ high: Int) {
    for i in low...high {
        helper[i] = array[i]
    }
    
    var left = low, right = middle + 1, current = low
    while left <= middle && right <= high {
        if helper[left] < helper[right] {
            array[current] = helper[left]
            left += 1
        } else {
            array[current] = helper[right]
            right += 1
        }
        current += 1
    }
    
    guard middle - left >= 0 else {
        // check whether left half exhausted, if so, there's no need to handle rest
        return
    }
    
    for i in 0...(middle - left) {
        // handle the rest, only left half can have remaining
        array[current] = helper[left + i]
    }
}

Quick Sort

func quickSort(_ array: [Int]) -> [Int] {
    guard array.count > 1 else {
        return array
    }
    let pivot = array[array.count / 2]
    let left = array.filter { $0 < pivot }
    let middle = array.filter { $0 == pivot }
    let right = array.filter { $0 > pivot }
    return quickSort(left) + middle + quickSort(right)
}

Basic Searching

While iterating through the whole collection could find search term in O(n), searching in a sorted collection in a binary way could reduce it to O(logn).

func binarySearch<T: Comparable>(_ values: [T], _ target: T) -> Bool {
    var left = 0, mid = 0, right = values.count - 1
    while left <= right {
        mid = (right - left) / 2 + left
        if values[mid] == target {
            return true
        } else if values[mid] < target {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    return false
}

// note: people often forget +1 and -1 when re-adjusting left and right

There are a few meetings, merge the ones that overlap. Example: given [[1, 3], [5, 6], [4, 7], [2, 3]] Output: [[1, 3], [4, 7]]

class MeetingTime {
    var start: Int
    var end: Int
    init(_ start: Int, _ end: Int) {
        self.start = start
        self.end = end
    }
}

func merge(meetingTimes: [MeetingTime]) -> [MeetingTime] {
    guard meetingTimes.count > 1 else {
        return meetingTimes
    }
    
    let sortedTimes = meetingTimes.sorted { (time1, time2) -> Bool in
        if time1.start != time2.start {
            return time1.start < time2.start
        } else {
            return time1.end < time2.end
        }
    }
    
    var mergedTimes = [MeetingTime]()
    mergedTimes.append(sortedTimes[0])
    
    for i in 1..<sortedTimes.count {
        let current = sortedTimes[i]
        let last = sortedTimes[sortedTimes.count - 1]
        if current.start > last.end {
            mergedTimes.append(current)
        } else {
            last.end = max(last.end, current.end)
        }
    }
    
    return mergedTimes
}

A product has several versions, if version n had a bug, versions after n would all have the same bug. Given a function that checks whether a given version has a bug, find the first version that has the bug.

func findFirstBugVersion(version: Int, isBugVersion: ((Int) -> Bool)) -> Int {
    guard version > 1 else {
        return version
    }
    var left = 1, mid = version / 2, right = version
    while left < right {
        mid = (right - left) / 2 + left
        if isBugVersion(mid) {
            right = mid
        } else {
            left = mid + 1
        }
    }
    return left
}

Use binary search for rotated array, for example, [0, 1, 2, 4, 5, 6, 9] becomes [4, 5, 6, 9, 0, 1, 2].

func searchRotated<T: Comparable>(values: [T], target: T) -> Int {
    var (left, mid, right) = (0, 0, values.count - 1)
    
    while left <= right {
        mid = (right - left) / 2 + left
        
        if values[mid] == target {
            return mid
        }
        
        if values[mid] > values[left] {
            if values[mid] > target && target >= values[left] {
                right = mid - 1
            } else {
                left = mid + 1
            }
        } else {
            if values[mid] < target && target <= values[right] {
                left = mid + 1
            } else {
                right = mid + 1
            }
        }
    }
    
    return -1
}

ToC

Objective-C

Mixing Objective-C and Swift

Initializer (init)

Difference of Protocol

Introspection

In Objective-C, there are two methods to check whether an object is of certain type:

In Swift, is can be used for the same purpose, it’s the same as isKindOfClass.

ToC