Skip to content

Swift Xcode

Mingtao edited this page Aug 21, 2019 · 31 revisions

Xcode shortcut keys

Cmd 0: hide/show left panel

Cmd 1: show project navigator

Cmd Option 0: hide/show right panel

Option left-click a file: Open file side by side

Cmd Shift F: Search within file / file name

Cmd Shift O: Open file

Option left-click a variable: Check its type

Swift code

String

Character at index of a string

let greeting = "Hello world!"
let firstChar = greeting[greeting.startIndex] // 'H'
let secondCharIndex = greeting.index(greeting.startIndex, offsetBy: 1)
let secondChar = greeting[secondCharIndex] //'e'
let lastCharIndex = greeting.index(before: greeting.endIndex)
let lastChar = greeting[lastCharIndex] //'!'
let secondLastCharIndex = greeting.index(greeting.endIndex, offsetBy: -2)
let secondLastChar = greeting[secondLastCharIndex] //'d'

Insert and Remove

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"

Substring

let greeting2 = "Hello,world!"
let index = greeting2.firstIndex(of: ",") ?? greeting2.endIndex
let excludeIndexSubstring = greeting2[..<index] //"Hello" of type String.SubSequence
let includeIndexSubstring = greeting2[...index] //"Hello," of type String.SubSequence
let newString = String(excludeIndexSubstring) //"Hello" of type String

Array / List

var someInts = [Int]()
someInts.append(3)
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
var shoppingList = ["Eggs", "Milk"]
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
shoppingList.insert("Maple Syrup", at: 0)
let mapleSyrup = shoppingList.remove(at: 0)
let apples = shoppingList.removeLast()
for item in shoppingList {
    print(item)
}
for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)")
}

Set

A type must be hashable in order to be stored in a set

var letters = Set<Character>()
let count = letters.count
let isEmpty = letters.isEmpty
letters.insert("a")
letters = [] // letters is now an empty set, but is still of type Set<Character>
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres2: Set = ["Rock", "Classical", "Hip hop"] //Simpler than above line
let removedGenre = favoriteGenres.remove("Rock")
let contains = favoriteGenres.contains("Funk")
for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted() // []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9]
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals) // true
farmAnimals.isSuperset(of: houseAnimals) // true
farmAnimals.isDisjoint(with: cityAnimals) // true. To determine whether two sets have no values in common.

Dictionary

A dictionary Key type must conform to the Hashable protocol, like a set’s value type.

var namesOfIntegers = [Int: String]()
namesOfIntegers[16] = "sixteen"
namesOfIntegers = [:] // namesOfIntegers is once again an empty dictionary of type [Int: String]
let count = namesOfIntegers.count
let isEmpty = namesOfIntegers.isEmpty
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports2 = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] //Simpler format than above line
airports["LHR"] = "London"
let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") //oldValue is of type string?
airports["APL"] = nil //Remove a key-value pair
let removedValue = airports.removeValue(forKey: "DUB") //Another way to remove a key-value pair

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
for airportName in airports.values {
    print("Airport name: \(airportName)")
}
let airportCodes = [String](airports.keys) // airportCodes is ["LHR", "YYZ"]
let airportNames = [String](airports.values) // airportNames is ["London Heathrow", "Toronto Pearson"]

Control flow

for loop

// Open range (i.e. not include 60)
let minuteInterval = 5
let minutes = 60
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    print (tickMark)// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

//Close range (i.e. include 12)
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    print (tickMark)
}

while loop

var a = 0
var b = 5
while a < b {
    print (a)
    if a == 3 {
        break
    }
    a += 1
}

repeat while loop

a = 0
repeat {
    print (a)
    a += 1
}while a < b

switch

switch statements in Swift do not fall through the bottom of each case and into the next one by default.

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case -1: fallthrough
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")

Tuple

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) is at the origin")
case (_, 0):
    print("\(somePoint) is on the x-axis")
case (0, _):
    print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
default:
    print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"

Tuple let

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case (let x, let y):
    print("somewhere else at (\(x), \(y))")
}

where

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}

Labeled statements

label name: while condition {
    statements
}

Guard statement

guard always has an else clause

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }
    
    print("Hello \(name)!")
    
    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }
    
    print("I hope the weather is nice in \(location).")
}

greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."

Check API availability

if #available(iOS 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}

Functions

func greet(p person: String) -> String {
    return "Hello, " + person + "!"
}

print(greet(p : "Anna"))

func greeting(_ person: String) -> String {
    return "Hello, " + person + "!"
}

print (greeting("Tom"))

Multiple return values

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")

Optional tuple return values

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

if let bounds = minMax(array : [Int]()) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}else{
    print("Array is empty")
}

