Day 14 - 100 Days of Swift

5 minute read

Second Review Day

Day 14 is the second review day. You review functions. You review optionals and optional chaining. And you review enums, structs and classes.

You can encapsulate some reusable piece of code in a function. They start with the func keyword, you can define parameters to be passed into a function inside of the () and a value to be returned after ->:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func getLastName(_ firstName: String) -> String {
        switch firstName {
        case "Lindsey", "Maebe":
            return "Fünke"
        case "Michael", "George Michael", "George Sr.", "Lucille",
             "Buster", "Gob":
            return "Bluth"
        default:
            return "Unknown"
        }
}

print("Michael is a \(getLastName("Michael"))")
// "Michael is a Bluth"
print("Maebe is a \(getLastName("Maebe"))")
// "Maebe is a Fünke"
print("Steve is a \(getLastName("Steve"))")
// "Steve is a Unknown"

Optionals let you communicate that this will either be nothing, nil in Swift, or something of a certain type. But to use them you have to unwrap them first:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// If we edit this function to return an optional String,
// we can return nil for names we don't know
func getLastName(_ firstName: String) -> String? {
        switch firstName {
        case "Lindsey", "Maebe":
            return "Fünke"
        case "Michael", "George Michael", "George Sr.", "Lucille",
             "Buster", "Gob":
            return "Bluth"
        default:
            return nil
        }
}

// In the sayHi function, we can unwrap the optional we get back
// and do different things based on the result
func sayHi(to firstName: String) {
    if let lastName = getLastName(firstName) {
        print("Hello \(firstName) \(lastName)!")
    } else {
        print("Hi \(firstName). I don't know your last name.")
    }
}

sayHi(to: "Michael") // "Hello Michael Bluth!"
sayHi(to: "Steve") // "Hi Steve. I don't know your last name."

If you have an optional object and you need to access one of its properties or methods, you can access it if it exists with optional chaining. You can also provide a default value with the nil coalescing operator:

1
2
3
4
5
6
7
8
let lastName = getLastName("Gob")
print(lastName?.count)
// Optional(5)

// We can provide a default value to unwrap it
// with the nil coalescing operator
print(lastName?.count ?? 0)
// 5

Enumerations are one custom type you can make in Swift and are defined with the enum keyword. At their most basic, they are a limited set of options, and they can have associated values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum Bluth {
    case michael
    case georgeMichael
    case georgeSr
    case lucille
    case lindsey
    case gob
    case buster
    case maebe
}

// Our getLastName function can be rewritten to use this enum
// and it will only accept names that we know.
func getLastName(_ firstName: Bluth) -> String {
    switch firstName {
    case .lindsey, .maebe:
        return "Fünke"
    default:
        return "Bluth"
    }
}

print(getLastName(.michael)) // "Bluth"

Structures are another custom type you can make in Swift and are defined with the struct keyword. They are a way to bundle up related values into one object. They are passed by value, and they get memberwise initializers for free:

1
2
3
4
5
6
7
8
9
10
11
12
struct Person {
    var firstName: String
    var lastName: String

    func introduce() {
        print("Hi, my name is \(firstName) \(lastName)")
    }
}

// We get this initializer for free
let tobias = Person(firstName: "Tobias", lastName: "Fünke")
tobias.introduce() // "Hi, my name is Tobias Fünke"

Classes are yet another custom type you can make in Swift and are defined with the class keyword. They are like structs in a lot of ways, but they don’t get initializers for free, they can inherit from other classes, and they are passed by reference:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Person {
    var firstName: String
    var lastName: String
    var profession: String

    // Since it is a class now, we have to add our own initializer
    init(firstName: String, lastName: String, profession: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.profession = profession
    }

    func introduce() {
        var string = "Hi, my name is \(firstName) \(lastName)"
        string += " and I am a \(profession)"
        print(string)
    }
}

// We can inherit from Person with our Doctor class,
// get everything from the super class for free,
// and change what we want to
class Doctor: Person {
    init(firstName: String, lastName: String) {
        super.init(firstName: firstName,
                   lastName: lastName,
                   profession: "Doctor")
    }
}

let tobias = Doctor(firstName: "Tobias", lastName: "Fünke")
tobias.introduce()
// "Hi, my name is Tobias Fünke and I am a Doctor"

Reflections

One thing that stood out to me today was that when the raw value for enums are ints, they are faster behind the scenes. It makes sense once you think about it, because ints are a much smaller and less complex structures than strings. But it is not something I had ever really considered, because I have never been in a situation where it really makes much practical difference. Good to know though!