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
            }
        }

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

Hi,

this is nothing SwiftUI specific. This is because of SE-225. Swift can't infer the return statement.

// works
var body: some View {
    HStack {
        Text("Hello")
        Text("World!")
    }
}

// does not work
var body: some View {
    let stack = HStack {
        Text("Hello")
        Text("World!")
    }
}

// works
var body: some View {
    let stack = HStack {
        Text("Hello")
        Text("World!")
    }
    return stack
}

// This will just work with the return keywords.
var body: some View {
    let isMagic = someMagicBool
    if isMagic {
        return Text("Magic happens here!")
    } else {
        return Text("Sorry, no magic left for today!")
    }
}

Hope it helps.
Have fun using SwiftUI!

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

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?

Terms of Service

Privacy Policy

Cookie Policy