Default Parameter Values

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    print ("\(parameterWithoutDefault), \(parameterWithDefault)")
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4)

Variadic Parameters (Equivalent to Java varargs)

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5) // returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75) // returns 10.0, which is the arithmetic mean of these three numbers

In-Out Parameters (Like pointer in C)

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // Prints "someInt is now 107, and anotherInt is now 3"

Use Function Types

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
    return a * b
}

var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))") // Prints "Result: 5"
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))") // Prints "Result: 6"
let anotherMathFunction = addTwoInts //Type can be inferred

Pass function as parameter types

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)   // Prints "Result: 8"
//Pass function as return types
func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
print("Counting to zero:")
//Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")

Nested functions

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

Closure

Basics

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
var reversedNames = names.sorted(by: backward) // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
reversedNames = names.sorted(by: {(s1: String, s2: String) -> Bool in
    return s1 > s2
})
reversedNames = names.sorted(by: {s1, s2 in return s1 > s2})
reversedNames = names.sorted(by: {s1, s2 in s1 > s2})
reversedNames = names.sorted(by: {$0 > $1})
reversedNames = names.sorted(by: >)

Trailing Closures

If you need to pass a closure expression to a function as the function’s final argument, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function.

reversedNames = names.sorted() { $0 > $1 }
reversedNames = names.sorted { $0 > $1 } //If closure is the only argument

Here’s how you can use the map(_:) method with a trailing closure to convert an array of Int values into an array of String values. The array [16, 58, 510] is used to create the new array ["OneSix", "FiveEight", "FiveOneZero"]:

let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

let closure = { (number : Int) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}

let strings = numbers.map(closure)

let strings2 = numbers.map{ (number : Int) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}

Capturing Values

A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function.

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

var incrementBy5 = makeIncrementer(forIncrement: 5)
print(incrementBy5()) // 5
print(incrementBy5()) // 10
var anotherIncrementBy5 = incrementBy5
print(anotherIncrementBy5()) // 15

var incrementBy10 = makeIncrementer(forIncrement: 10)
print (incrementBy10()) // 10
print (incrementBy10()) // 20
print (incrementBy10()) // 30

Escaping closure

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { //completionHandler will be called later in the code after someFunctionWithEscapingClosure() is called 
    completionHandlers.append(completionHandler)
}

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure {
            print ("set x to 100")
            self.x = 100
        }
        someFunctionWithNonescapingClosure {
            print ("set x to 200")
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x) //200 , "set x to 100" is not printed

completionHandlers.first?()
print(instance.x) //100

Autoclosure

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count) // Prints "5"

let customerProvider = { customersInLine.remove(at: 0) } //type is () -> String
print(customersInLine.count) // Prints "5"

print("Now serving \(customerProvider())!") // Prints "Now serving Chris!"
print(customersInLine.count) // Prints "4"

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: {customersInLine.remove(at: 0)}) // Prints "Now serving Alex!"

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: {customersInLine.remove(at: 0)}) // Prints "Now serving Ewa!"
serve(customer: customersInLine.remove(at: 0)) // {} can be omitted due to @autoclosure. Prints "Now serving Barry!"

Enumeration

Basics

enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
for beverage in Beverage.allCases {
    print(beverage)
}

Associated Values

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}

Raw Values

enum Animal: String {
    case cat = "tom"
    case mouse = "jerry"
    case duck = "donald"
}

let aMouse = Animal.mouse
print(aMouse.rawValue)

enum Planet: Int, CaseIterable { //Implict raw values. the implicit value for each case is one more than the previous case. 
    case mercury = 100, venus, earth, mars, jupiter, saturn, uranus, neptune
}

for p in Planet.allCases{
    print ("\(p) = \(p.rawValue)") // 100 - 108
}

let possiblePlanet = Planet(rawValue: 102) //earth

Recursive Enumerations

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))

Structs vs Classes

Value types vs reference types

Structs are value types while Classes are reference types

var struct1 = MyStruct()
struct1.name = "old name"
var struct2 = struct1
struct2.name = "new name"
//Now struct1.name = "old name"

var class1 = MyClass()
class1.name = "old name"
var class2 = class1
class2.name = "new name"
//Now class1.name = "new name"

=== vs ==

Note that identical to (represented by three equals signs, or ===) doesn’t mean the same thing as equal to (represented by two equals signs, or ==).

let class1 = MyClass(name: "name")
let class2 = class1
let class3 = MyClass(name: "name")
class1 === class2 //True
class1 === class3 //False
class1 == class3 //Maybe True TODO

Lazy init

class DataImporter {
    
    var filename : String
    
