Learning Swift - Day Two

Done

The Goal

I want to do 100 days of learning Swift. This is day two of doing an hour a day. The goal of today is to get through the part one reading of documentation and also watch another Stanford leacture.


At the start of the day I focused on the documentation reading. I’m currently up to Enums and structs and have been playing around in a Swift playground.

The below is an example of what an enum can have in it. Swift assigns the raw values starting at zero and incrementing by one each time. In the example below, Ace is explicitly given a raw value of 1, and the rest of the raw values are assigned in order. You can also use strings or floating-point numbers as the raw type of an enumeration.

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "Ace"
        case .jack:
            return "Jack"
        case .queen:
            return "Queen"
        case .king:
            return "King"
        default:
            return String(self.rawValue)
        }
    }
}

let ace = Rank.ace
let aceRawValue = ace.rawValue

So every case above is assigned a value by default similar to an array’s index. Teh default value starting at and and then incrementing by one. In the above example we’re starting the default value at 1 rather than 0. So ace = 1, then two = 2, three = 3 so on and so on.

enum Suit {
    case spades, hearts, diamonds, clubs

    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "this is hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}

let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()

Another example:

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00am", "8:09pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case .result(let sunrise, let sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case .failure(let message):
    print("Failure... \(message)")
}
// Prints "Sunrise is at 6:00am and sunset is at 8:09pm."

Concurrency

func fetchUserId(from server: String) async -> Int {
    if server == "primary" {
        return 97
    }

    return 501
}

func fetchUsername(from server: String) async -> String {
    let userID = await fetchUserId(from: server)

    if userID == 501 {
        return "John Appleseed"
    }

    return "Guest"
}
func connectUser(to server: String) async {
    async let userID = fetchUserId(from: server)
    async let username = fetchUsername(from: server)
    let greeting = await "Hello \(username), user ID \(userID)"
    print(greeting)
}
Task {
    await connectUser(to: "primary")
}

let userIDs = await withTaskGroup(of: Int.self) { group in
    for server in ["primary", "secondary", "development"] {
        group.addTask {
            return await fetchUserId(from: server)
        }
    }

    var results: [Int] = []

    for await result in group {
        results.append(result)
    }

    return results
}
actor ServerConnection {
    var server: String = "primary"
    private var activeUsers: [Int] = []

    func connect() async -> Int {
        let userID = await fetchUserId(from: server)
        // communicate with server...
        activeUsers.append(userID)

        return userID
    }
}

let server = ServerConnection()
let userID = await server.connect()

Protocols & Extensions

protocol ExampleProtocol {
    var simpleDescription: String { get }

    mutating func adjust()
}
class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class"
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += " Now 100% adjusted."
    }
}

var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"

    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}

var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
extension Int: ExampleProtocol {
    var simpleDEscription: String {
        return "The number \(self)"
    }

    mutating func adjust() {
        self += 42
    }
}

print(7.simpleDescription)

Error handling

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}
func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }

    return "Job sent"
}
do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch PrinterError.onFire {
    print("On fire")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError)")
} catch {
    print(error)
}
let printerSuccess = try? send(job: 1884, toPrinter: "Max")
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true

    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}

if fridgeContains("banana") {
    print("Found a banana")
}

print(fridgeIsOpen)

// Returns false

Generics


// Say you have
func swapInts(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}

// Which can then be changed to
func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}
var a: Int = 5
var b: Int = 10
swapValues(&a, &b)   // unambiguously Int

var first = "hello"
var second = "world"
swapValues(&first, &second)

print(first)   // "world"
print(second)