Why Does Swift Need Implicitly Unwrapped Optionals If We Already Know There Is a Value?
Have you ever encountered a situation where you are reading someone else's Swift code and see strange constructs with an exclamation mark in the variable declaration? var name: String! — this is not just an optional type, it is an Implicitly Unwrapped Optional (IUO). And a reasonable question arises: "Why create such a thing if you are sure the value exists? Why not just use a regular String?"
This question plagues thousands of developers, and on Stack Overflow it has gathered tens of thousands of views. Today we will finally figure out why IUOs are needed, when to use them, and when it is better to stay away. Let's go!
A Brief Excursion into Optionals (for those who forgot)
Before diving into IUOs, let's recall what regular Optionals are. In Swift, a variable that can be nil must be declared as optional:
var maybeNumber: Int? = 42maybeNumber = nil // This is fineTo use the value, you need to unwrap it — either via if let, guard let, or via force unwrap:
print(maybeNumber!) // Dangerous! If nil — crashNow imagine that you are absolutely sure that the variable will never be nil at the moment of access, but for some reason you cannot assign it a value during initialization. This is where Implicitly Unwrapped Optionals come in.
What Is an Implicitly Unwrapped Optional?
An Implicitly Unwrapped Optional is an optional type that automatically unwraps when accessed. You declare it with ! instead of ?:
var name: String! = "Alice"print(name) // "Alice" — automatically unwrapped!Notice: we don't need to write name!. The system does it for us. However, if name turns out to be nil at the moment of access, the application will crash with an error — exactly like with force unwrap.
IUO is a promise to the compiler: "I know what I'm doing. This value will exist, even if it's not there right now." It is a compromise between the safety of Optionals and the convenience of regular types.
Main Use Cases for IUO
1. Outlets in Interface Builder (UIKit)
The most common example is @IBOutlet in iOS development. When you create a connection between code and Storyboard, the outlet cannot be initialized in init because the view hasn't been loaded yet. But after loading (viewDidLoad), you know for sure that the outlet exists. Previously (before Swift 3), all outlets were declared as IUO:
@IBOutlet var label: UILabel!Why not UILabel?? Because then every time you access the label, you would have to write label! or use guard let. This is tedious and clutters the code. IUO makes the code cleaner while preserving safety (if you accidentally forget to connect the outlet, the crash will happen immediately, and you will quickly find the error).
Important: Starting with Xcode 9 and Swift 4, Apple recommends using regular Optionals UILabel? for outlets. However, many projects still use IUO, and it works.
2. Two-Phase Initialization
Sometimes you cannot assign a value to a property at initialization time, but you guarantee it will be set before first use. A classic example is dependencies that are set after calling another method:
class NetworkManager { var apiKey: String! func configure(with key: String) { self.apiKey = key } func fetchData() { // Here apiKey is definitely not nil print("Fetching