Unwrap Swift optional value in Switch case

⋅ 4 min read ⋅ Swift

Table of Contents

When you want to unwrap an optional value in Swift, you probably use if let or guard let syntax.

let name: String? = "Sarunw"

if let name = name {
print("Hello, \(name)!")
} else {
print("Hello, World!")
}

But you might not be aware that you can also do this in a switch statement.

Unwrap optional value the wrong way

Unwrap optional value with switch case is a bit tricky if you aren't familiar with the language.

First, let's try to unwrap it the wrong way.

let name: String? = "Sarunw"

switch name {
case let unwrappedName:
print("Hello, \(unwrappedName)!")
default:
print("Hello, World!")
}

If you try to do it like this, you will get two warnings.

You can't unwrap an optional value like this.
You can't unwrap an optional value like this.
  1. The first one is because our unwrappedName variable that we expected it to be unwrapped is still an optional value.
  2. The second warning one telling you that the default case wouldn't be reached.

As you can see, we can't use case let[1] to unwrap an optional value in switch case. That would just bind the same optional value to a new variable[1:1].

Since the first case just takes any value and reassigns it to a new variable, there is no more case to handle. And that is a reason why the default case won't be reached.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Optional Binding using a switch statement

You have to know one fact to unwrap an optional value using a switch statement. That is, an optional is just an enum.

Optional Enum

Optional is an enumeration with two cases.
none represents an absence of a value (nil).
some(Wrapped) represents a presence of a value stored as an associated value.

enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
}

Since it is an enum with an associated value, we can get an unwrapped optional value by reading the associated value from the .some case, the same way we do with a normal enum.

let name: String? = "Sarunw"

switch name {
case .none:
print("Hello, World!")
case .some(let unwrappedName):
print("Hello, \(unwrappedName)!")
}

Optional Pattern

There is also a special pattern for direct matches against a value wrapped in a .some(Wrapped) called Optional Pattern.

You can use an Optional pattern by putting a question mark (?) right behind a variable name.

Optional patterns are syntactic sugar for Optional enumeration case patterns. So, the following are equivalent:

let name: String? = "Sarunw"

switch name {
case .none:
print("Hello, World!")
case .some(let unwrappedName):
print("Hello, \(unwrappedName)!")
}

switch name {
case .none:
print("Hello, World!")
// Optional Pattern
case let unwrappedName?:
print("Hello, \(unwrappedName)!")
}

case .some(let unwrappedName) is equivalent to case let unwrappedName?.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Conclusion

Unwrap optional values using a switch statement might seem overcomplicated for simple cases that I show in this article. But you would find a place for it in more complex cases.

Here is an example of an optional as an associated value of another enum.

let result: Result<String?, Error> = .success("Response")

switch result {
case .success(let str):
// 1
if let str = str {
print("Success: \(String(str))")
} else {
print("Success with no Data")
}
case .failure(let error):
print("Failure: \(error.localizedDescription)")
}

switch result {
// 2
case .success(let str?):
print("Success: \(String(str))")
// 3
case .success(.none):
print("Success with no Data")
case .failure(let error):
print("Failure: \(error.localizedDescription)")
}
  1. In the first case, 1, we unwrap an optional associated value using if let.
  2. In the second case, we use what we learn and split .success into two cases. One for a success case with value 2, another one without the value 3.

Another example where this might be useful is when we have to control the flow based on more than one optional value.

In this example, I try to print a proper message based on two optional values, first name and last name.

let firstName: String? = "John"
let lastName: String? = nil

if let firstName = firstName,
let lastName = lastName {
print("\(firstName) \(lastName)")
} else if let firstName = firstName {
print("Only \(firstName)")
} else if let lastName = lastName {
print("Only \(lastName)")
} else {
print("No name")
}

switch (firstName, lastName) {
case let (firstName?, lastName?):
print("\(firstName) \(lastName)")
case let (firstName?, .none):
print("Only \(firstName)")
case let (.none, lastName?):
print("Only \(lastName)")
case (.none, .none):
print("No name")
}

There is nothing wrong with if else, but I usually find this kind of comparing easier to understand on a switch statement.

The thing we learned in this article is just a tool. You have to judge for yourself when to use it. There is no right or wrong.


  1. A value-binding pattern binds matched values to variable or constant names. https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#ID422. ↩︎ ↩︎


Read more article about Swift or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to change Background Color of Button in SwiftUI

Learn how to change the background color of a SwiftUI Button.

Next
Enable and Disable SwiftUI List rows reordering on a specific row

The onMove modifier enables item reordering on every row. Let's learn how to disable this on particular rows.

← Home