Optionals are in the core of Swift and have existed since the first version of Swift. An optional value allows us to write clean code while at the same time taking care of possible nil values.
If you’re new to Swift, you should get used to the syntax of adding a question mark to properties. Once you get used to them, you can start benefiting from them with, for example, extensions.
What is an optional value in Swift?
Before we dive into the list of things you should know, it’s first good to know the basics.
Properties, methods, and subscripts can return an optional which basically means that it either returns a value if it exists or otherwise nil
. Multiple queries can be chained together, which is called Optional chaining. This is an alternative to Force Unwrapping which is explained in more detail later on.
The following code example defines an optional String
and uses chaining to print out the count of characters.
let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)
Note: The ?? operator (nil coalescing operator) will be explained later on.
1. Forced Unwrapping optionals in Swift
Force Unwrapping either returns the value if it exists or triggers a runtime error when the value is nil
.
But before we dive into force unwrapping, lets first go over the possibilities of unwrapping without force.
How to unwrap an optional?
There are multiple ways to unwrap a value in Swift. You can use a guard statement:
let name: String? = "Antoine van der Lee"
guard let unwrappedName = name else {
return
}
print(unwrappedName.count)
Or you can use an if let statement:
let name: String? = "Antoine van der Lee"
if let unwrappedName = name {
print(unwrappedName.count)
}
And since SE-0345, we can also use the shorthand syntax to unwrap properties of the same name:
let name: String? = "Antoine van der Lee"
if let name {
print(name.count)
}
Or you can use the double question marks operator, also known as the nil coalescing operator. This will either return the optional value if it exists or the default value, which in this case is defined as zero:
let name: String? = "Antoine van der Lee"
print(name?.count ?? 0)
Force unwrapping using the exclamation mark (!)
An optional can be forced unwrapped using the exclamation mark (!) directly after the value:
var name: String? = "Antoine van der Lee"
print(name!.count)
Whenever the name variable in the above example would be set to nil
it would cause a fatal runtime error as following:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
Therefore, it’s important to realize you’re in control and you’re risking a crash by using force unwrapping. In my experience, it’s safer and better not to use force unwrapping if possible.
Unwrapping can be chained
Optional chaining can be done like this:
struct BlogPost {
let title: String?
}
let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title?.count ?? 0)
And the same counts for force unwrapping:
let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post!.title!.count)
But be aware that if you would only unwrap the last optional you would still end up with an optional. In the following example, we’re only unwrapping the title but not the post. This means that if the post is nil
we would still not get back a title:
let post: BlogPost? = BlogPost(title: "Learning everything about optionals")
print(post?.title!.count) // Prints: Optional(35)
Optionals as best practice, force unwrapping to catch programming errors
It’s best practice not to use the exclamation mark if it’s not needed. Some even recommend enabling the force unwrapping SwiftLint rule. This will prevent you from introducing a lot of unexpected crashes.
However, some developers prefer to use force unwrapping when it’s a programming error if a value is nil
. Therefore, you can help yourself debug by force unwrapping and catching a bug in an early stage. My preference is trying not to use force unwrapping at all.
2. An optional is an enum of two cases
It’s good to know that an optional is basically an enum of two cases:
enum Optional<Wrapped> {
/// The absence of a value.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
}
However, instead of using the .none
case you would use nil
to indicate the absence of a value.
With that in mind, you can define the above name variable by using the enum:
let name = Optional.some("Antoine van der Lee")
print(name!.count)
Or you can use a switch-case just like you would with a normal enum:
func printName(_ name: String?) {
switch name {
case .some(let unwrappedValue):
print("Name is \(unwrappedValue)")
case .none:
print("Name is nil")
}
}
printName(nil) // Prints: "Name is nil"
printName("Antoine van der Lee") // Prints: "Name is Antoine van der Lee"
And looking at its documentation you can see that an optional comes with quite some handy methods. A great example is the map
method:
let sideLength: Int? = Int("20")
let possibleSquare = sideLength.map { $0 * $0 }
print(possibleSquare) // Prints: "Optional(400)"
Or the flatMap
method, which in this case only returns the name if it passed the validation of having at least 5 characters:
var name: String? = "Antoine van der Lee"
let validName = name.flatMap { name -> String? in
guard name.count > 5 else { return nil }
return name
}
print(validName) // Prints: "Optional("Antoine van der Lee")"
If you want to know more about the differences between map, flatMap, and compactMap, check out my blog post: CompactMap vs flatMap: The differences explained
Extending optionals
Now you know that an optional is defined as an enum; you can also write extensions for it!
The most common example is to extend a String optional and handle an empty value:
extension Optional where Wrapped == String {
var orEmpty: String {
return self ?? ""
}
}
var name: String? = "Antoine van der Lee"
print(name.orEmpty) // Prints: "Antoine van der Lee"
name = nil
print(name.orEmpty) // Prints: ""
While we used the enum to define the extension, we could also use the following syntax using the question mark:
extension String? {
var orEmpty: String {
return self ?? ""
}
}
3. Writing unit tests for optionals
When you’re writing tests, there is a nice way to work with optionals without force unwrapping. If you would use a force unwrap, you take the risk of causing a fatal error that will stop all your tests from succeeding.
You can use XCTUnwrap
, which will throw an error if the value is nil
:
func testBlogPostTitle() throws {
let blogPost: BlogPost? = fetchSampleBlogPost()
let unwrappedTitle = try XCTUnwrap(blogPost?.title, "Title should be set")
XCTAssertEqual(unwrappedTitle, "Learning everything about optionals")
}
4. Optional protocol methods
If you’ve had experience with Objective-C, you might miss the optional protocol methods. Although there is a better way to mimic this behavior in Swift using default protocol implementations, a common way in standard libraries looks as follows:
@objc protocol UITableViewDataSource : NSObjectProtocol {
@objc optional func numberOfSections(in tableView: UITableView) -> Int
// ...
}
This allows you to call the method using the question mark:
let tableView = UITableView()
let numberOfSections = tableView.dataSource?.numberOfSections?(in: tableView)
You can read more about protocol methods here: Optional protocol methods in Swift.
5. Nested optionals is a thing
Although SE-0230 – Flatten nested optionals resulting from ‘try?’ removed one of the most common causes of a nested optional, it’s still a thing!
var name: String?? = "Antoine van der Lee"
print(name!!.count)
You’ve unwrapped an optional which still returns an optional. This used to be the case when you used the try?
operator in earlier Swift versions.
A common example is when you’re working with dictionaries that contain optional values:
let nameAndAges: [String:Int?] = ["Antoine van der Lee": 28]
let antoinesAge = nameAndAges["Antoine van der Lee"]
print(antoinesAge) // Prints: "Optional(Optional(28))"
print(antoinesAge!) // Prints: "Optional(28)"
print(antoinesAge!!) // Prints: "28"
You can see that it basically only requires to use an extra exclamation or question mark.
Conclusion
That’s it! We covered a lot of things you need to know when working with optionals in Swift. From the basics of unwrapping using the exclamation mark (!!) to the more advanced implementations of extending the Optional enum.
If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.
Thanks!