Peter Schumacher
Peter Schumacher

Reputation: 441

Why does compiling SwiftUI modifier without leading point compile?

By accident i added a modifier to my swift UI view without a leading point. And it compiled. And i can not wrap my head around why i did that. Here some example Code:

    struct ContentView: View {
        var body: some View {
            NavigationView {
                NavigationLink(
                    destination: DetailView(),
                    label: {
                        Text("Goto Detail")
                    })
                    navigationTitle("ContentView")
            }
            
        }
    }
    
    struct DetailView: View {
        var body: some View {
            Text("Detail View")
                .padding()
                navigationTitle("Detail")
        }
    }

Somehow even stranger is that the first "wrong" modifier navigationTitle("ContentView") does just nothing. The second one navigationTitle("Detail") lets the App crash when navigating to the View during runtime.

Similar to this is

struct DetailView: View {
    var body: some View {
        padding()
    }
}

This View does compile but just crashes, if tried to shown with Previews. And it just can't be navigated to.

I would really expect this code to not even compile. Is somebody able to explain this?

Upvotes: 0

Views: 48

Answers (1)

Sweeper
Sweeper

Reputation: 271175

If you refer to a method by its simple name (e.g. navigationTitle), it's kind of like saying self.navigationTitle:

struct DetailView: View {
    var body: some View {
        Text("Detail View")
            .padding()
        self.navigationTitle("Detail")
    }
}

This is valid, because self is also a View, and that modifier is available on all Views. It gives you a new view that is self, but with a navigation title of Detail. And you are using both of them as the body. Normally you can't return multiple things from a property, but it works here because the body protocol requirement is marked @ViewBuilder.

Of course, you are using self as the self's body, and you can't have self referencing views, so it fails at runtime.

If you add self. to the other cases, it's pretty easy to understand why they compile:

struct DetailView: View {
    var body: some View {
        self.padding() // self conforms to View, so you can apply padding()
        // padding() returns another View, so it's valid to return here
    }
}

"The body of DetailView is itself, but with some padding."

NavigationView {
    NavigationLink(
        destination: DetailView(),
        label: {
            Text("Goto Detail")
        })
    self.navigationTitle("ContentView")
}

"The navigation view consists of a navigation link, and ContentView itself (what self means here) with a navigation title of ContentView"

This doesn't crash immediately either because NavigationView's initialiser is smart enough to ignore the junk ContentView that you have given it, or because there is an extra level of indirect-ness to the self-referencing. I don't think we can know exactly why it crashes/doesn't crash immediately, until SwiftUI becomes open source.

Upvotes: 2

Related Questions