Introduction to Swift - Part 1
Published: 2021-05-02 21:51:55

*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.*

# Swift overview

Swift is a programming language developed by Apple to be used in application development for Apple platforms, as a replacement for (or in combination to) Objective-C, its previous recommended language. This way, and using Xcode, we can develop apps for iOS, iPad OS, MacOS, WatchOS and TVOS. Since 2015, Swift is also Open Source, being maintained not just by Apple, but by a open development community.

Swift is multi-paradigm, which means we can use it to develop object oriented code (OOP), functional programming, scripting, etc. Swift adapts to our preferred programming style, although the community has agreed on some best practices and a way to develop code following mainly an object oriented approach.

Swift is modern, which means it includes features from other modern languages, as we'll see during this course. What's more, it continues being added more features during the years.

Swift is simple to learn, but it can be as complex and go as low level as we need it to go.

During this course we'll learn the Swift syntax and usage examples. But not in depth. We'll learn the necessary syntax in order to start developing iOS application. However, it's important to note that the language can be much more complex than what we'll learn here. This course isn't intended to be comprehensive, but to bring the needed tools to start.

# Variables

Variables are present in most (if not all) programming languages, and are names (also called identifiers), which contain a value.
In Swift, variables are declared using the keyword `var`, followed by a `=` and the value it contains. For instance:

```swift
var name = "Fernando"
var street = "Rivadavia Av."
var catLegsCount = 4
```

Something that could be curious in these declarations is that we aren't specifying their type. That's because Swift includes a feature called **type inference**. Type inference let us declare variables without having to be explicit about their types, because Swift is "intelligent" enough to figure out that a `4` is an `Int`, and that `"Fernando"` is a `String`.

## We can be explicit

Of course, there are moments where it could be useful to be explicit about the data type of a variable. For instance, if we're talking about a price.

```swift
var price1 = 12
```

We could want to be explicit about the type because the price is a decimal value, and it's not an `Int`. In Swift, we can specify the data type using `: <DataType>`. For example:

```swift
var price2: Double = 2.50
```

## Constants

Let's suppose we have a variable with a value that we **know** it won't change. For example, a cat has four legs. If we want to define the number of legs a cat has, we can use a constant. In order to so, we'll replace `var` by `let`, transforming that variable into a constant. A constant is a variable to which we **can only associate a value once**. If we want to update its value afterwards, we'll get an error.

```swift
var leavesInATree = 10
leavesInATree = 22
leavesInATree = 25 // We won't get any errors if we modify this value.
let catLegsCount = 4

// The next line won't compile.
// catLegsCount = 5 
```

While naming a variable, it's important to be very explicit on its meaning. This means: **do not abbreviate**
 
`lbl` isn't a good variable name. `label` is.
`chCnt` isn't a good variable name. `charactersCount` is.
`i` isn't a good variable name. `index` is.
 
Every programming language has it's own "correct" way to write code with. We know it as **best practices**. In Swift, the best practice is to declare variables as constants, using `let`, and use `var` only when it's strictly needed. This will  save us from potential headaches, because we know that the variable we're using that was declared using `let`, hasn't been modified. For complex functions, this can be critical.

# Data Types

As we've seen in the last section, although it isn't necessary to be explicit about a variable's data type (because the compiler **infers** the data type), we can choose to be explicit and specify it anyway. In this section, we'll do it that way.
Swift comes with a set of predefined data types, and let us define our custom types, as we'll see in the following sections.

## Numeric Data Types

Swift defines the following main numeric data types:

* `Int`: Integer number.
* `Double`: A decimal number.
* `Float`: A decimal number, but with less capacity than a `Double`.

```swift
let legs: Int = 4
let price: Double = 30.50
let temperature: Float = 20.2
```

## Bool
`Bool` is a boolean value. It can either be `true` or `false`. Those are only two possibilities.

```swift
let enabled: Bool = true
let isReady: Bool = false
```

## String
`String` is a list of characters. It's used to define texts and words. For instance, a person name or a street name could be defined as `String`.

```swift
let name: String = "Fernando"
```

A curious fact about `String` values in Swift is that emojis are valid `String` values!
Why? Why not? 🤷

```swift
let fancyHelloWorld: String = "👋 🌎!"
```

# Print

