Update: with Xcode 12, we can write if let
bindings without problems. This code is now valid:
if let address = library.address {
Text(address)
}
I'll leave the post up for historical context.
Recently I was building an app that lists public libraries in my home city. In early stages, the main view was just a list view that displays the library name and address. Something like this:
As you can see, some libraries don't have an address, because of some unrelated reasons. And if the address isn't present, the name should be vertically centred in the row.
A library not having an address is translated into my data model as an optional String. Here's a simplified version of my Library
model:
Initial approach
If a library doesn't have an address, I won't render a Text
component for it, simple as that. And the easiest way for me to do that was using an if let
binding:
As it turns out, SwiftUI doesn't like this. The compiler tells us that Closure containing control flow statement cannot be used with function builder 'ViewBuilder'
. I beg your pardon?
How else am I supposed to conditionally render views in my app if "control flow statement cannot be used" to build it? After a moment of panic, I tested using a normal if
:
And surprisingly that worked... wtf. Needless to say, I wasn't very exited with that solution, since it requires force unwrapping an optional value. I knew that there was another way.
Current solution
One of the key features of SwiftUI is that if we add a nil
component to our view, it won't show up, it's just ignored. So, what's the easiest way to convert a conditional string into a conditional Text
? That's right, using map
.
Here we map
over the possibly nil
address, without needing to force unwrap it, as the closure will only be executed if there was a value in the first place. This, however, seems like a hack. But hey, It works.
Let's wait and see
As I was reading some discussions about this issue, I found out that SwiftUI builds views using a struct named ViewBuilder
— remember the error I was getting before? If we peek its definition (cmd
+ shift
+ o
in XCode to go to any class definition) we can clearly see that it supports if
statements, but nothing more.
SwiftUI is not event a year old, and we know Apple only introduces big features once a year. So here's hoping that when the next version of SwiftUI shows up, they add if let
support.
Top comments (3)
if let
– is from another paradigm.The sense of SwiftUI goes to declarative approach, but not into imperative, which is classic.
If you're saying that
if let
isn't supported in SwiftUI because it's not declarative, then why is the normalif
supported? It's even used in the official documentation. To me, both share the same level of "imperativeness", but theif
is not as type-safe in this case!It's clear that SwiftUI it's inspired by other declarative frameworks like React.js or Vue.js, and in those is idiomatic to check if values are present to conditionally render a view. Something like this:
Here, since we are checking that
library.address
has a value, TypeScript smart casts it from astring?
to a normalstring
. It's the TS equivalent of anif let
binding.I wouldn't use React as example of correctness, as it's also not a mature technology.
In case of SwiftUI, I think, they will propose appropriate usage for cases that you aware of.