Developers new to Swift are often confused by the seemingly random sprinkling of question and exclamation marks in Swift code. Yesterday, we discussed the meaning of the question mark. Today, I would like to zoom in on the meaning of the exclamation mark in Swift.
Note that I won't be discussing the prefix and infix operators that contain an exclamation mark. The topics covered today are:
- forced unwrapping
- implicitly unwrapped optionals
- error handling using the
try!
keyword
What You Already Know
The Swift language tries to be expressive with as few words or symbols as possible. The question and exclamation mark are an example of this exercise. As we discussed yesterday, the question mark hints at an uncertainty. An optional contains a value or it doesn't. We are not given a guarantee hence the question mark.
The bang or exclamation mark hints at potential danger. If you use an exclamation mark in Swift, you are about to perform an operation that can backfire. You are about to perform a dangerous operation and are doing so at your own risk. That is the meaning of the exclamation mark in Swift.
If you use an exclamation mark in Swift, you are about to perform an operation that can backfire.
Forced Unwrapping
As we discussed in yesterday's tutorial, you don't need to safely unwrap the value stored in an optional. Optional binding isn't the only way to access the value of an optional. You can directly access an optional's value using the exclamation mark. This is known as forced unwrapping.
class Person {
var name: String?
}
let person = Person()
person.name = "John Doe"
print(person.name)
print(person.name!)
But there is a caveat. If you forced unwrap an optional that is nil
, a runtime error is thrown and the process or application is terminated. You take a risk and you are responsible for the consequences.
class Person {
var name: String?
}
let person = Person()
// person.name = "John Doe"
print(person.name)
print(person.name!)
The message is clear. Only use forced unwrap optionals if you are absolutely certain that the optional you are accessing contains a value. That said, I strongly recommend to avoid forced unwrapping as much as possible. The use of the exclamation mark is in many scenarios a code smell.
Optional Binding
Remember from yesterday's tutorial that you can use optional binding instead. Together with optional chaining, optional binding is the preferred way to access the value of an optional.
class Person {
var name: String?
}
let person = Person()
person.name = "John Doe"
if let name = person.name {
print(name)
}
Optional Chaining
Optional chaining is another technique to safely and elegantly access an optional's value or invoke a method on an optional. If the optional doesn't contain a value, the method isn't invoked.
class Person {
var name: String?
func sayHello() {
print("hello")
}
}
var person: Person?
person = Person()
person?.sayHello()
person?.name = "John"
The question mark indicates that we use optional chaining to invoked sayHello()
and set the value of the name
property. We could achieve the same result using forced unwrapping. But you now know the implicit danger of using the exclamation mark.
person!.sayHello()
person!.name = "John"
Implicitly Unwrapped Optionals
As the name implies, implicitly unwrapped optionals are optionals. But the name also suggests that they are a bit different. Implicitly unwrapped means that the value of the optional can be directly accessed, there is no need to unwrap the value. An implicitly unwrapped optional is defined with an exclamation mark instead of a question mark.
class Person {
var name: String!
func sayHello() {
print("hello")
}
}
let person = Person()
person.name = "John"
print(person.name)
The exclamation mark indicates that the name
property of the Person
class is defined as an implicitly unwrapped optional. Note that the print statement returns the value of name
, not an optional.
Even though we can directly access the value of implicitly unwrapped optionals, we can continue to use optional binding and optional chaining to safely access the optional's value.
if let name = person.name {
print(name)
}
When to Use Implicitly Unwrapped Optionals
You can use implicitly unwrapped optionals if you are absolutely certain that the implicitly unwrapped optional has a value when it is first accessed. Outlets are a common use case for implicitly unwrapped optionals.
Remember that every stored property of a class needs to have a valid value before the initialization of the instance is complete. If you don't know the value of a stored property during initialization, you can assign a sensible default value or use an optional, which have a default value of nil
, the absence of a value. But there is a third option, implicitly unwrapped optionals.
If you use an implicitly unwrapped optional, the above requirement is met since the value of the implicitly unwrapped optional is nil
by default. But you can still directly access the value of the implicitly unwrapped optional.
Outlets are a common use case for implicitly unwrapped optionals. The value of an outlet is set after the instance of the class is initialized. This is inconvenient since you know that the outlet will have a valid value when it is later accessed.
import UIKit
class ViewController: UIViewController {
@IBOutlet var titleLabel: UILabel!
...
}
By declaring an outlet as an implicitly unwrapped optional, its value doesn't need to be set during initialization of the class instance and it can still be accessed as a non-optional.
Drawbacks
Because we use an exclamation mark to define an implicitly unwrapped optional, there is danger involved. Whenever you access an implicitly unwrapped optional that is equal to nil
, a runtime error is thrown. That is the danger of using implicitly unwrapped optionals.
Always remember that implicitly unwrapped optionals are a tradeoff between safety and convenience.
Always remember that implicitly unwrapped optionals are a tradeoff between safety and convenience. The only advantage implicitly unwrapped optionals have over optionals is convenience, that is, you can directly access the value of an implicitly unwrapped optional. You don't need to use optional binding or check for nil
before accessing its value.
Error Handling
Error handling is built into the Swift language and the try
keyword makes it unmistakable when you invoke a method or function that is throwing.
do {
let person = try createPerson(withName: "John")
} catch {
print(error)
}
try?
Because you need to wrap a throwing method call in a do-catch
statement, error handling is a bit verbose. If you are not interested in the error that may be thrown, but you still want to know if the operation was successful, you can use the try?
keyword.
By using the try?
keyword, an optional is returned and no errors are thrown. The optional contains a value if the operation is successful and is equal to nil
if it failed. The advantage is that you don't need to wrap the throwing function or method call in a do-catch
statement.
let person = try? createPerson(withName: "John")
try!
If you are absolutely certain that a throwing method or function won't throw an error, you can use the try!
keyword. The try!
keyword removes the need for a do-catch
statement and doesn't return an optional. Double win. Right? Not really.
let person = try! createPerson(withName: "John")
If the throwing method or function does throw an error, the error causes the process or application to terminate. The error cannot be caught and this causes your application to crash.
The try!
keyword is an odd addition to the Swift language and I have never had a valid use for it.
Don't Be Lazy
Safety is an integral aspect of the Swift language. Embrace it. Use the exclamation mark sparingly. You can write Swift without ever having to use forced unwrapping, implicitly unwrapped optionals, or the try!
keyword. It is up to you what type of code you want to write.
Even though Apple uses implicitly unwrapped optionals to define outlets, that doesn't mean you have to follow this pattern. Curtis Herbert, for example, prefers to play it safe and doesn't use implicitly unwrapped optionals for outlets. You decide how much risk you want to take.