Error Handling

  • A function indicates that it can throw an error by including the throws  keyword in its declaration. When you call a function that can throw an error, you prepend the try  keyword to the expression.

    func canThrowAnError() throws {
        // this function may or may not throw an error
    }
    
  • When you call a function that can throw an error, you prepend the try  keyword to the expression.

  • Swift automatically propagates errors out of their current scope until they’re handled by a catch  clause.

    do {
        try canThrowAnError()
        // no error was thrown
    } catch {
        // an error was thrown
    }
    
  • Example :

    func makeASandwich() throws {
        // ...
    }
    
    
    do {
        try makeASandwich()
        eatASandwich()
    } catch SandwichError.outOfCleanDishes {
        washDishes()
    } catch SandwichError.missingIngredients(let ingredients) {
        buyGroceries(ingredients)
    }
    

precondition

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

assert

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.

guard

func hit(enemy: Monster?, with weapon: Weapon) {
    guard let enemy else { return }
    
    if enemy.isAlive {
        enemy.health -= weapon.damage
    }
}

if let

  • Extended version:

let myNumber = Int(possibleNumber)
// Here, myNumber is an optional integer
if let myNumber = myNumber {
    // Here, myNumber is a non-optional integer
    print("My number is \(myNumber)")
}
// Prints "My number is 123"
  • Simplified version:

    • Because this kind of code is so common, you can use a shorter spelling to unwrap an optional value: Write just the name of the constant or variable that you’re unwrapping. The new, unwrapped constant or variable implicitly uses the same name as the optional value.

    if let myNumber {
        print("My number is \(myNumber)")
    }
    // Prints "My number is 123"
    
  • You can include as many optional bindings and Boolean conditions in a single if  statement as you need to, separated by commas. If any of the values in the optional bindings are nil  or any Boolean condition evaluates to false , the whole if  statement’s condition is considered to be false . The following if  statements are equivalent:

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"


if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"
  • Example :

    func hit(enemy: Monster?, with weapon: Weapon) {
        if let enemy {
            if enemy.isAlive {
                enemy.health -= weapon.damage
            } 
        }
    }
    
    • “If the optional Monster  returned by enemy  contains a value, then it is true".

    func hit(enemy: Monster?, with weapon: Weapon) {
        if let new_enemy = enemy {
            if new_enemy.isAlive {
                new_enemy.health -= weapon.damage
            } 
        }
    }
    
    • “If the optional Monster  returned by enemy  contains a value, set a new constant called new_enemy  to the value contained in the optional.”

Fallback

  • Another way to handle a missing value is to supply a default value using the nil-coalescing operator ( ?? ). If the optional on the left of the ??  isn’t nil , that value is unwrapped and used. Otherwise, the value on the right of ??  is used. For example, the code below greets someone by name if one is specified, and uses a generic greeting when the name is nil .

let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Prints "Hello, friend!"

Force Unwrapping

  • You can ignore the fact that the object might be nil, using the !  operator:

    var selected: String?
    
    func okPressed() {
        // I "know" 'selected' will never be null, let me use it.
        saveFile(selected!)
    }
    
  • If it is nil , it will crash at runtime, so !  and fatalError  are equivalent.

    let possibleNumber = "123"
    let convertedNumber = Int(possibleNumber)
    
    
    let number = convertedNumber!
    
    
    guard let number = convertedNumber else {
        fatalError("The number was invalid")
    }
    

Implicit Unwrap

  • You can think of an implicitly unwrapped optional as giving permission for the optional to be force-unwrapped if needed.

  • Usage :

    • Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter.

    • An implicitly unwrapped optional is a normal optional behind the scenes, but can also be used like a non-optional value, without the need to unwrap the optional value each time it’s accessed.

  • Cautions :

    • If an implicitly unwrapped optional is nil  and you try to access its wrapped value, you’ll trigger a runtime error.

    • The result is exactly the same  as if you write an exclamation point to force unwrap a normal optional that doesn’t contain a value.

  • Creation :

    • You write an implicitly unwrapped optional by placing an exclamation point ( String! ) rather than a question mark ( String? ) after the type that you want to make optional.

    • Rather than placing an exclamation point after the optional’s name when you use it, you place an exclamation point after the optional’s type when you declare it.

  • Differences between Implicit and Explicit :

    let possibleString: String? = "An optional string."
    let forcedString: String = possibleString! // Requires explicit unwrapping
    
    
    let assumedString: String! = "An implicitly unwrapped optional string."
    let implicitString: String = assumedString // Unwrapped automatically
    

Overflow

  • Crashes :

    var score = Int.max
    
    score = score + 1  // Crashes due to overflow.
    
  • Ignoring with &  (yolo operator) :

    var score = Int.max
    
    score = score &+ 1  // Does not crash, but overflows (the number flips and becomes negative).
    

Concurrency

var a = [1, 2, 3, 4, 5]

Task {
    print(a.count)
}

a.append(10)  // Gives a warning for trying to modify something in a "race condition".