Sometimes we need to print a value in the console. In Swift, we can do that using the `print()` function. For example, a "Hello, World!" program can be written like this:

```swift
print("Hello, World!")
```

If you execute this program using the button in the left bar, a message will appear in the console that is at the bottom of the screen.

# Comments

Comments can be single-line or multi-line.

## Single-liner comments

They begin with a `//` and only occupy one line.

```swift
let mensaje = "Hello, world!" // This is a message
```

## Multi-line comments

They begin with a `/*` and end with `*/`.

```swift
/* This line
 prints the
 Hello, World! message */
print(mensaje)
```

# Interpolation

How do we create a `String` from other variables? An easy way to do it is by concatenating the parts.

```swift
let firstName = "Fernando"
let lastName = "Ortiz"
let fullName = firstName + " " + lastName // Fernando Ortiz
```

The alternative, and the most common way to do it in Swift, is by using **interpolation**. A `String` interpolation consists in "embedding" variables inside a `String`. This is done by entering `\(<variable>)` inside the `String`. For instance:

```swift
let middleName = "Martín"
let experience = 7
let description = "My name is \(firstName) \(middleName) \(lastName) and I have \(experience) years of experience as an iOS developer."

print(description) // "My name is Fernando Martín Ortiz and I have 7 years of experience as an iOS developer."
```

Note that we're also introducing an `Int` in the middle of the `String` and we aren't getting problems. That's another advantage of `String` interpolation.

# Collections

Let's collections in Swift to the data types that contain other data types on them and that we can iterate over them on some way.

## Array

Also known as lists or vectors, they are data types that store values in a sequential way. What's interesting about Arrays in Swift is that you can't mix values of different types in an `Array`. The simplest way of defining an array is this:

```swift
let students = ["Federico", "Micaela", "Aldana", "Oscar"]
```

Note that we are just using `String` values. An `Array` with the values `["Fernando", 28, true]` won't be valid, unless the `Array` is of type `[Any]`. The `Any` type is a type that can be of "any" type of value, but there are certain disadvantages on using it. My advice here is that, for now at least, **AVOID USING IT**.

If we want to be explicit about the type, we can also do it:

```swift
let numbers1: [Int] = [1, 21, 129]
let numbers2: Array<Int> = [3, 122]
```

There are many ways of declaring an empty `Array`:

```swift
let numbersEmpty1: [Int] = [] // Preferred
let numbersEmpty2: Array<Int> = []
let numbersEmpty3 = [Int]()
let numbersEmpty4 = Array<Int>()
```

### Useful methods in `Array`

If we want to know the number of elements in an `Array`, we use the property `count`.

```swift
let numbers3 = [12, 31, 231, 23]
print("\(numbers3) has \(number3.count) elements")
```

Curious fact, `String` in Swift are actually sequences of characters, so they also have the `count` property defined on them. Actually, most of the things we explain in this section apply also for `String` values.

```swift
let name = "Fernando"
let nameLength = name.count
print("\(name) has \(nameLength) characters.")
```

If we want to get the first element in an `Array`, we'll use `.first!`, why we're using `!` will be explained later. For now, let's say we're using `!` to affirm that we know with **full confidence** that the first element in the `Array` will be there, because the `Array` is not empty. Note that if we do `.first!` and the `Array` is in fact empty, the program will stop executing and crash.

```swift
let names = ["Tamara", "Nicolás", "Francisco"]
let firstName = nombres.first! // "Tamara"
```

If we want to know if an `Array` is empty, we'll use the `isEmpty` property, that's of type `Bool`.

```swift
if numbersEmpty.isEmpty {
    print("It's empty!")
}
```

If we'd like to add an element to an `Array, we'll use the function `append`.

```swift
var namesToFill: [String] = []
namesToFill.append("Fernando")
namesToFill.append("Martín")
print("namesToFill has \(namesToFill.count) elements") // namesToFill has 2 elements
```

If we want to access an exact position in an `Array`, we'll use what's known as a `subscript`, and that's basically a number (the index) between square brackets. Indexes in a Swift `Array` start from 0. Note that if we want to get an element in a position that is undefined, the program will stop executing and crash.

```swift
let names2 = ["Aldana", "Iván", "Marcos", "Cristian"]
let thirdName = names2[2] // "Marcos"
// This will fail when executed.
// let quintoNombre = nombres2[4]
```

