Your Avatar

Swifting Gears: From React to SwiftUI

Join me as I shift gears into the exciting world of SwiftUI!

100 Days of SwiftUI Intro ✅. Now, onto Checkpoint Solutions!

Header Image of Solutions to Checkpoints for 100 Days of SwiftUI
Photo by Olav Ahrens on Unsplash

It is quite difficult to force yourself to dedicate yourself to a daily activity of learning something new. I am not that person who starts something and then just drops it. At least not anymore 😇. I have found that I really started enjoying learning how to code in Swift for iOS. It is getting more and more interesting every day. Although Paul Hudson’s challenge is amazing there is still something I would like to record for myself. Checkpoint solutions are code snippets that I would like to keep here in case I decide to update them in the future.

Let’s start with the Checkpoint 1.

This one is the easiest as it checks your understanding of declaring variables. You need to show how you can use add, multiply, and divide Swift language operators. Of course the use of print and string interpolation.

The goal here is to convert Celsius to Fahrenheit with the help of a Swift playground. The task is the following:

1. Creates a constant holding any temperature in Celsius.

2. Converts it to Fahrenheit by multiplying by 9, dividing by 5, then adding 32.

3. Prints the result for the user, showing both the Celsius and Fahrenheit values.

There is no free solution for the checkpoints but there are some hints that I encourage you to follow if you are stuck or you are at the very beginning of your coding journey. If you have the opportunity to buy access to the solutions by subscribing to full tutorials (https://www.hackingwithswift.com/plus/solutions/checkpoint-1) you should do it.

I decided to go the other way and find the solutions myself.

            
              var celciusTemperature = 19.0
        
              var fahrenheitTemperature = (celciusTemperature * 9 / 5) + 32
        
              print(
                """
                This is the temperature in Celcius: \(celciusTemperature)°C
                And this is the temperature in Fahrenheit: \(fahrenheitTemperature)°F
                """
              )
            
          

First I have googled the formula of Celsius to Fahrenheit conversion to make it easier to see - (0°C × 9/5) + 32 = 32°F.

Next step I created a variable to hold temperature in Celsius and tried to call it as meaningful as possible (celciusTemperature). In Swift, as in JavaScript, you can put calculations after equal sign when you declare the variable. So I converted the Fahrenheit formula to the language Swift compiler can understand and after declaring the fahrenheitTemperature variable made it equal to the formula. That gives us celciusTemperature as the holder of °C and fahrenheitTemperature equals °F. 

I wanted to print the result in two lines and instead of using print() twice or using \n I chose to practice ””” triple quotations. And of course, I used String interpolation \(fahrenheitTemperature) to show the results.  One more thing I used a keyboard shortcut to enter the degree symbol (°). You should press Option + Shift + 8 on the keyboard to enter the symbol.

Checkpoint 2.

Here you have to create an array of strings, then write some code that prints the number of items in the array and also the number of unique items in the array. The most important thing here is a unique number of items of course. The best way is to use a Set as it doesn’t allow duplicates inside.

This is the code I came up with.

            
                let enigmaAlbums: [String] = [
                      "The Fall of a Rebel Angel", 
                      "The Fall of a Rebel Angel", 
                      "Seven Lives Many Faces", 
                      "Voyageur", 
                      "A Posteriori", 
                      "The Screen Behind the Mirror", 
                      "The Cross of Changes", 
                      "Le Roi est Mort", 
                      "Vive le Roi!", 
                      "MCMXC a.D."
                      ]
        
                print("Number of items in the array: \(enigmaAlbums.count)")
        
                let setOfEngimaAlbums: Set<String> = Set(enigmaAlbums)
        
                print("Unique number of items is: \(setOfEngimaAlbums.count)")
            
        

I decided to use the albums of Enigma. I was listening to it at that time. Google the top albums and add them to an Array. I used let to declare a constant array as I am not going to update it anywhere else. I added some album names 2 times to make it clear to see if my code works and can print the number of items all together and without duplicates. The first part to print the number of all items is done with .count added after the variable enigmaAlbums. 

As the next step I need to convert Array to Set to get rid of duplicates and it is done by Set(Array). After that, I printed the number of items in the Set by using the same .count added now to a Set.

Checkpoint 3.

The goal is to loop from 1 through 100, and:

1. If it’s a multiple of 3, print “Fizz”

2. If it’s a multiple of 5, print “Buzz”

3. If it’s a multiple of 3 and 5, print “FizzBuzz”

4. Otherwise, just print the number.

This challenge is checking your logic and usage of conditions and a new method .isMultiple(of: )

Here is the solution to this challenge that I did.

            
                for i in 1...100 {
                    if i.isMultiple(of: 15) {
                        print("It is \(i) and it is - FizzBuzz!")
                    } else if i.isMultiple(of: 5) {
                        print("It is \(i) and it is - Buzz!")
                    } else if i.isMultiple(of: 3) {
                        print("It is \(i) and it is - Fizz!")
                    } else {
                        print("It is \(i)!")
                    }
                }
            
        

First I wanted to use the modulo operator (%) like in JavaScript and it worked. Then I remembered that Paul was talking about the .isMultiple(of:) method and rewriting it. This method is easier to use and easier to read than the modulo operator. 

In the beginning, I need to start the loop from 1 to 100 and for this, I used for i in 1…100 and then used the if condition to check if every number is equally divided by 15 as it is the first number that is equally divided by 3 and 5. If it is true then I printed "It is \(i) and it is - FizzBuzz!”.

The next condition is similar to the first but checks if it is a Multiple of 5 and if yes then prints "It is \(i) and it is - Buzz!”. After I checked if i was Multiple 3 that would give me “Fizz”. And if nothing above is true then I just printed the number represented in the Loop by i variable.  

Checkpoint 4.

In this challenge, you need to use your knowledge of throw functions and how to use them along with enums, and loops, and try catch blocks, and break.

The challenge is this: write a function that accepts an integer from 1 through 10,000, and returns the integer square root of that number. That sounds easy, but there are some catches:

1. You can’t use Swift’s built-in sqrt() function or similar – you need to find the square root yourself.

2. If the number is less than 1 or greater than 10,000 you should throw an “out of bounds” error.

3. You should only consider integer square roots – don’t worry about the square root of 3 being 1.732, for example.

4. If you can’t find the square root, throw a “no root” error.

This is my idea of the solution for this checkpoint.

            
                enum rootErrors: Error {
                    case tooLowNumber, tooHighNumber, noRoot
                }
                
                func rootReturn(number: Int) throws -> Int {
                    if number < 1 {throw rootErrors.tooLowNumber}
                    if number > 10000 {throw rootErrors.tooHighNumber}
                    
                    var root: Int = 0
                    
                    for i in 1...100 {
                        if i*i == number {
                          root = i
                            break
                        }
                        if i == 100 {throw rootErrors.noRoot}
                    }
                    
                     return root
                }
                
                do{
                    let numberToFind: Int = 9
                    let rootSquare: Int = try rootReturn(number: numberToFind)
                    print("The root square of \(numberToFind) is equal to \(rootSquare)")
                    
                } catch {
                  print("There is an error: \(error)")
                }
            
        

Ok. First I started with an understanding of how to calculate the square root of a number without the sqrt() functions. So easiest way is to loop throw all the integers up to the number you check. If the integer multiplied by the same integer is equal to the number then you found the square root of it. 

There is a hint given by Paul that you don’t need to go over 100. So if it is a hundred then it means there is “no root” and the function needs to throw an error. It can be easily done with the if condition but I also to update the function to be able to throw the errors.

I created the enum with error cases such as tooLowNumber, tooHighNumber, and noRoot. I added a first condition to the top of the function to check if the number is less than 1 and the next condition is to check if the number is greater than 10000. You should put it at the top to check at the very beginning and do not let the computer do extra work and just throw the error.

I used the do { } catch { } combination to use the function I wrote. Within the curly brackets of do scope, I put the variable that holds the number I need to find a square root and a variable that tries the rootReturn function, and if all is good prints the result otherwise the catch part prints the error.   

Checkpoint 5.

In this checkpoint, you revise your understanding of closures and inline code writing along with universal parameter names. 

You are given the following input: let luckyNumbers = [7, 4, 38, 21, 16, 15, 12, 33, 31, 49]

Here you have to:

1. Filter out any numbers that are even.

2. Sort the array in ascending order.

3. Map them to strings in the format “7 is a lucky number”.

4. Print the resulting array, one item per line.

My solution looks like this:

            
                let luckyNumbers = [7, 4, 38, 21, 16, 15, 12, 33, 31, 49]
        
                let filteredNumbers: () = luckyNumbers
                                .filter { !$0.isMultiple(of: 2) }
                                .sorted { $0 < $1 }
                                .forEach {
                                      print("\($0) is a lucky number") 
                                    }
            
        

This looks more familiar to me as it is the way you write code in React and it showed itself very efficient but takes time to understand it.

In the beginning, I decided to filter the whole array and create a new array of integers that are not equally divided by 2. I did it by using $0 as a universal representer for an item in the loop that filter closer does and it was checked if it is multiple of 2 (!$0.isMultiple(of: 2)). I had to put (!) to say that it will be true if it is not correct. So that added to a new array only the integers that are not even.

After that, I sorted the new array by .sorted{ $0 < $1 } where < operator shows that a preceding item is larger than the one after it. If you change it to > then it will sort it in descending order. In the last part, I used .forEach to loop through all the items of a new array and print all the lucky numbers. 

Checkpoint 6.

This challenge is a little bigger as you need to work with structs and use most of the things you learned about them in your code.

1. Create a struct to store information about a car.

2. Include: Its model, Number of seats,Current gear.

3. Add a method to change gears up or down.

4. Have a think about variables and access control.

5. Don't allow invalid gears - 1...10 seems a fair maximum.

This is how I came up with the solution.

            
              struct Car {
                let model: String
                let seatsNumber: Int
                var gear: Int
                    
                mutating func gearChange(directions: String) {
                  if directions == "Up" && gear < 10 {
                    gear += 1
                            print("Gear is changed Up to \(gear)")
                        } else if directions == "Down" && gear > 1 {
                          gear -= 1
                            print("Gear is changed Down to \(gear)")
                        } else {
                            print("Wrong move!")
                            print("You should use only words \"Up\" or \"Down\"")
                        }
                    }
                    
                    init(model: String, seatsNumber: Int, gear: Int) {
                      self.model = model
                      self.seatsNumber = seatsNumber
                      
                      if gear > 1 && gear <= 10 {
                        self.gear = gear
                        } else {
                          self.gear = 1
                        }
                    }
                }
                
                var myCar = Car(model: "Honda", seatsNumber: 5, gear: 2)
                
                myCar.gearChange(directions: "forward")
                myCar.gearChange(directions: "Up")
                myCar.gearChange(directions: "Up")
                myCar.gear
            
        

First I created the struct Car that contains variables of a car model, seat number, and gear. 

Secondly, I have added a method to change gears Up or Down that is why it should take a String parameter of a direction. Because it will update the variable inside the structure I had to put “mutating” in front of the method. So I first check if the direction is equal to “Up” and the gear is not more than 10 then 1 is added to the gear and the method is printing that it changed up. If the direction parameter is equal to “Down” and the gear number is not less one than it 1 is subtracted from the gear variable. If none of the above then it prints “Wrong move”.

As I needed to stop anyone from creating a car with gear put to less than 1 or greater than 10 I wrote my own initializer to implement that logic. I added if statement when initializing gear.

Checkpoint 7.

This checkpoint is one with a quite long code to be done here. You need to revise Classes and Hierarchy as well as Override for methods and create class Initialisers. 

This is what you need to do in this challenge:

1. Make a class hierarchy for animals.

2. Start with Animal. Add a legs property for the number of legs an animal has.

3. Make Dog a subclass of Animal, giving it a speak method that prints a dog barking string, but each subclass should print something different.

4. Make Corgi and Poodle subclasses of Dog.

5. Make Cat an Animal subclass. Add a speak method, with each subclass printing something different, and an is Tame Boolean, set with an initializer.

6. Make Persian and Lion as subclasses of Cat.

I came up with the following solution to this Checkpoint.

            
                class Animal {
                    var legs: Int
                    
                    init(legs: Int) {
                        self.legs = legs
                    }
                }
                
                class Dog: Animal {
                    
                    func speak() {
                        print("Woof")
                    }
                }
                
                class Corgi: Dog {
                    var breed: String = "Corgi"
                    
                    override func speak() {
                        print("I am a \(legs)-leg \(breed) who barks. Woof!")
                    }
                }
                
                class Poodle: Dog {
                    var breed: String = "Poodle"
                    
                    override func speak() {
                      print("I am a \(legs)-leg \(breed) who barks. Woof!")
                    }
                }
                
                class Cat: Animal {
                    var isTame: Bool
                    
                    func speak() {
                        print("Meow")
                    }
                    
                    init(isTame: Bool, legs: Int) {
                        self.isTame = isTame
                        super.init(legs: legs)
                    }
                }
                
                class Persian: Cat {
                    var breed: String = "Persian"
                    
                    override func speak() {
                        print("I am a \(breed) who is a \(isTame ? "tamed" : "wild") cat!")
                    }
                }
                
                class Lion: Cat {
                    var breed: String = "Lion"
                    
                    override func speak() {
                        print("I am a \(breed) who is a \(isTame ? "tamed" : "wild") cat!")
                    }
                }
                
                let dog = Dog(legs: 4)
                dog.speak()
                
                let corgi = Corgi(legs: 4)
                corgi.speak()
                
                let poodle = Poodle(legs: 4)
                poodle.speak()
                
                let cat = Cat(isTame: true, legs: 4)
                cat.speak()
                
                let persian = Persian(isTame: true, legs: 4)
                persian.speak()
                
                let lion = Lion(isTame: false, legs: 4)
                lion.speak()
            
        

The first thing is to create a Class of Animal which is a parent and has only a property of legs as Integer. To make this class work I needed to write an initializer that takes a parameter of legs and assigns it to property legs (self.legs = legs). After this, we can create a class of an Animal that can have legs of your choice.

The next step is to write a Subclass of a Dog using inheritance (class Dog: Animal) and give the dog the possibility to speak by adding a method speak(). 

Later I added two more subclasses of Dog which was a subclass of an Animal. These classes are Corgi and Poodle. Technically they inherit everything from a Dog class because they are its children. Because the Dog is a subclass of an Animal that means that Corgi and Poodle are the Animal’s grandchildren and inherit everything from it too. We also need Corgi and Poodle to say something different when speak() is called. To make it possible I used Override in front of the speak() method inside new subclasses and updated what Dog’s method can print.

Similar to this I have created a Cat class which is a subclass of Animal and added a speak() method and a new Bool property isTame. Here I had to use a new init() as I was adding the isTame property. Even if it is a subclass initializer we still need to pass the legs property to it and then to its parent by using super.init(legs: legs). As you may see a subclass of Cat has a new property which I used as a default so I don’t need to add it to the initializer and can easily differentiate it in code for myself. I also override the speak() method and use breed property along with isTame to print a new String when speak() is called. 

I added the possibility to change the printed string when you change the isTame property if you decide to change it when creating a new Lion or Persian class. I put a ternary operator to choose between “wild” and “tamed” depending on isTame Boolean.

Checkpoint 8.

Here is the revision done around protocols and extensions and of course, creating structs based on them.

1. Make a protocol that describes a building.

2. Your protocol should require the following:

* A property storing how many rooms it has.

* A property storing the cost as an integer.

* A property storing the name of the estate agent selling the building.

* A method for printing the sales summary of the building.

3. Create two structs, House and Office, that conform to it.

            
                protocol Building {
                    var rooms: Int {get}
                    var cost: Int {get}
                    var agentName: String {get}
                    
                    func salesSummary()
                }
                
                extension Building {
                    func salesSummary() {
                        print("The property of \(self.rooms) rooms was sold by \(self.agentName) for $\(self.cost)!")
                    }
                }
                
                struct House: Building {
                    let rooms: Int = 12
                    let cost: Int = 1_200_000
                    let agentName: String = "John Travolta"
                }
                
                struct Office: Building {
                    var rooms: Int = 3
                    var cost: Int = 300_000
                    var agentName: String = "Meralyn Manson"
                }
                
                let house = House()
                let office = Office()
                
                house.salesSummary()
                office.salesSummary()
            
        

I started with the protocol for Building struct. There I put properties for rooms as Int, for cost as Int, and for agentName as String. As it is not necessary to change these properties further but necessary to read them I added { get } without set to make sure that I cannot change them by accident. Also, I added a method that has to be in all structs that conform to my Building protocol and it is the salesSummary() method. I don’t need to put the whole method I just need to make sure this one will be in every struct that follows this protocol.

As the next step, I wrote an extension for the Building protocol where I specified exactly what needs to be printed by the salesSummary() method. I created an extension to the protocol so I don’t need to write a new method every time I create a new struct that conforms to the Building protocol. To make the salesSummray() method work as intended I used interpolation for the print() string and added properties directly from the Building protocol. But you need to make sure that the descriptor self is added in front of the variable when you use it in the extension of a protocol.

Then I created two structs House and Office that conform to a Building protocol. In this case, I need to only specify the properties as the salesSummary() method is covered by the extension.  To make it easier to read large Int numbers I used Underscores ( _ ) as they are allowed between digits for readability, but they're ignored and therefore don't affect the value.

Checkpoint 9.

When starting to read this checkpoint challenge I thought that I would need to write a lot of code as the previous ones expected you to write longer code. This is a checkpoint #9 and it covers almost everything but at the same time makes you think twice because the the most important part here is that you need to write the solution in one line of code. 

1. Write a function that accepts an optional array of integers, and returns one of those integers randomly.

2. If the array is missing or empty, return a new random number in the range 1 through 100.

3. Write your function in a single line of code.

To tell you the truth it is the shortest solution out of all 9 of them. And if you understand it you will appreciate the beauty of Swift.

            
                let randomInt = {(_ randomArr: [Int]?) -> Int in randomArr?randomElement() ?? Int.random(in: 1...100)}
                
                randomInt([1,2,3,4,5,6,7,8,9])
            
        

So here I have to use my knowledge of closures, optional, and nil-coalescing operators. 

I started with creating a closure that takes an array of Int and returns a random Int out of that array randomly. As I don’t want to use any parameter names when calling a function I used the ( _ ) underscore for it. To show that I am not sure whether the array passed to the function exists or is valid I had to put a question mark after the parameter ([Int]?). It is important not to forget to use an arrow if you have to return something (->) and specify what you need to return by adding a type after it (-> Int). 

The important part of a closure is (in) as it shows you where the code itself starts. 

As the parameter randomArr is optional I have to put a question mark (?) after it to show that it is optional. Because I needed to return a random item from the array I used the .randomElement() method after it which allows us to do it.

There is a problem as it is optional I need to return something because it will be checked if it array exists or not automatically and if not complier will crash. For that purpose, I used nil-coalescing operator (??). It pretty much says if something before me doesn’t work use the one after me. And for that purpose, I used Int.random(in: 1…100) which always returns an integer in the range from 1 to 100.

The code is very tiny and it works. It needs a little bit more study on how to read it but it saves much time if you can understand and use it in your programs. 

I wrote about these 9 Checkpoints and my solutions to them because writing helps to revise and go through the challenges once again. I hope it can help someone to have their “Aha” moments for Swift language concepts.