Unwrapping Optionals in SwiftUI

Emmanuel Garsd
2 min readNov 14, 2019

--

The current ways of unwrapping optionals in a SwiftUI View currently feel a bit weird, so let’s get creative and make it better!

A Classic Example

Let’s say we have an AvatarView that takes in an optional image and an optional string to display. Because you can’t use optional binding to unwrap our values in a view builder you might come up with something like this:

struct AvatarView: View {
var image: UIImage?
var initials: String?

var body: some View {
ZStack {
initials.map {
Text($0)
.colorInvert()
}
image.map {
Image(uiImage: $0)
.resizable()
}
}
.frame(width: 32, height: 32)
.background(Color.blue)
.cornerRadius(16)
}
}

You could also use if statements like this:

struct AvatarView: View {
var image: UIImage?
var initials: String?

var body: some View {
ZStack {
if initials != nil {
Text(initials!)
} else if image != nil {
Image(uiImage: image!)
}
}
.frame(width: 32, height: 32)
.background(Color.blue)
.cornerRadius(16)
}
}

Better buuut the force unwrapping doesn’t feel very swifty. There are several other techniques but to me none feel quite right so, using a little magic, we can make it look like this..

The Solved View

To me this feels much better. No force unwraps. No overlaps. No fuss. Plus we can create a chain of fallbacks as long as we want.

How To

EDIT 11/1/19: Using AnyView is bad practice from a performance standpoint. I figured out how to adjust this so type erasure isn’t necessary. Will post soon.

EDIT 3/11/20: Turns out AnyView isn’t necessarily bad. See this link for more info.

OptionalView initializes with an optional value. If that value is nil OptionalView returns nil as well. If the value is not nil then we pass it to the content closure as a safely unwrapped value.

The other piece of the puzzle is our extension on the Optional type when the type is a View. We created two functions that in essence chain a fallback type erased view in case the original value was nil.

That’s all the time I have for now. Next time maybe I’ll get into how to do the same thing with enums which would be a “state safe” way of addressing this issue? Let me know what you think.

OptionalView(cheers) { cheers in
MannyView(cheers, "🍻")
}

--

--