# Dictionaries

A Swift `Dictionary` is a container that associate identifiers to values. They are **key-value** structures where the key is generally a `String` and the value can be anything. In contrast to `Array`, where the `Any` type was generally discouraged, creating `Dictionary` objects where the key is `String` and the value is `Any` is perfectly normal and widely used.
The `Dictionary` type is similar to what a `Map` is in other programming languages.

```swift
let person: [String: Any] = [ // Note that when we define a dictionary with `Any` as its value type, it can be anything.
    "firstName": "Richard",
    "lastName": "Brown",
    "age": 27,
    "position": "Project Manager"
]

let family: [String: String] = [ // If we define the dictionary as [String:String], values can only be String.
    "mother": "Marge",
    "father": "Homer",
    "son": "Bart",
    "daughter": "Lisa",
    "baby": "Maggie",
    "dog": "Santa's Little Helper"
]

let emptyDictionary: [String: Any] = [:] // The `:` sign must be there, so the compiler won't confuse this with an empty `Array`.
```

The advantage of using key-value objects is that we can now access to each value by its key. In the Family example:

```swift
let mom = family["mother"]! // "Marge"
```

The `!` sign is necessary because we need to tell the compiler that there is an actual value associated to that key.

# For

The `for` loop is a very well known structure in general programming languages. In Swift it's a bit different. The `for` loop let us execute a code fragment a predefined number of times. In practice, the most common use case for it, is to iterate over a collection, such as an `Array` or a `Dictionary`.

```swift
// We'll use these values for the examples.
let names = ["Ayelén", "Lautaro", "Natalia", "Sergio", "Gerardo"]
let person: [String: Any] = [
    "firstName": "Richard",
    "lastName": "Brown",
    "age": 27,
    "position": "Project Manager"
]
```

## for-in - Array

To iterate over an `Array`, we can use a `for-in` loop. In this case, this will print:

```swift
 Name: Ayelén
 Name: Lautaro
 Name: Natalia
 Name: Sergio
 Name: Gerardo
```

```swift
for name in names {
    print("Name: \(name)")
}
```

## for-in - Dictionary

We can also iterate over dictionaries using the `for-in` loop. In this case, the syntax will be a bit different, because for each element in a `Dictionary` wi'll get a pair (`tuple` actually, as we'll see in a later section), with the corresponding key and value. Let's see an example. this will print:
 
```swift
 lastName : Gomez
 position : Project Manager
 firstName : Ricardo
 age : 27
```
 
Also note that a `Dictionary` doesn't preserve the fields order. In this example, `lastName` was printed third, regardless I defined it first.

```swift
for (key, value) in person {
    print("\(key) : \(value)")
}
```

## Ranges

An alternative way of using `for` loops is by define a `Range`. A `Range` defines a value interval.

- `(0 ...< 10)` for instance, defines a range from 0 to 10, not including 10.
- `(0 ... 10)` defines a range from 0 to 10, 10 included.

Will print:
 
```
 Number: 0
 Number: 1
 Number: 2
 Number: 3
 Number: 4
 Number: 5
 Number: 6
 Number: 7
 Number: 8
 Number: 9
 Number: 10
```

```swift
for number in 0 ... 10 {
    print("Number: \(number)")
}
```

We can use the `count` property in `Array`, to iterate over it in a more "traditional" way. Note that `for index in 0 ..< names.count` is very similar to something like `for var index = 0 ; index < names.count ; index++`, which is a syntax that isn't valid in Swift. Furthermore, `index++` isn't valid Swift code. An alternative to that is `index += 1`, but anyway, the most traditional way of using `for` isn't valid in Swift.

```swift
for index in 0 ..< names.count {
    print("Name: \(names[index])")
}
```

# While

The `while` loop is widely used in programming languages. However, if I have to be honest, in my almost 7 years as iOS developer, I don't remember having to ever use it. Most of the times, some sort of `for` is enough, and it's better also, because every time we use `whie`, we are in the risk of accidentally entering into an infinite loop.
In this section, I'll show you how to use `while`. But let me repeat, it's almost unnecessary to use it.
`while` is a keyword what let us define a condition and a block of code. The condition will be evaluated. It it's `true`, then the block of code will be executed`. This process will be repeated indefinitely, until the condition is evaluated as `false`.

This example will print:
 
