It’s day 14 of the 100 Days of SwiftUI! Yesterday, we learned about protocols. Today, we’re learning about optionals. Optionals are quite difficult to grasp, but we’re told they’re an essential part of SwiftUI. So, let’s dive in!
Handle missing data with optionals
Swift is a safe language, that encourages us to write code that is safe and works as we expect. To help us do that, Swift features optionals. An optional is a constant or variable that might contain nothing.
When I say nothing, I really do mean nothing. For example, a string containing just double quotes without any text is not nothing, it’s just an empty string. An integer with a value of 0 is not nothing either.
By saying that a constant/variable might be optional, we ensure Swift forces us to actually check if there is any data before we can use it and don’t run into any unexpected trouble. Checking if an optional contains a value or not, is called unwrapping. Here’s an example of how to unwrap an optional.
var myNumber: Int? = nil
if let myNumber = myNumber {
print("The number is \(myNumber)")
} else {
print("There is no value.")
}
// "There is no value"
myNumber = 5
if let myNumber = myNumber {
print("The number is \(myNumber)")
} else {
print("There is no value.")
}
// "The number is 5"
Besides using if let let like in the example above, you can also unwrap an optional with guard. The difference is that when using if let, you’re checking if an optional has a value and then run the code within the braces if it does. With guard you’re checking if an optional has a value and run the code within the braces if it doesn’t. Guard is used with functions to check if they work properly and data is passed in.
func printSquare(of number: Int?) {
guard let number = number else {
print("Missing input")
return
}
print("\(number) x \(number) is \(number * number)")
}
Nil coalescing operator
It turns out there even is a third way to unwrap an optional and it’s called the nil coalescing operator. This operator allows you to specify a default value if an optional turns out to be empty (nil). If you want to know when to use the nil coalescing operator, check out this article by Paul.
let soccerTeam: String? = "AFC Ajax"
let newTeam = soccerTeam ?? "Mavericks FC"
// If soccerTeam was nil, newTeam would get the default value provided: "Mavericks FC"
Chaining multiple optionals with optional chaining
It’s possible to chain multiple optionals together. This allows you to unwrap an optional and then, if the optional contains a value, add additional code or check another optional inside the first optional. If an optional does turn out to be empty, the additional code won’t do anything and will just return nil. If you want to know why optional chaining is important, Paul has got you covered.
struct Book {
let title: String
let author: String?
}
var book: Book? = nil
let author = book?.author?.first?.uppercased() ?? "A"
print(author)
// Check if the book has an author. If it does, then give us the first letter. If it has a first letter, then uppercase it. If it's empty (nil) return "A".
Handling function failure with optionals
During day 8 of the 100 Days of SwiftUI, we learned how to handle possible errors within our functions. As it turns out, there is also a way to handle possible errors using optionals.
By using try? instead of do, try and catch, you can simplify how to handle an error. Essentially, by using try?, what you’re telling Swift is if there’s an error, just return a nil and if not, return an optional containing the value that was expected.
The downside to using an optional to catch an error is that Swift will not tell you what the error was. If you want to know what the error was and specify how to handle it, use the regular way of handling errors. If you just want to test a function and don’t care about the error, or at least not at that moment, optionals are a great way to quickly test whether a function works or not.
// Regular error handling
func raceOnTrack(speedInPit: Int, tracklimit: Bool) throws -> String {
if speedInPit > 300 || speedInPit < 0 {
throw driverError.tooFastInPit
} else if tracklimit == false {
throw driverError.trackLimits
} else {
return("Racing within speed and track limits!")
}
}
do {
let maxVerstappen = try(raceOnTrack(speedInPit: 250, tracklimit: true))
print(maxVerstappen)
} catch driverError.trackLimits {
print("You've exceeded the track limits, Max.")
} catch driverError.tooFastInPit {
print("You exceeded the speed limit in the pitlane, Max.")
} catch {
print("There was an unknown error, we'll check it after the race, Max.")
}
// If we don't care about the error at the moment, you can make use this code instead
if let maxVerstappen = try? raceOnTrack(speedInPit: 300, tracklimit: false) {
print(maxVerstappen)
} else {
print("There was an unknown error, we'll check it after the race, Max.")
}
SwiftUI checkpoint 9
We finished the final day of SwiftUI fundamentals with checkpoint 9. The challenge today:
Write a function that accepts an optional array of integers, and returns one randomly. If the array is missing or empty, return a random number in the range 1 through 100.
If that sounds easy, it’s because I haven’t explained the catch yet: I want you to write your function in a single line of code. No, that doesn’t mean you should just write lots of code then remove all the line breaks – you should be able to write this whole thing in one line of code.
Let me know what you think of my solution!
// Create a function that accepts an optional array of integers and returns an integer
func optionalArray(randomNumbers: [Int]?) -> Int {
/* Check if randomNumbers contains one or more integer, then select a random element.
If the optional array is empty, return a random integer in the range of 1 to 100 instead */
randomNumbers?.randomElement() ?? Int.random(in: 1...100)
}
// Create a variable that contains an optional Int array and declare it empty.
var randomArray: [Int]? = nil
// Call the optionalArray function to test it.
let randomNumber = optionalArray(randomNumbers: randomArray)
print(randomNumber)
And that was it for day 14! This was the final day where we covered SwiftUI fundamentals. Tomorrow, there’s a consolidation day where we reflect on the past two weeks and go over the difficult subjects again. Time to recharge!
100 Days of SwiftUI – Day 14 – Optionals