Local vars in SwiftUI builders?

Just watched the first SwiftUI WWDC talk. I tried modifying the demo code as shown below. Compilation fails with: Closure containing a declaration cannot be used with function builder 'ViewBuilder'

You can see what I'm trying to do — wrap the cell structure in a Link, or not. This pattern seems useful. Will local variables like this eventually work?

var body: some View {
    NavigationView {
        List(things) { thing in
            let cell = HStack {  // <-- first error here
                Image(systemName: "photo")
                VStack(alignment: .leading) {
                    Text(thing.name)
                    Text("Foo")
                }
            }
            if thing.navable {
                NavigationLink(destination: Text("foo")) { cell }                        
            } else {
                cell
            }
        }
1 Like

You could refactor the cell into it's own View class and thus avoid the declaration of the variable.

You're also already 7 layers deep in your indentation, so that's a good indication something in there could be it's own View

That helps, thanks. Any idea why this works:

    var body: some View {
        return NavigationView {
            Text("hi")
        }
    }

But this doesn't:

    var body: some View {
        return NavigationView {
            let t = Text("hi")
            return t
        }
    }

It says: Cannot convert return expression of type 'NavigationView<_>' to return type 'some View'.

Okay, so NavigationView doesn't adopt View? Then how does it work in the first case?

Rob

1 Like

This is just a property return. As long as you meet the single type requirement of the some keyword, it's just like any computed property return.

This, however, is using a builder closure for the NavigationView's view. It fails to compile since the separate property is incompatible with the builder closure syntax. Unfortunately the error message is misleading.

Aren't both examples using the "builder closure"? The builder closure is this content parameter to the init of NavigationView, no?

// in public struct NavigationView...
public init(content: () -> Content)

Maybe I'm lost. I thought that content arg would be marked with a @ViewBuilder attribute.

Sorry, yes, I was looking at the wrong thing. They're both the same NavigationView call, but the closure is a @ViewBuilder and so doesn't support inline variables. I'm not sure why it's not showing up in the module definition, but none of the SwiftUI views do, and they're all @ViewBuilder closures.

Okay, the function builders proposal says "Local declarations are left alone by the transformation.", so that sounds like this will work, eventually, and I'm just hitting this problem because this is half-done beta software. Do you agree?

1 Like