Introduction to Swift - Part 2
Published: 2021-05-03 00:12:29

*Note: This article is part of the course* **Introduction to iOS using UIKit** *I've given many times in the past. The course was originally in Spanish, and I decided to release it in English so more people can read it and hopefully it will help them.*

# Object oriented programming

In every program we've got two well defined concepts: Data and behavior.
A tuple with five fields that define an address is data. 
A function that takes that tuple and returns a `String` with its description is behavior.

Everything we've done so far in the first part of Swift introduction has been defining data on one side and the behavior that acts on that data in another, separated side, which has worked well for us so far. However, the most frequent way of organizing code in modern languages, such as Swift, is integrating data and behavior in cohesive structures such as the ones we'll see today, when we'll review **Object oriented programming** (OOP).

This will bring us some advantages. Especially, the ability to abstract. A class will have behavior that can be used from the outside. It means, we'll know **what** that class can do. However, the **how** is private to that class. That's known as **encapsulation**.

# Class
 
A class is one of those structures that integrate data and behavior as a unit. Data inside a class is called **attributes**. Behavior inside a class is modeled as a set of functions known as **methods**.
In Swift, a class is defined using the keyword `class` followed by its name. Attributes will be variables, and methods will be functions.
Let's see an example of a class hierarchy and then we'll explain in detail what just has happened:

```swift
class Animal {
    var name: String
    var walks: [String]
    
    var animalType: String { return "" }
    
    init(name: String) {
        self.name = name
        self.walks = []
    }
    
    func emitSound() {}
    
    func walk(to place: String) {
        self.walks.append(place)
    }
}

class Dog: Animal {
    override var animalType: String { return "Dog" }
    override func emitSound() {
        print("\(name): Woof!")
    }
}

class Cat: Animal {
    override var animalType: String { return "Cat" }
    override func emitSound() {
        print("\(name): Meow!")
    }
}

class Person {
    private var name: String
    private var pets: [Animal]
    
    init(name: String, pets: [Animal]) {
        self.name = name
        self.pets = pets
    }
    
    func getHome() {
        print("\(name): gets home")
        for pet in pets {
            pet.emitSound()
        }
    }
}

let romina = Person(
    name: "Romina",
    pets: [
        Cat(name: "Fausto"),
        Dog(name: "Lupi")
    ]
)

romina.getHome()
```

It will print:

``` 
Romina: gets home
Fausto: Meow!
Lupi: Woof!
```
 
## Inheritance
 
Ok, what just has happened here? First, we've defined a base class, called `Animal`. It's called base class because it's the class that will define the base of the class hierarchy, it will define the common set of data and behavior for the other classes in the hierarchy. 

What can an animal do? (behavior). In this case, it can go for a walk, and it can emite a sound. What sound? We don't know that. The sound an animal emits will be define in its subclasses. That's called **inheritance**. An animal can either be a `Cat` or a `Dog`, in this example. So that, a `Cat` can say `Meow`, and define its type as `"Cat"`, but it **inherits** all the behavior defined in its base class.

What do we know of an animal? (data). In this case, its name and the walks it has taken.

And `animalType`? It's a computed variable, or **getter**. A getter is essentially a function that returns a value. From the outside of a class, it's seen and it's used a variable that we can't change its value. It takes part of the behavior of a class.

## Encapsulation

A class only exposes part of its data and behavior that we can use from its outside. What is internal to that class is defined with the keyword `private`. What is `private` can only be accessed from inside that class. What isn't marked as `private` is accessible from anywhere else in the app and it's known as the **public interface** for that class.

## Init

The `init` method is an especial method of a class, that has the responsibility of define how an object of that class will be created. In this example, the `Person` class will have an `init` defined like this:
 
```swift
 init(name: String, pets: [Animal]) {
     self.name = name
     self.pets = pets
 }
```

`self` in this case refers to the same object on which we're working.
 
`init` doesn't have to be written in order to be used. Consider this example:
 