```
 Name with while: Ayelén
 Name with while: Lautaro
 Name with while: Natalia
 Name with while: Sergio
 Name with while: Gerardo
```

```swift
var index = 0
while index < names.count {
    print("Name with while: \(names[index])")
    index += 1
}
```

# Functions
Functions are blocks of code that can receive a set of parameters, the **input**, and can return a value as a result, named **output**.
In Swift, functions are declared with the keyword `func`. The structure to declare a function is the following:

`func <functionName> (<input1>: <InputType1>, <input2>: <InputType2>, ..., <inputN>: <InputTypeN>) -> <OutputType> { <body> }`
 
Functions may or may not have an output, as previously said. However, if we declare that the function will return a value, we need to return it using the `return` keyword.

All of this can be a bit confusing but it's actually very intuitive. Let's see some examples:

```swift
func sum(x: Int, y: Int) -> Int {
    return x + y
}

func helloWorld() {
    print("Hello, World!")
}

func greet(name: String) {
    print("Hi, \(name)!")
}
```

The `sum` function receives two parameters, `x` and `y`, and returns the sum of both of them (it has an Output).
The `helloWorld` function doesn't have an input, neither an output. It only prints `"Hello, World!"` in the console.
The `greet` function receives an input `name` and doesn't return anything.

## Calling a function

Functions can be called (or "invoqued") by their name, and the names of their arguments explicitly. This is very important and it's different to other languages.

```swift
let sumResult = sum(x: 10, y: 14) // 24
helloWorld()
greet(name: "Fernando")
```

## Legibility

Now, if you're detail-oriented people, you probably noticed that `greet(name: "Fernando")` doesn't read so natural, to be honest. We can fix that. Don't you think it would be easier to read `greet(to: "Fernando")`? However, that would make things worse, because inside the function we'd have a variable `to`, with the value `"Fernando"`.
Actually, we aren't forced to choose between one way or the other. Swift differentiates between **internal names** and **external names** for function parameters. Let's do a modification here, with a new function `sayHello`.

```swift
func sayHello(to name: String) {
    print("Hello, \(name)!")
}

sayHello(to: "Fernando")
```

Much better! This new function `sayHello` receives a single argument with an internal name `name`, visible only inside the function body, and an external name `to`, visible from the outside of the function (when we call it). If we don't specify different names for the internal and external ones, both will be the same, such as in the first couple of examples.

Let's fix another thing. The function `sum` looks a bit weird. Instead of `sum(x: 10, y: 14)`, it would be more natural to say `sum(10, 14)` or `sum(10, to: 14)`, or something similar. Swift let us define empty external names using the keyword `_`. The low dash let us specify that we don't want an external name for that parameter.
Let's try to define a divide function that would look like `divide(8, by: 2)`.

```swift
func divide(_ x: Double, by y: Double) -> Double {
    return x / y
}
divide(8, by: 2) // 4
```

# Preconditions

A precondition is a condition that needs to be `true` so that a function is executed. If the precondition is `false`, then the function will return a different value or throws an error. In Swift, we express the preconditions using the keyword `guard`. We can read it as "Ensure this is true. Otherwise, do this instead".

```swift
func describeDivision(of x: Double, by y: Double) -> String {
    guard y != 0 else {
        return "Dividing by zero is not possible."
    }
    let result = x / y
    return "The result of dividing \(x) by \(y) is \(result)."
}

let description1 = describeDivision(of: 20, by: 5)
print(description1) // The result of dividing 20 by 5 is 4.

let description2 = describeDivision(of: 10, by: 0)
print(description2) // Dividing by zero is not possible.
```

# Best practices

Until here, the theory of functions syntax. **The following best practices section is entirely optional. Feel free to skip it or read it at the end of this course.** However, I will give you a piece of advice that I think is important.
Why do we write functions? There are many reasons, but it's important to understand that a function is an important tool that we have to build abstractions in our code. An abstraction is in some way, to adapt a concept to natural terms, so we can reason on it in a more intuitive way. Considering we write code to define a solution to a problem in a way that not only the computer will understand (which is the simplest part), but also other people will be able to read it, understand it, and feel confident modifying it and extending it, then abstractions will allow us to improve the readability of our code.
A good function needs to be understandable, clear, readable and modifiable. I'll list, next, a couple of best practices when writing functions.