    init() {
        print ("Init Data Importer")
        filename = "data.txt"
    }
    

    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    lazy var importer = DataImporter()
    //var importer = DataImporter()
    func doStuff(){
        print ("Do stuff")
    }
    func doImport(){
        print (importer.filename)
    }
    
}

let manager = DataManager()
manager.doStuff()
manager.doImport()
// Prints Do stuff, Init Data Importer, data.txt with lazy keyword
// Prints Init Data Importer, Do stuff, data.txt without lazy keyword

Computed Properties

struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

didSet and willSet

class StepCounter {
    var totalSteps: Int = 0 {
        willSet {
            print("About to set totalSteps to \(newValue), add to \(totalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

Type Properties

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}

print(SomeStructure.storedTypeProperty) // Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty) // Prints "Another value."
print(SomeStructure.computedTypeProperty) // Prints "1"
SomeStructure.computedTypeProperty = 6 //Compilation error as computedTypeProperty is a read only property

Methods

mutating

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

self

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next() // ovenLight is now equal to .high
ovenLight.next() // ovenLight is now equal to .off

Type methods

class SomeClass {
    class func someTypeMethod1() {
        print ("calling method 1")
    }
    
    static func someTypeMethod2() {
        print ("calling method 2")
    }
}
SomeClass.someTypeMethod1() //Prints "calling method 1"
SomeClass.someTypeMethod2() //Prints "calling method 2"

Subscripts

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")

Multiple input parameters

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}
 
var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
 
print (matrix[0, 1]) //Prints 1.5
print (matrix[2, 2]) //Error: Index out of range

Inheritance

Override properties

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // do nothing - an arbitrary vehicle doesn't necessarily make a noise
    }
}

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

let car = Car()
car.description

Overriding property observer

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")

Initialization

struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) // boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15) // freezingPointOfWater.temperatureInCelsius is 0.0

Memberwise Initializers for Structure Types

struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0) //Good 

class Size2 {
    var width = 0.0, height = 0.0
}
let twoByTwo2 = Size2(width: 2.0, height: 2.0) //Compilation error

self.init()

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

Designated init and Convenience init

class Food {
    var name: String
    init(name: String) { //designated init
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]") //Convenience init() must call designated init()
    }
}

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) { //designated init
        self.quantity = quantity
        //self.name = name //This line causes compilation error
        super.init(name: name)
    }
    override convenience init(name: String) { //Convenience init
        self.init(name: name, quantity: 1)
    }
    convenience init(quantity: Int) {
        self.init(name:"[Unnamed]", quantity: quantity)
    }
}

let oneMysteryItem = RecipeIngredient() //no-arg init() is inherited from superclass
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var price = 0.0
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}
//Because it provides a default value for all of the properties it introduces and does not define any initializers itself, ShoppingListItem automatically inherits all of the designated and convenience initializers from its superclass.
let shoppingList1 = ShoppingListItem()
let shoppingList2 = ShoppingListItem(name: "banana", quantity: 2)
let shoppingList3 = ShoppingListItem(name: "banana")
let shoppingList4 = ShoppingListItem(quantity: 3)

Failable init

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}    

Enumerations with raw values automatically receive a failable initializer, init?(rawValue:)

enum TemperatureUnit: Character {
    case kelvin = "K", celsius = "C", fahrenheit = "F"
}

let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}

let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

Override failable init with non-failable init

class Document {
    var name: String?
    // this initializer creates a document with a nil name value
    init() {}
    // this initializer creates a document with a nonempty name value
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
 
class UntitledDocument: Document {
    override init() {
        super.init(name: "[Untitled]")!
    }
}

Required Initializers

Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    required init() {
        // subclass implementation of the required initializer goes here
    }
}

Setting a Default Property Value with a Closure or Function

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
    }()
}

Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.

deinit

class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

class Player {
    var coinsInPurse: Int
    var name: String
    init(name: String, coins: Int) {
        self.name = name
        coinsInPurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
    }
    deinit {
        print("Player \(name) has left the game")
        Bank.receive(coins: coinsInPurse)
    }
}

var playerOne: Player? = Player(name: "Josh", coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
// Prints "There are now 9900 coins left in the bank"

playerOne!.win(coins: 2_000)
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
// Prints "PlayerOne won 2000 coins & now has 2100 coins"
print("The bank now only has \(Bank.coinsInBank) coins left")
// Prints "The bank now only has 7900 coins left"

playerOne = nil
// Prints "Player Josh has left the game"
print("The bank now has \(Bank.coinsInBank) coins")
// Prints "The bank now has 10000 coins"

weak self

https://www.avanderlee.com/swift/weak-self/

Clone this wiki locally