Interesting ways of using Swift optionals
The concept of optionals has been a core part of Swift since the very beginning, and it’s incredibly rare to come across a Swift code base that doesn’t use optional values one way or another.
However, despite both being so commonly used, and conceptually simple (an optional either contains a value, or it doesn’t), optionals have quite a few interesting features that go quite far beyond simple unwrapping using statements like if let or guard let. So let’s explore some of those features, shall we?

Swift by Sundell is brought to you by the Genius Scan SDK — Add a powerful document scanner to any mobile app, and turn scans into high-quality PDFs with one line of code. Try it today.
Taking values
Sometimes, when handling an optional value, we want to immediately reset it back to nil when we’re done, effectively extracting (or taking) the inner value that the optional might contain.
For example, within the following TextViewModel we apply a text suggestion that the user has accepted, and then set the suggestion property back to nil in order to reset our view model’s state:
@MainActor struct TextViewModel {
var text: String
private(set) var suggestion: String?
mutating func acceptSuggestion() {
guard let suggestion else { return }
self.suggestion = nil
text = suggestion
}
}That works perfectly fine, but there’s an arguably more elegant way to implement the above kind of pattern, and that’s by using the built-in take method instead, which (like its name implies) takes the optional’s wrapped value and leaves nil in its place — giving us this implementation:
@MainActor struct TextViewModel {
var text: String
private(set) var suggestion: String?
mutating func acceptSuggestion() {
guard let suggestion = suggestion.take() else { return }
text = suggestion
}
}The above might fall into the category of syntactic sugar, but it’s still a useful tool that also helps prevent mistakes if any of our code paths would miss setting the property in question back to nil, since that’s now automatically done as part of retrieving the value.
Direct switching
Continuing with the theme of syntactic sugar, let’s explore how optional enum values can be directly switched on before first being unwrapped.
Within the following example, we’ve defined a LoadingState enum which is being used by a SyncService in order to keep track of whether the service is currently performing a sync operation:
enum LoadingState {
case inProgress(completionRate: Double)
case finished(Result<Date, Error>)
}
actor SyncService {
private var loadingState: LoadingState?
...
func sync() async {
...
}
}Now, if we wanted to implement a statusText method that takes that optional loadingState property and switches on it in order to return different status texts depending on what kind of state the service is in, then we might take an approach like the following — where we first guard-unwrap the optional, and then switch on its value:
extension SyncService {
func statusText() -> String {
guard let loadingState else {
return "Not yet synced"
}
return switch loadingState {
case .inProgress(let completionRate):
"Syncing... (\(Int(100 * completionRate))% done)"
case .finished(.success(let date)):
"Synced as of \(date.formatted())"
case .finished(.failure(let error)):
"Sync failed: \(error)"
}
}
}Again, the above is a completely valid solution, but what’s really cool is that we can instead simply include nil as one of the case patterns that we’re matching against, which enables us to handle the optional unwrapping and switching within a single statement (which in turn enables us to omit all return keywords within our method, since we’re now directly returning the result of our switch expression):
extension SyncService {
func statusText() -> String {
switch loadingState {
case nil:
"Not yet synced"
case .inProgress(let completionRate):
"Syncing... (\(Int(100 * completionRate))% done)"
case .finished(.success(let date)):
"Synced as of \(date.formatted())"
case .finished(.failure(let error)):
"Sync failed: \(error)"
}
}
}Alternatively, we could’ve also chosen to match against .none, which would’ve made all of our above case patterns look uniform. That works since nil is effectively an alias for Optional.none, although nil is definitely the more commonly used term to refer to the lack of a value.
Functional chains
Sometimes, when we have to unwrap a chain of multiple optionals, it’s easy to get into a situation where we’re starting to construct a nested pyramid of if let statements that makes our code become increasingly indented. For example, in a situation like this, where we’re extracting an optional deep link path from a dictionary:
func handleNotificationJSON(_ json: [String: String]) {
if let deeplink = json["deeplink"] {
if let deeplinkURL = URL(string: deeplink) {
openDeeplinkPath(deeplinkURL.relativePath)
}
}
...
}Of course, situations like the above is what the guard statement was made for. However, using guard unwrapping requires us to exit the current scope if the value in question is nil, so in order to use such a statement we’d have to wrap our path extracting code within a new scope, such as a dedicated utility function:
func openNotificationDeeplinkIfNeeded(withJSON json: [String: String]) {
guard let deeplink = json["deeplink"],
let deepLinkURL = URL(string: deeplink)
else { return }
openDeeplinkPath(deepLinkURL.relativePath)
}Thankfully, there’s a way to get the best of both worlds — to both avoid nested statements and to keep everything within our original function’s scope. Swift optionals come with two very useful functional mapping methods — map, which transforms the inner value into another one, and flatMap, which transforms into another optional. By chaining those two APIs together, we can create sort of a functional chain that extracts and handles our deep link paths as a connected set of operations:
func handleNotificationJSON(_ json: [String: String]) {
json["deeplink"]
.flatMap(URL.init)
.map(\.relativePath)
.map(openDeeplinkPath)
...
}By passing both functions and a key path-based reference to the relativePath property above, we can construct quite an elegant chain that still keeps our code easy to read and maintain.
Extending the Optional type
So far, we’ve been exploring built-in optional APIs and language features, but since Swift’s optionals are modeled using an actual type (the Optional enum), that means that we can also extend that type to add our own APIs and functionality.
For example, a common way to add a utility property that lets us check whether an optional collection (such as an array or string) is nil or empty is to extend Optional when its Wrapped value type conforms to the standard library’s Collection protocol — like this:
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
self?.isEmpty != false
}
}But let’s see if we can generalize that concept a bit to make it more powerful. In general, it’s quite common to want to check if an optional is either nil or if its wrapped value meets some criteria, besides specifically being an empty collection.
So to express the above idea in a more general-purpose way, we could write a function that accepts an expression closure that we can then map onto the optional’s wrapped value (or otherwise return false as a fallback, in case the optional is nil):
extension Optional {
func isNil(or expression: (Wrapped) -> Bool) -> Bool {
map(expression) ?? true
}
}With the above in place, combined with the magic of Swift’s key paths, we can now express “is nil or empty” like this:
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
isNil(or: \.isEmpty)
}
}Of course, we can absolutely still keep the above computed property, since its such a common variant of the “is nil or X” pattern, but because we have now created a general-purpose matching API for that pattern, we can also apply it elsewhere.
For example, let’s go back to our LoadingState enum from earlier, and extend it with a computed isFinished property, which will tell us whether the loading operation in question is considered finished, or whether it’s currently in progress:
enum LoadingState {
case inProgress(completionRate: Double)
case finished(Result<Date, Error>)
var isFinished: Bool {
switch self {
case .inProgress: false
case .finished: true
}
}
}Combining the above with our isNil(or:) API, we can now write a very clean, simple check to guard against duplicate sync operations within our SyncService — like this:
actor SyncService {
private var loadingState: LoadingState?
...
func sync() {
guard loadingState.isNil(or: \.isFinished) else { return }
...
}
}Just like take, map, and flatMap, it’s not like we’ve invented some brand new piece of functionality with isNil(or:). But if we’re performing checks against that kind of pattern in many different parts of a code base, then adding that little bit of syntactic sugar can make that code base easier to work with, and our code simpler to both read and write.

Swift by Sundell is brought to you by the Genius Scan SDK — Add a powerful document scanner to any mobile app, and turn scans into high-quality PDFs with one line of code. Try it today.
Conclusion
Swift optionals are really quite powerful. Not only in the way that they help us avoid bugs and crashes when a value turns out to be unexpectedly missing, but also because they come with their own set of APIs and features (which can then be further extended) that can help us model common patterns used when working with optional values in very neat ways.
Thanks for reading!