## Short functions
A function shouldn't be longer than 10 or 15 lines of code, in general. If a function is longer than that, it's convenient to split it in smaller functions, and then compose those smaller functions into a bigger function. A single-line function isn't necessarily bad, if it bring clarity to the code.

## Readable functions
This is try not only to the function body, but also to the function invocation part. Code inside a function needs to be a well defined sequence of steps. The invocation of that function needs to be read naturally.

## Cohesive functions
A function must do one thing, and do it right. This is even more important than writing small functions. If a function is longer than 25 lines or code, but every line of code contribute to the same goal without adding cognitive load to the code reader, then there is no reason to modify it.

# Tuples

A tuple is the first custom data type we'll see during this course. Tuples are sets of data with a name and without any additional functionality (unlike classes, for instance). A tuple can be defined using the keyword `typealias`, or when it's needed.

```swift
typealias FullName = (firstName: String, lastName: String)
let name: FullName = (firstName: "Fernando", lastName: "Ortiz")
print("The first name is \(name.firstName) and the last name is \(name.lastName)") // The first name is Fernando and the last name is Ortiz
```

The most common use case for tuples is to let a function return more than a single value.

```swift
func divide(_ dividend: Int, by divisor: Int) -> (quotient: Int, remainder: Int) {
    // We assume we're not dividing by zero
    return (
        quotient: Int(dividend / divisor), // Integer value without the decimal values
        remainder: dividend % divisor // Modulo operator returns the remainder of a division
    )
}

let result = divide(13, by: 4)
print("Quotient: \(result.quotient) ;; Remainder: \(result.remainder)") // Quotient: 3 ;; Remainder: 1
```

## Destructuring

Destructuring is a way of split a tuple into its components. It's done by assigning a tuple to a set of variables in parenthesis, and separated by comma:

```swift
let (myQuotient, myRemainder) = divide(17, by: 3)
print("Quotient: \(miQuotient) ;; Remainder: \(miRemainder)") // Quotient: 5 ;; Remainder: 2

// We can also ignore any of those values with a low dash.
let (quotient, _) = divide(50, by: 4) // We are ignoring the remainder here
print("The result of dividing 50 by 4 is \(quotient)") // The result of dividing 50 by 4 is 12
```

## Anonymous components

A tuple can also have their components are anonymous values. Even when it's not recommendable, it's possible. In this case, we can access to its components using their index. For example: `tuple.0` for the first element, `tuple.1` for the second one, etc. The alternative is to use destructuring `let (first, second) = tuple`, which is preferrable.
Let's see a last example, generating intervals using a function that will return the lower and the upper extremes in a tuple:

```swift
func generateInterval(value: Int, amplitude: Int) -> (Int, Int) {
    let lower = valor - amplitud
    let upper = valor + amplitud
    return (lower, upper)
}

// Using destructuring - Preferrable.
let (lower, upper) = generateInterval(value: 10, amplitude: 2)
print("[\(lower);\(upper)]") // [8;12]

// Accessing by order to the anonymous components
let interval = generateInterval(value: 10, amplitude: 2)
print("[\(interval.0);\(interval.1)]") // [8;12]
```

# Optional data types

Let's suppose we want to describe a person. What attributes does a person have? We could say `firstName`, `lastName`, `identifier`, `age`, etc. All of them exist. A person always have a `firstName`. A person always have `lastName`. All of them are of type `String`. `String` is a **non-optional type**.
However, this isn't true for every attribute in a person. What about `middleName`? A person may not have `middleName`, it's **OPTIONAL**.
Optional data types in Swift let us design exactly that, values that may not be there, they may be `nil`. You might have already noticed that in all examples so far, we have used non-optional values, and only in specific places we have used `!` to note that we are sure that the value is not `nil`, like in `first!`, for `Array`.
Every data type have its optional counterpart, that is defined with the `?` suffix. So, for instance:

- `String?` is a `String` that might have a `String` value or `nil`.
- `Int?` is an `Int` that might be `nil`.
- `Bool?` is a `Bool` that might be `nil`.
- `[String]?` is an `Array` of `String` that might be `nil`.
- `(quotient: Int, remainder: Int)?` is a type composed by two `Int` values. The tuple might be `nil`. Its components, on the other hand, can't be `nil`.
- `(firstName: String, lastName: String?)` is a tuple composed by two `String` values. The first one (`firstName`) can't be `nil`. The second one might be `nil`. The `tuple` isn't optional.

