Understanding the swift language and its documentation

Hi, I am inexperienced hobbyist programmer in the Xcode environment and the swift language.

I have a very basic question: I read the documentation about one of the NavigationSplitView initializers:

init(sidebar: () -> Sidebar, detail: () -> Detail)

I created two basic views the SidebarView and the DetailsView.

The following code, use the views above as parameters but gives errors, and I cannot understand why.

import SwiftUI

struct ContentView: View {
  var body: some View {
    NavigationSplitView(sidebar: SidebarView(), detail: DetailsView())
  }
}

#Preview {
    ContentView()
}

I am interested to understand how the swift language works, and how to implement the documentation of swiftUI in real code.

Is there any way to use the views as parameters and not in brackets?

import SwiftUI

struct ContentView: View {
  var body: some View {
    NavigationSplitView {
      SidebarView()
    } detail: {
      DetailsView()
    }
  }
}

#Preview {
    ContentView()
}
Answered by ssmith_c in 775417022

Hi. I'm glad you asked this because I'm constantly tripping over SwiftUI syntax myself. There are a bunch of ways to express the contents of your NavigationSplitView.

Note that SwiftUI is not Swift. It looks like Swift, it is written in Swift, but it is a domain specific language for creating descriptions of user interfaces. It extends the Swift language.

If you look at the documentation for NavigationSplitView in Xcode's Documentation viewer, you'll find

struct NavigationSplitView<Sidebar, Content, Detail> where Sidebar : View, Content : View, Detail: View

and if you click on View, you'll find that it is a protocol.

The initializer you are using is the one which creates a two-column view, with no control over the visibility of the columns

public init(@ViewBuilder sidebar: () -> Sidebar, @ViewBuilder detail: () -> Detail) where Content == EmptyView

The parameters (labelled sidebar and detail here) are closures - functions returning structs conforming to the View protocol.

my SidebarView and DetailView are Views defined like this

struct SidebarView: View {
    var body: some View {
        Text("sidebar")
    }
}

struct DetailView: View {
    var body: some View {
        Text("detail")
    }
}

And here are functions which return these structs

func DetailViewFunc() -> DetailView {
    return DetailView()
}

func SidebarViewFunc() -> SidebarView {
    return SidebarView()
}

we can use these functions as parameter values:

struct ContentView: View {
    var body: some View {
        NavigationSplitView ( sidebar: SidebarViewFunc , detail: DetailViewFunc)
    }
}

that's a bit verbose, but it does look like a regular function call with a parameter list.

More succinctly, you can just write the functions directly in the parameter list, without names. You can omit the return keyword because of the use of @ViewBuilder in the declaration of the NavigationSplitView initializer. You don't need SidebarViewFunc or DetailViewFunc any more. Usually, separate closures go on separate lines:

struct ContentView: View {
    var body: some View {
        NavigationSplitView ( 
            sidebar: { SidebarView() },
            detail: { DetailView() }
        )
    }
}

This works fine, but most code examples won't look like this. The Swift developers were very proud of how much typing you don't have to do. If the last parameter to a function is a closure, you can omit its name and move it outside the parentheses:

struct ContentView: View {
    var body: some View {
        NavigationSplitView (
            sidebar: { SidebarView() } )
        { DetailView() }
    }
}

this works too. However SwiftUI does quite a lot with closures, often views take multiple closure parameters, so some people thought it would be cool to extend this syntax to multiple trailing closures. See https://github.com/apple/swift-evolution/blob/main/proposals/0279-multiple-trailing-closures.md for more context. The rule here is that the first trailing closure can be unnamed, while the subsequent ones must be named. Which has the effect here of removing the visible "sidebar" label and reinstating the "detail" label. But because all the parameters are closures and were moved out of the parentheses, we can omit the parentheses, grounds for rejoicing in some circles. So now the ContentView can look like this:

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            SidebarView() }
        detail: {
            DetailView() }
    }
}

Xcode's code completion suggestions suggest the explicitly labelled parameter version, not the multiple trailing closure syntax version, which is unhelpful. And if you try to translate from Xcode's suggestion to trailing closure syntax, and forget to delete a comma, you'll see five compiler errors, none of which tell you that you have a comma which shouldn't be there.

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            SidebarView() }, //  added (and unnecessary) comma
        detail: {
            DetailView() }
    }
}