```swift
 let romina = Persona(
     name: "Romina",
     pets: [
         Cat(name: "Fausto"),
         Dog(name: "Lupi")
     ]
 )
```
 
## Polymorphism
 
Functionality defined in a base class can be redefined with the keyword `override` in the subclasses. So for instance, `emitSound` doesn't have an actual functionality in the base class. But it does in its subclasses. 
Subclasses "override" behavior from the base class. For instance:
 
```swift
 override func emitSound() {
     print("\(name): Meow!")
 }
```
 
What's interesting is that we can refer to dog and cats as `Animal` in the `pets` `Array`. Whenever we call `pet.emitSound()` we aren't sure if the pet is a cat or a dog, that's determined in runtime, where each object will execute the behavior defined by its specific subclass.

# Protocol

A `class` answers the question **What is this?**

- What is this? A Cat

- What is this? A Dog

- What is this? A Person

A `protocol`, on the other hand, answers the question **What can this do?**. 
On its simplest form, a protocol (also called **interface** in other programming languages such as Java), is a set of method signatures under a name. Any class can **implement** a protocol. If a class implements a protocol, then it must implement all the methods defined on it. Otherwise, the code won't compile.

Let's see an example:

```swift
protocol EmitsSound {
    func emitSound()
}

class Animal {}

class Dog: Animal, EmitsSound {
    func emitSound() {
        print("Woof!")
    }
}

class Cat: Animal, EmitsSound {
    func emitSound() {
        print("Meow!")
    }
}

class Ringer: EmitsSound {
    func emitSound() {
        print("Ring!")
    }
}
```

Note here that:

1. A class can only inherit from a single class. However, it can implement as many protocols as we need. If a class inherits from another class and also implements protocols, what is written at the right of `:` must be first the base class, and then the protocols to be implemented.
2. A Dog, a Cat, and a Ringer are now of the same type. All of them are of the type `EmitsSound`. And as such, we can use them in an `Array`, for instance:

```swift
class SoundEffects {
    let sounds: [EmitsSound]
    
    init(sounds: [EmitsSound]) {
        self.sounds = sounds
    }
    
    func play() {
        for sound in sounds {
            sound.emitSound()
        }
    }
}

let getHomeSounds = SoundEffects(sounds: [Ringer(), Cat(), Dog()])
getHomeSounds.play()
// Ring!
// Meow!
// Woof!
```

As we've implemented `EmitsSound` for all our objects, regardless of their class, we can now use them to emit sounds.  In this example, we use this to implement sound effects. In practice, this same principle is used a lot. Especially when all we need from other object is what it can do, regardless of what type it really is.

# Struct

A `struct` is very similar to a `class`. Let's see some differences:
 
A `class`:
- Has attributes and methods.
- The attributes and methods it has may be private.
- It can implement protocols.
- It must implement a `init` method to initialize its attributes when it's instantiated.
- It can inherit from other classes.
 
A `struct`:
- Has attributes and methods.
- The attributes and methods it has may be private.
- It can implement protocols.
- **It is not necessary** that it implements an `init` method to initialize its attributes in general. An `init` method is automatically generated.
- **It can't inherit** from another `class` or `struct`.

A `class` also can't inherit from a `struct`.

```swift
protocol HasDescription {
    func getDescription() -> String
}

struct Address: HasDescription {
    let street: String
    let number: String
    let apartment: String
    let floor: String
    let city: City
    
    func getDescription() -> String {
        return "\(street) \(number) - Apt. \(floor)-\(apartment) - \(city.getDescription())"
    }
}

struct City: HasDescription {
    let name: String
    let state: State
    
    func getDescription() -> String {
        return "\(name), \(state.getDescription())"
    }
}

struct State: HasDescription {
    let name: String
    let country: String
    
    func getDescription() -> String {
        return "\(name), \(country)"
    }
}

let someAddress = Address(
    street: "Some St.",
    number: "18",
    apartment: "A",
    floor: "2",
    city: City(
        name: "Palermo",
        state: State(
            name: "Buenos Aires",
            country: "Argentina"
        )
    )
)

print(someAddress.getDescription()) // Some St. 18 - Apt. 2-A - Palermo, Buenos Aires, Argentina
```