```swift
typealias PersonData = (firstName: String, middleName: String?, lastName: String, age: Int)
let fernando: PersonData = (
    firstName: "Fernando",
    middleName: "Martín",
    lastName: "Ortiz",
    age: 29
)
let nicolas: PersonData = (
    firstName: "Nicolás",
    middleName: nil, // it's nil, it doesn't exist
    lastName: "Duarte",
    age: 29
)
```

## Force unwrap optionals

If we're sure that an optional value isn't nil, we can force it to convert it into its non-optional counterpart. To do that, we can add a `!` at the end of the variable name, and it will become non-optional.
Let's see some examples. I have to say, however, that this is a **VERY BAD PRACTICE**. If we add a `!` to get the non-optional counterpart of a value that is actually `nil`, the app will crash because of a fatal error.

```swift
print("Fernando's middle name is \(fernando.middleName)")
// Fernando's middle name is Optional("Martín")
// We have to get the non optional value of Fernando's middle name.

print("Fernando's middle name is \(fernando.middleName!)")
// Fernando's middle name is Martín
```

## if-let

This is one of the "correct" ways of handling optional values. Inside an `if`, we can include an assignment from an optional that will be executed just in the case that the value isn't `nil`. For example:

```swift
if let middleName = fernando.middleName {
    // Inside here, middleName isn't optional
    print("Fernando's middle name is \(middleName)") // Fernando's middle name is Martín
}

if let middleName = nicolas.middleName {
    // This won't be executed
    print("Nicolas' middle name is \(middleName)")
} else {
    print("Nicolas doesn't have a middle name")
}
```

## guard-let

`guard` is the opposite of `if`. It will execute its body only in the case that the condition isn't met and it's used to express a precondition. `guard` can also be used to unwrap optionals in a safe way, such as `if-let`.

```swift
func getMiddleName(of person: PersonData) -> String {
    guard let middleName = person.middleName else {
        return "\(person.firstName) doesn't have middle name"
    }
    return "\(person.firstName)'s middle name is \(middleName)"
}

print(getMiddleName(for: fernando)) // Fernando's middle name is Martín
print(getMiddleName(of: nicolas)) // Nicolás doesn't have middle name
```

## Implicitly unwrapped optionals

An implicitly unwrapped optional is an optional data type, that we can assign `nil` to, obviously, but that when we need to use it, it will be considered non optional for most of the cases.
Of course, the app will crash in case the value is actually `nil` and we try to use it as a non-nil value. And, of course, this is a bad practice. However, this is used a lot in iOS, especially in frameworks that are written in Objective-C (Swift's predecessor, in Apple's platforms).

```swift
let nonNilMiddleName: String! = "Marcos"
let nilMiddleName: String! = nil

func printMiddleName(_ middleName: String) {
    print(middleName)
}

printMiddleName(nonNilMiddleName) // Marcos
// printMiddleName(nilMiddleName) // CRASH!
```

## Optional chaining

Let's suppose we have an optional tuple.

```swift
var optionalPerson: PersonData?
optionalPerson = (
    firstName: "Nicolás",
    middleName: nil,
    lastName: "Duarte",
    age: 29
)
```

If in this case, that `optionalPerson` is optional, we need to access any of its components, being them attributes or methods (such as in classes, as we'll see later), we can use a feature called **Optional chaining**.

Optional chaining lets us access members of the optional object using `?.` instead of `.`.

```swift
let firstName = optionalPerson?.firstName // type: String?, because, although `firstName` in `PersonData` is `String`, we don't know if `optionalPerson` exists, or if it's `nil`.
```

# Exercises

1. Define a tuple that describes an address, with fields like `city`, `state`, `zipCode`, `country`, etc. Feel free to experiment and use dictionaries, optional values, and everything we've learned in this lesson.
2. Inside the address, define some optional components, like `floorNumber` and `apartment`.
3. Create three addresses as constants.
4. Write a function that gets an address and prints it as a well formatted `String`. Use interpolation.
5. Write a function that gets an `Array` of addresses and returns an `String` with `"floor: \(floor) ; apt: \(apartment)"` ONLY for those addresses with non-nil floor and apartment.