2

I'm a Java developer trying to learn Swift/SwiftUI. I am going through the Apple tutorial for SwiftUI (https://developer.apple.com/tutorials/swiftui/creating-and-combining-views) In it they have the following code snippet:

import SwiftUI

@main
struct LandmarksApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

I am trying to understand this portion:

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }

I understand that it creates a new variable called body of type Scene. I know that you can instantiate objects like this: var x: String = "test" but I'm not familiar with the syntax. Does that create a new object of type Scene?

I have gone through this but I couldn't find this syntax on there.

Christophe
  • 68,716
  • 7
  • 72
  • 138
areddy11
  • 31
  • 2
  • It is a "Closure" and Yes it returns a `Scene`. You will use `some View` quite a bit more than `some Scene` in SwiftUI. Computed Properties work similarly where you can setup custom getters and setters. If you look at the first line in your code you see it again `struct LandmarksApp: App {` – lorem ipsum Dec 20 '20 at 20:48
  • For your question about instantiate objects, it's a closure. Because you have a java background, I will link to a kotlin doc that explains lambda expressions: https://kotlinlang.org/docs/reference/lambdas.html#lambda-expressions-and-anonymous-functions – Rickard Elimää Dec 20 '20 at 20:55
  • 1
    I did that tutorial a a couple of months ago. Just read through everything and accept what you don't understand, or read through half of this documentation (to Optional Chaining) so you at least heard of a few of the things presented in the tutorial: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html Read Type Casting -> Generics when you done with the tutorial. – Rickard Elimää Dec 20 '20 at 21:04
  • I think the braces after the word Scene indicate a computed property. – AceN May 12 '21 at 14:40

1 Answers1

5

This syntax is indeed hard nut to crack for Swift novices. It involves implicit getters, constructors, several kind of closure shortcuts, implicit return types, generic type inference and opaque types. Let's go step by step.

Getter syntax for computed properties

LandmarksApp conforms to the App protocol. This requires the definition of a body property of a type complying with the Scene protocol. Moreover, we are told in the documentation that the body is expected to be a read-only computed property.

The general syntax of a computed property's getter is:

var X : T {
    get { ... }
    ...
}

But if a it's a read-only propery, a shortcut may be used: put the getter code in a block directly after the type and without the get:

var CX : T { ... }   // that's a computed property with its getter

Moreover, you do not need a return if the getter code is made of a single expression: the return is implicit.

WindowGroup constructor using a closure

WindowsGroup is the type we are going to use. It complies with Scene. It is a generic type based on the type parameter Content that conforms to the View protocol, and we have a ContentView for that. So here the first try:

struct LandmarksApp: App {
    var body : WindowGroup<ContentView> {      // read-only getter 
        WindowGroup<ContentView>(content: myhelper)  // return a newly constructed object 
    }
}

The constructor for WindowGroup<T> requires as argument a function that takes no argument and returns a T. To start simple, an ordinary function was used. It's defined as follows:

func myhelper() -> ContentView {
    return ContentView()
}

Simplification based on closures and generics

We can simplify with a closure to replace the helper function:

var body : WindowGroup<ContentView> {
    WindowGroup<ContentView>(content: { ()->ContentView in return ContentView() })
}

Swift can infer the types used in the closure from the context and it also allows an implicit return for closures made of a single expression. In absence of parameters, we remove in. All those simplification lead to:

var body : WindowGroup<ContentView> {
    WindowGroup<ContentView>(content: { ContentView() })
}

Swift is also able to infer the generic type from the context. So this expression could be simplified to:

var body : WindowGroup<ContentView> {
    WindowGroup (content: { ContentView() })
}

Last but not least, the most tricky part: Swift allows a special syntax if the closure is the last argument of a function. It's called trailing closures. This leads to:

var body : WindowGroup<ContentView> {
    WindowGroup () { ContentView() } 
}

This syntax also allows to get rid of the empty parenthesis:

var body : WindowGroup<ContentView> {
    WindowGroup { ContentView() } 
}

Opaque types

Until now, we used a body of a concrete well-known type. But couldn't the type of this variable be inferred? It would thus be tempting to write:

var body {  // OUCH !!!
    WindowGroup { ContentView() } 
}

But Swift requires computed properties to have an explicit type. To keep it as general as possible but ensure compliance with the required protocol, an opaque type may be used:

var body : some Scene {
    WindowGroup { ContentView() }
}

More on opaque types in this question.

Wrap up

enter image description here

Christophe
  • 68,716
  • 7
  • 72
  • 138