In practice, the `struct` objects are used to model small chunks of information. They can serve us to describe an address, credit card data, etc.

# Enum

A `struct` or `class` let us design based on "and". For example, `struct Person` has `firstName` and `age`, and `email` as its members.
An `enum`, on the other hand, allow us design based on "or". For instance, `enum State` has `florida`, `california` or `texas` as its members.

Unlike `enum` in languages as C, `enum` in Swift can get to be really complex. Anyway, we'll use basic functionality on these examples.

Let's see a basic example

```swift
class User {
    var name: String
    var credentialType: CredentialType
    
    init(
        name: String,
        credentialType: CredentialType
    ) {
        self.name = name
        self.credentialType = credentialType
    }
}

// enum CredentialType defines how a user created their account.
enum CredentialType {
    case email
    case facebook
    case apple
    case google
}
```

With the `CredentialType` enum, we can say that for instance, a user registered using `email`, or `facebook`, or `apple`, or `google`. It becomes impossible that a user has registered using more than one of these values, and this code makes it impossible.
 
## Switch using enum

Ok, let's try to use `CredentialType` for our case. The most typical way of using an `enum` in our logic, is by using the keyword `switch`.

```swift
func isSocialNetworkUser(_ user: User) -> Bool {
    switch user.credentialType {
    case .email:
        return false
    case .facebook, .apple, .google:
        return true
    }
}
```

Also note that we could have defined that function inside `User`. And that we're combining several options inside the same `case`, but they could have also been written with a `case` for each option (facebook, apple, google).
 
## Methods and attributes in `enum`

An `enum` may have methods and attributes, like a class. The most common pattern for doing that is including a `switch self` clause inside the method or attribute. Let's see an example:

```swift
enum Country {
    case argentina, germany, england, usa
    
    // We'll create a computed property. A `getter`.
    // Note that this is the same as write
    //
    // func isEuropean() -> Bool { ... }
    //
    // But when we call it, we won't use parenthesis, exactly as if we would be working with a property.
    var isEuropean: Bool {
        // switch self is VERY common in these cases
        switch self {
        // If we're calling this property for cases `germany` or `england`.
        // then we'll return `true`
        case .germany, .england:
            return true
        // However, if we're doing this from any other case of this enum, then
        // we'll return false
        default:
            return false
        }
    }
    
    // Similar to the previous case but with a String
    var nombre: String {
        switch self {
        case .argentina:
            return "Argentina"
        case .germany:
            return "Germany"
        case .england:
            return "England"
        case .usa:
            return "United States of America"
        }
    }
}

func describe(country: Country) {
    if country.isEuropean {
        print("\(country.name) is European")
    } else {
        print("\(country.name) is NOT European")
    }
}

describe(country: Country.germany) // Germany is European
```