if that bothers you, I'd encourage you to file a bug. Swift error reporting has improved since the beginning, but the language has also become more and more complex.

Stick at it. Eventually you'll get used to it, and know what to look for when Swift kicks out an error you cannot understand.

Accepted Answer

Hi. I'm glad you asked this because I'm constantly tripping over SwiftUI syntax myself. There are a bunch of ways to express the contents of your NavigationSplitView.

Note that SwiftUI is not Swift. It looks like Swift, it is written in Swift, but it is a domain specific language for creating descriptions of user interfaces. It extends the Swift language.

If you look at the documentation for NavigationSplitView in Xcode's Documentation viewer, you'll find

struct NavigationSplitView<Sidebar, Content, Detail> where Sidebar : View, Content : View, Detail: View

and if you click on View, you'll find that it is a protocol.

The initializer you are using is the one which creates a two-column view, with no control over the visibility of the columns

public init(@ViewBuilder sidebar: () -> Sidebar, @ViewBuilder detail: () -> Detail) where Content == EmptyView

The parameters (labelled sidebar and detail here) are closures - functions returning structs conforming to the View protocol.

my SidebarView and DetailView are Views defined like this

struct SidebarView: View {
    var body: some View {
        Text("sidebar")
    }
}

struct DetailView: View {
    var body: some View {
        Text("detail")
    }
}

And here are functions which return these structs

func DetailViewFunc() -> DetailView {
    return DetailView()
}

func SidebarViewFunc() -> SidebarView {
    return SidebarView()
}

we can use these functions as parameter values:

struct ContentView: View {
    var body: some View {
        NavigationSplitView ( sidebar: SidebarViewFunc , detail: DetailViewFunc)
    }
}

that's a bit verbose, but it does look like a regular function call with a parameter list.

More succinctly, you can just write the functions directly in the parameter list, without names. You can omit the return keyword because of the use of @ViewBuilder in the declaration of the NavigationSplitView initializer. You don't need SidebarViewFunc or DetailViewFunc any more. Usually, separate closures go on separate lines:

struct ContentView: View {
    var body: some View {
        NavigationSplitView ( 
            sidebar: { SidebarView() },
            detail: { DetailView() }
        )
    }
}

This works fine, but most code examples won't look like this. The Swift developers were very proud of how much typing you don't have to do. If the last parameter to a function is a closure, you can omit its name and move it outside the parentheses:

struct ContentView: View {
    var body: some View {
        NavigationSplitView (
            sidebar: { SidebarView() } )
        { DetailView() }
    }
}

this works too. However SwiftUI does quite a lot with closures, often views take multiple closure parameters, so some people thought it would be cool to extend this syntax to multiple trailing closures. See https://github.com/apple/swift-evolution/blob/main/proposals/0279-multiple-trailing-closures.md for more context. The rule here is that the first trailing closure can be unnamed, while the subsequent ones must be named. Which has the effect here of removing the visible "sidebar" label and reinstating the "detail" label. But because all the parameters are closures and were moved out of the parentheses, we can omit the parentheses, grounds for rejoicing in some circles. So now the ContentView can look like this:

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            SidebarView() }
        detail: {
            DetailView() }
    }
}

Xcode's code completion suggestions suggest the explicitly labelled parameter version, not the multiple trailing closure syntax version, which is unhelpful. And if you try to translate from Xcode's suggestion to trailing closure syntax, and forget to delete a comma, you'll see five compiler errors, none of which tell you that you have a comma which shouldn't be there.

struct ContentView: View {
    var body: some View {
        NavigationSplitView {
            SidebarView() }, //  added (and unnecessary) comma
        detail: {
            DetailView() }
    }
}

if that bothers you, I'd encourage you to file a bug. Swift error reporting has improved since the beginning, but the language has also become more and more complex.

Stick at it. Eventually you'll get used to it, and know what to look for when Swift kicks out an error you cannot understand.

Thank you very much for such a detailed answer. I was afraid that, due to my poor skills in writing English, my post will not be understandable. But you got the point of my question. Since I have good knowledge of C & C++ I usually try to interpret SwiftUI to these languages but I think it is not a good practice, because there are no straight equivalents. From this point of view, SwiftUI (and Swift) syntax looks to my eyes a bit chaotic. Now it is very clear to me and I'll explore the SwiftUI documentation much easier

Understanding the swift language and its documentation
 
 
Q