100 Days of SwiftUI - Day 3

100 Days of SwiftUI - Day 3

Hello, and welcome to day 3 of the 100 Days of SwiftUI blog series where I'm following the hackingwithswift.com course by the same name. If you've skipped the first couple of posts, you can go back over all the posts here: https://blog.grayw.co.uk/tag/100daysofswiftui/

Workspace

My workspace for today will be Xcode on the MacBook again. However, I think it's important to note that while I have opted to use the MacBook once again, I feel that I enjoyed my time in the Playgrounds editor far more.

It was an incredibly clean, simple interface, and just felt like a far smoother experience than Xcode. I’m certain this opinion will change once I get past the very basics, though.

How to store ordered data in arrays

The first part of today will be taking a look at how to store data in arrays.

Starting with the basics, we create and array much like any other variable or constant. Except this time, we wrap them in [] after the assignment operator =.

var goodGuys = ["Two Face", "The Penguin", "Joker"]
var badGuys = ["Batman", "Robin"]

let someLostNumbers = [4, 8, 15, 16, 23, 42]

When referencing a value in an array, we query its index. That is its location within the array. However, we start our count at 0, rather than 1.

print(goodGuys[2])
print(someLostNumbers[4])

We can add data to a variable array by using .append()

badGuys.Append("Joker?")

We need to keep in mind that we can't add any data we want to an array. If it contains strings, we can't add an integer. Type safety wants to ensure that an array of strings is an array of strings. An array of integers, remains as integers.

Attempting to add a string to an array of integers will result in an error. Xcode will even try to give you a hint at what sort of value you should be adding, too.

If you do append an erroneus item to an array, the first part of the error does seem to be a little cryptic compared to some others I've seen in the past.

error: Day_003.playground:64:17: error: no exact matches in call to instance method 'append'
someLostNumbers.append("Something")
                ^

Day_003.playground:64:17: note: found candidate with type '(__owned Int) -> ()'
someLostNumbers.append("Something")
                ^

Day_003.playground:64:17: note: found candidate with type '(__owned String) -> ()'
someLostNumbers.append("Something")
                ^

But, as you work through it, you can kind of see what it's getting at.

What happens if you want to create an array, but don't have anything to put in it yet? Well, this can be done by creating an empty specialised array.

var scores = Array<Int>()

// A shorter way
var scores = [Int]()

If you need to know how many items are in an array, we can use .count.

someLostNumbers.count

There are various ways to remove items from an array. The first, removing one item at a time by its index. Another way it to remove all of the items. By typing .remove in Xcode, I can also see there are many more options.

someLostNumbers.remove(at: 2)
someLostNumbers.removeAll()

How about checking to see if an array contains something? This can be done using .contains.

someLostNumbers.contains(15) // Returns true

Sorting an array can be done by using .sorted

someLostNumbers.sorted()

Finally, for, there is also reversing an array. This can be done using .reversed().

let foundNumbers = someLostNumbers.reversed()
print(foundNumbers)

Doing this doesn't actually return what I'd have expected, but rather something called a ReversedCollection that makes a reference back to the original

ReversedCollection<Array<Int>>(_base: [4, 8, 15, 16, 23, 42])

I don't fully understand how or why this is happening, and will need to look in to this further. It's not the behaviour I expected. Initially, I can see how it could be seen as more optimal as it's not having to do the work of changing it all and saving it again.

  • Arrays Review Test: 6/6

Storing and Finding Data in Dictionaries

Reading items from an array using their index isn't always ideal, and could actually cause issues. Swift also has dictionaries, which are based on key/value pairs which let us decide how we want to store the data inside.

They are created in much the same way as an array, but rather than just being a long list of values, you assign keys.

let batmanData = [
    "realName": "Bruce Wayne",
    "base": "Bat Cave",
    "sidekick": "Robin"
]

When trying to return data from the dictionary, Xcode may warn you that you may, or may not, get data back. When it returns the value, it comes back with "Optional".

The solution to this is telling Swift to return another value by default if it can't return something.

print(batmanData["base"])

// If the key doesn't exist, we can tell Swift to return another value.
print(batmanData["realName", default: "Unknown"])

We can also create other types of dictionaries, such as a string/boolean, or an integer/string.

// We can also keep booleans as values - A String/Boolean dictionary
let hasGraduated = [
    "Eric": false,
    "Maeve": true,
    "Otis": false
]

// Or an Integer/String dictionary
var olympics = [
    2012: "London",
    2016: "Rio",
    2021: "Tokyo"
]

print(olympics[2012, default: "Unknown"])

To create an empty dictionary, it would be done like this:

var heights = [String: Int]()

// Add a key and value to the empty dictionary
heights["Person One"] = 229

And finally, dictionaries cannot contain duplicate values. If you were to assign a new value, it would simply overwrite the existing.

olympics[2021] = "Somewhere Else"
  • Dictionary Review Test: 11/12

Mistakes:

One of the questions asked if this was valid code. I assumed this was false as I mistakingly thought it was trying to add something to the dictionary. On a second look, I can see that it's assigning a value to a constant, based on what could be in the dictionary.

let capitals = ["England": "London", "Wales": "Cardiff"]
let scotlandCapital = capitals["Scotland"]

Using Sets for Fast Data Lookup

As well as arrays, and dictionaries, there is also sets. They work similar to arrays, but they don't remember the order you added the values, and they don't allow duplicates.

So, why would you use this over an array? You may not want to allow duplicates in your data. Sets are also incredibly efficient in how it stores the data, so returning a result by using something like .contains() will be far faster than if you used an array.

Creating a set is done in a similar way to an array.

// This creates an array inside the set
var actors = Set([
    "Denzel Washington",
    "Tom Cruise",
    "Nicolas Cage",
    "Samuel L Jackson"
])

Adding items to a set is done using insert, rather than append.

actors.insert("Angelina Jolie")

You can also sort a set, which will return an array with your sorted data.

print(actors.sorted())

For a deeper dive on sets, see https://www.avanderlee.com/swift/array-vs-set-differences-explained/

  • Sets Review Test: 12/12

Creating an Using enums

Enum, short for enumeration, is a set of named values. We can define types that contain a specific set of values, such as the days of the week:

enum Weekday {
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
}

Storing things this way is far more efficient, as Swift stores them in an optimised form.

It's also a safer way, ensuring that a wrong value couldn't be assigned.

Enums Review Test: 12/12

Summary

That's it for day 3. The dictionaries review test caught me out on something I should have spotted, but it's coming up to lunch time and I was starting to make some silly mistakes.

It's also taking far, far longer than I wanted to get through the content, and write up a post. Maybe that's a good thing, as it will reinforce what I'm taking in, but it's also making me want to stop already ... at day 3.

Show Comments