Displaying an empty view in SwiftUI

Viewed 19165

In SwiftUI there's frequently a need to display an "empty" view based on some condition, e.g.:

struct OptionalText: View {
  let text: String?

  var body: some View {
    guard let text = text else { return }

    return Text(text) 
  }
}

Unfortunately, this doesn't compile since the body of guard has to return some view, that is an "empty" view when text is nil. How should this example be rewritten so that it compiles and renders an "empty" view when text is nil?

4 Answers

You have to return something. If there is some condition where you want to display nothing, "display" an...EmptyView ;)

var body: some View {
    Group {
        if text != nil {
            Text(text!)
        } else {
            EmptyView()
        }
    }
}

The SwiftUI DSL will require you to wrap the if/else in a Group and the DSL has no guard/if let nomenclature.

As of Xcode 12 beta 2 the Group view is no longer needed and if let declarations are supported, so the resulting body can be a bit more succint:

var body: some View {
    if let text = text {
        Text(text)
    } else {
        EmptyView()
    }
}

You can use the @ViewBuilder. Then you don't even need an EmptyView:

@ViewBuilder
var body: some View {
    if let text = text {
        Text(text)
    }
}

Note that here you use @ViewBuilder to just build your view. If you want to learn more about how it's done behind the scenes, please see the below answer:

Here's the shortest version of your code (if let shorthand declared for Swift 5.7+) that you can use in Xcode 14.0 and higher. This kind of optional binding shadows an existing variable.

struct ContentView: View {       
    @State var text: String? = nil

    var body: some View {   
        if let text {
            Text(text)
        }
    }
}

If you choose to use the guard let shorthand statement, implement this version:

struct ContentView: View {
    
    @State var text: String? = nil

    var body: some View {
        nonOptionalText(text)
    }
    
    func nonOptionalText(_ text: String?) -> some View {
        guard let text else { return Text("") }
        return Text(text)
    }
}
Related