Another curious thing about `enum` is that when we need to use one of them, like in the case of `describe`, there is not necessary to specify the type. For instance, `Country.germany` could have been just `.germany`. (note: This is not specific of enums, but it's notorious and easy to recognize for these)

```swift
describe(country: .argentina) // It's the same to say describe(country: Country.argentina) and it feels more natural.
```

## rawValue

There is an alternative way to define the country name, as we've seen in the previous example, and it's by using a `rawValue`. Each `enum` can have a single `rawValue` for each of its cases, and all of them must be of the same type.

`rawValue` let us not only get a value associated to each `case`, but also create a case for the enum based on that associated value.

```swift
enum State: String {
    case florida = "Florida"
    case newYork = "New York"
    case california = "California"
}

let state1 = State.florida
print("State 1 is \(state1.rawValue)")
// We use the rawValue to get the value for that state
// this will print "State 1 is Florida"

let state2 = State(rawValue: "California")
// Here, we're doing the inverse process
// instead of getting the rawValue of a state,
// we create the state based on its rawValue
//
// We can make mistakes, so there is no guarantee that it exists
// a state with that name, the type of state2
// is `State?`, an optional State.

if let state = state2 {
    print("We could get state2 and its \(state.rawValue)")
} else {
    print("We couldn't get state2, and it's nil")
}
```

## Associated value

This is the last case we'll see here about `enum`. I'd like to explain them here because they are something very used in Swift, although it's not inside the scope of what's needed to continue with the course. I mean, it's not necessary to grasp the following lessons. However, knowing this can be very useful in your careers.

Each enum case can have associated values. For example, let's imagine a `Cookie` enum. Each cookie might be represented using a `enum`.

```swift
enum Cookie {
    // Each case may have any number of associated values. Each one may optionally have
    // an associated name and may be of the type we need.
    case chocChip(dough: DoughType)
    case stuffed(stuff: StuffType, dough: DoughType)
    case fortune
    
    var cookieDescription: String {
        switch self {
        // We can get associated values using the keyword `let`.
        case .chocChip(let dough):
            return "Chocolate chips cookie with dough of type \(dough.doughDescription)"
        case .stuffed(let stuff, let dough):
            return "Stuffed cookie of \(stuff.stuffDescription), flavor \(dough.doughDescription)"
        case .fortune:
            return "Fortune Cookie"
        }
    }
}

enum StuffType {
    case vanilla, strawberry
    
    var stuffDescription: String {
        switch self {
        case .vanilla: return "vanilla"
        case .strawberry: return "strawberry"
        }
    }
}

enum DoughType {
    case vanilla, chocolate
    
    var doughDescription: String {
        switch self {
        case .vanilla: return "vanilla"
        case .chocolate: return "chocolate"
        }
    }
}

struct CookieSet {
    let cookies: [Cookie]
    
    func describe() {
        print("-")
        print("Cookies:")
        for cookie in cookies {
            print(cookie.cookieDescription)
        }
    }
}

let cookies = CookieSet(cookies: [
    .fortune,
    .stuffed(relleno: .frambuesa, saborDeMasa: .chocolate),
    .stuffed(relleno: .frambuesa, saborDeMasa: .chocolate),
    .stuffed(relleno: .frambuesa, saborDeMasa: .chocolate),
    .chocChip(saborDeMasa: .chocolate),
    .chocChip(saborDeMasa: .vanilla),
    .chocChip(saborDeMasa: .vanilla),
    .fortune,
    .fortune,
    .chocChip(saborDeMasa: .chocolate)
])

cookies.describe()
//    -
//    Cookies:
//    Fortune
//    Stuffed cookie of strawberry, flavor chocolate
//    Stuffed cookie of strawberry, flavor chocolate
//    Stuffed cookie of strawberry, flavor chocolate
//    Chocolate chips cookie with dough of type chocolate
//    Chocolate chips cookie with dough of type vainilla
//    Chocolate chips cookie with dough of type vainilla
//    Fortune
//    Fortune
//    Chocolate chips cookie with dough of type chocolate
```

# Closures
 
`closure` are also called anonymous functions. We'll see first the concept of function as a data type.

## Functions as a data type
 
In Swift, Functions are a type of data, such as `Int`, `Double`, `Bool`, a `class`, `struct`, or `enum. This implies that we could take a function a send it as an argument for another function, or make a function return another function as a result. Or have a `struct` where one of its attributes is a function. This is actually a bit weird when we first see it, but it's actually pretty common in modern languages.

To convert a function into its related data type, we need to pay attention to the types of its input and output. So, the `sum` function:
 
```swift
func sum(x: Int, y: Int) -> Int { ... }
```
 
Is of type `(Int, Int) -> Int` because it receives to `Int` and returns an `Int`. Let's see other examples:
 
```swift
func sum(x: Int, y: Int) -> Int { ... } // (Int, Int) -> Int
func describe(_ person: Persona) { ... } // (Persona) -> Void
func printCurrentTime() { ... } // () -> Void
func getCurrentDate() -> String { ... } // () -> String
```

And, as we said, a Function data type can be used as an argument for other functions:

```swift
struct Person {
    let id: Int
    let name: String
    let role: PersonRole?
    let age: Int
}

enum PersonRole {
    case developer, projectManager, teacher, doctor
}

func printAdults(_ people: [Person]) {
    for person in people {
        if person.age >= 18 {
            print("\(person.id) - \(person.name)")
        }
    }
}

let people = [
    Person(id: 1, name: "Franco", role: .teacher, age: 34),
    Person(id: 2, name: "Gimena", role: .projectManager, age: 24),
    Person(id: 3, name: "Gonzalo", role: .teacher, age: 26),
    Person(id: 4, name: "Noelia", role: .developer, age: 29),
    Person(id: 5, name: "Pablo", role: nil, age: 15),
    Person(id: 6, name: "Lourdes", role: .doctor, age: 29),
]

printAdults(people)
// This will work correctly
//
// However, we don't have a way to provide 'flexibility' to the algorithm. I mean,
// inside the function printAdults we filter by age, and then we print the result.
// If we would like to change the filter criteria, we'd need to write a completely new function.
// Let's convert this function in something more flexible:

// We are "injecting" a function into another function as an argument
func print(_ people: [Person], who matchesCriteria: (Person) -> Bool) {
    for person in people {
        if matchesCriteria(person) {
            print("\(person.id) - \(person.name)")
        }
    }
}

func isAdult(_ person: Person) -> Bool {
    return person.age >= 18
}

print(people, who: isAdult) // Exactly the same result. We're sending the function as an argument in this case.
```

## Anonymous functions

Now it's time, with this introduction we can start talking about anonymous functions. An anonymous function, or **closure** is a function that lacks a name. It's as simple as it sounds. And the best context for using them is to send them to other functions. For example, in this case, I could have decided that it didn't make sense to define a new function just to determine is a person is an adult.

Let's define it as an anonymous function:

```swift
print("Printing adult people using a closure (1):")
imprimir(
    people,
    who: { (person: Person) -> Bool in
        return person.age >= 18
    }
)
```

This is a closure, and there are a couple of different ways to define one, here are more examples: https://fuckingclosuresyntax.com/

For now, let's see the transformation step by step
 
We have this function:
 
```swift
 func isAdult(_ person: Person) -> Bool {
     return person.age >= 18
 }
```
 
Step 1: We remove the `func` keyword and its name:
 
```swift
 (_ person: Person) -> Bool {
     return person.age >= 18
 }
```
 
Step 2: In case its parameters have a different internal and external names, we will only use its internal ones:
 
```swift
 (person: Person) -> Bool {
     return person.age >= 18
 }
```
 
Step 3: We move the curly bracket to the beginning of the definition, and in its place, we will put `in`:
 
```swift
 { (person: Person) -> Bool in
     return person.age >= 18
 }
```
 
Perfect! This is enough to correctly define a closure. We can use this closure as it is right now, but I'll show you some extra steps we can take to shorten this definition even more. Again, this is completely optional:

Extra step 1: We remove the data type, as the compile can infer it based on the context for most situations:
 
```swift
 { (person) in
     return person.age >= 18
 }
```
 
Extra step 2: We remove the parenthesis around the arguments:
 
```swift
 { person in
     return person.age >= 18
 }
```
 
Extra step 3: If the closure has a single sentence, we can remove the `return` keyword.
 
```swift
 { person in person.age >= 18 }
```
 
Extra step 4: Instead of using the arguments names (in this case `person`), we can refer to the arguments by its order. For instance, instead of `person`, we can use `$0`. If we had two arguments, the first of them would be `$0` and the second one `$1`. If we had four, they would be `$0`, `$1`, `$2`, `$3`, and so forth:
 
```swift
 { $0.age >= 18 }
```
 
And that's the minimum expression for this closure
 
```swift
print("Adult people who are teachers")
print(people, who: { $0.age >= 18 && $0.role == .teacher })
```

If we had a closure as the last argument for a function, we can move it outside the function call. Let's make it clearer with an example:

```swift
print("Adult people who are teachers (2)")
print(people) { $0.age >= 18 && $0.role == .teacher } // Exactly the same as the previous example
```

## Map, filter, sorted and forEach
There are some functions inside the Swift standard library that take other functions as their arguments, especially when working with `Array`.

- `map` is a function that let us transform each element of an array into another element by passing it a function that actually performs the transformation.
- `filter` is a function that let us filter an array by passing a function that returns `true` in case the element should be included into the result array, or `false` otherwise.
- `sorted` is a function that let us sort an array. It works similarly to the `filter` function, we will get two elements and we'll return `true` in case the first element should be first in the result array and which second.
- `forEach`, let us iterate over an array, performing anything based on the origin array. This function won't return a new array, unlike `map`, `filter` and `sorted`.
 
It's important to note that all these functions (except `forEach`) return a new array. They don't modify the origin array.

```swift
let adultPeople = people.filter { $0.age >= 18 }
let names = people.map { $0.name }
let peopleSortedByAge = people.sorted { $0.age < $1.age }

// We can also "chain" these functions, because each of them will return a new `Array`.

print("Chained functions:")

people
    .filter { $0.age >= 18 } // We'll only take into account adult people
    .sorted { $0.age < $1.age } // We'll then sort them by age
    .map { $0.name } // And we'll extract their names
    .forEach { print($0) } // Finally, we'll print their names
// Gimena
// Gonzalo
// Noelia
// Lourdes
// Franco
```

# Extensions

Extensions let us *extend* an existent type to add new functionality to it. This functionality may be computed properties or methods.
 
Let's consider a simple example:

```swift
struct Address {
    let street: String
    let number: String
    let city: String
}

extension Address {
    var addressDescription: String {
        return "\(street) \(number) - \(city)"
    }
}

let address = Address(street: "Rivadavia", number: "185", city: "Palermo")
print(address.addressDescription) // Rivadavia 185 - Palermo
```

And that's exactly the same as this:
 
```swift
 struct Address {
     let street: String
     let number: String
     let city: String
 
     var addressDescription: String {
         return "\(street) \(number) - \(city)"
     }
 }
```
 
An interesting use case for `extension` is to extend native types like `Int`, `Double` or `String`.

```swift
extension Int {
    func isBigger(than anotherNumber: Int) -> Bool {
        return self > anotherNumber
    }
}

if 10.isBigger(than: 5) {
    print("10 is bigger than 5")
}
```

# Typealias
 
`typealias` are basically that, alias for types. This means that we can refer to an existing type with a new name. For example, let's suppose we're coding an app that handles users, where each user has an identifier. This user ID is an `Int`. However, a `typealias` can be even better, so we are sure we're talking about the identifier of a user.

Remember that a good code is easy to extend and easy to understand.

```swift
typealias UserID = Int

struct User {
    let id: UserID
    let name: String
}

typealias BuildingID = Int
typealias Address = (street: String, number: String, city: String, state: String, country: String)

struct Building {
    let id: BuildingID
    let ownerId: UserID
    let address: Address
}

let office = Building(
    id: 1,
    ownerId: 10,
    address: (
        street: "Rivadavia",
        number: "18451 PB Torre 2",
        city: "Moron",
        state: "Buenos Aires",
        country: "Argentina"
    )
)
```

Note that we could have done exactly the same without `typealias`. In general (except for advanced use cases we won't cover during this course), `typealias` bring clarity to the code, making our intention even more evident and clear to the other developers.

# Exercises

We want to develop an application for organizing trips. The idea for the app is that we will have a list of possible destinations. The user can select their favorite destinations and save them, so they could then see those saved destinations in another list.

This exercise consists in developing the data structures needed to support the application use cases. It's required that, least:
 
- Use classes, structs or enums to the entities `User`, `Address`, `Place`, `Landmark`.
- Allow getting the favorites `Place` and `Landmark` objects for a certain user.
 
Use your creativity and try to get the exercise as complete as possible.