# SwiftUI Essentials

[SwiftUI](https://lochiwei.gitbook.io/ios/swiftui) ⟩ [intro](https://lochiwei.gitbook.io/ios/swiftui/intro) ⟩ SwiftUI Essentials

{% tabs %}
{% tab title="🖥️ 影片" %}
{% embed url="<https://developer.apple.com/videos/play/wwdc2024/10150/>" %}
SwiftUI Esssentials (WWDC2024)
{% endembed %}
{% endtab %}

{% tab title="🔴 主題" %}

| 時間╱主題                                                                                                             | 程式碼                                                          |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| 2:30 - [Declarative views](https://developer.apple.com/videos/play/wwdc2024/10150/?time=150)                      | :point\_right: [2:30](#id-2-30) tab                          |
| 2:43 - [Declarative views: layout](https://developer.apple.com/videos/play/wwdc2024/10150/?time=163)              | :point\_right: [2:43](#id-2-43) tab                          |
| 2:56 - [Declarative views: list](https://developer.apple.com/videos/play/wwdc2024/10150/?time=176)                | :point\_right: [Pet](#pet), [ContentView](#contentview) tabs |
| 3:07 - [Declarative views: list](https://developer.apple.com/videos/play/wwdc2024/10150/?time=187)                | :point\_right: [Pet](#pet), [ContentView](#contentview) tabs |
| 4:24 - [Declarative and imperative programming](https://developer.apple.com/videos/play/wwdc2024/10150/?time=264) | :point\_right: [Pet](#pet), [ContentView](#contentview) tabs |
| 5:33 - [Layout container](https://developer.apple.com/videos/play/wwdc2024/10150/?time=333)                       | :point\_right: [5:33](#id-5-33) tab                          |
| 5:41 - [Container views](https://developer.apple.com/videos/play/wwdc2024/10150/?time=341)                        | :point\_right: [Pet](#pet), [ContentView](#contentview) tabs |
| 6:23 - [View modifiers](https://developer.apple.com/videos/play/wwdc2024/10150/?time=383)                         | :point\_right: [Pet](#pet), [ContentView](#contentview) tabs |
| 7:14 - [Custom views](https://developer.apple.com/videos/play/wwdc2024/10150/?time=434)                           | :point\_right: [PetRowView](#petrowview), [Pet](#pet) tabs   |
| 7:20 - [Custom views: iteration](https://developer.apple.com/videos/play/wwdc2024/10150/?time=440)                | :point\_right: [PetRowView](#petrowview), [Pet](#pet) tabs   |
| 7:24 - [Custom views: view properties](https://developer.apple.com/videos/play/wwdc2024/10150/?time=444)          | :point\_right: [PetRowView](#petrowview), [Pet](#pet) tabs   |
| 7:34 - [Custom views: complete row view](https://developer.apple.com/videos/play/wwdc2024/10150/?time=454)        | :point\_right: [PetRowView](#petrowview) tab                 |
| 7:41 - [Custom views: input properties](https://developer.apple.com/videos/play/wwdc2024/10150/?time=461)         |                                                              |
| 7:53 - [Custom views: reuse](https://developer.apple.com/videos/play/wwdc2024/10150/?time=473)                    |                                                              |
| {% endtab %}                                                                                                      |                                                              |

{% tab title="📘 手冊" %}

* [SwiftUI Essentials](https://developer.apple.com/videos/play/wwdc2024/10150/) (WWDC2024)
  {% endtab %}
  {% endtabs %}

{% tabs %}
{% tab title="2:30" %}
2:30 - [Declarative views](https://developer.apple.com/videos/play/wwdc2024/10150/?time=150)

```swift
// declarative views
Text("Whiskers")
Image(systemName: "cat.fill")
Button("Give Treat") { /* action */ }
```

{% endtab %}

{% tab title="2:43" %}
2:43 - [Declarative views: layout](https://developer.apple.com/videos/play/wwdc2024/10150/?time=163)

```swift
// layout (container view)
HStack {
   Label("Whiskers", systemImage: "cat.fill")
   Spacer()
   Text("Tightrope walking")
}
```

{% endtab %}

{% tab title="5:33" %}
5:33 - [Layout container](https://developer.apple.com/videos/play/wwdc2024/10150/?time=333)

```swift
// container view (5:33)
HStack {
   Label("Whiskers", systemImage: "cat.fill")
   Spacer()
   Text("Tightrope walking")
}
```

{% endtab %}

{% tab title="7:53" %}
7:53 - [Custom views: reuse](https://developer.apple.com/videos/play/wwdc2024/10150/?time=473)&#x20;

```swift
PetRowView(pet: model.pet(named: "Whiskers"))
PetRowView(pet: model.pet(named: "Roofus"))
PetRowView(pet: model.pet(named: "Bubbles"))
```

{% endtab %}
{% endtabs %}

## Views

{% tabs %}
{% tab title="Views" %}

* ContentView
* PetRowView
* RatingView
* RatingContainerView
* PetListView
  {% endtab %}

{% tab title="ContentView" %}
{% tabs %}
{% tab title="2:56" %}

```swift
struct ContentView: View {

    // view state
    @State private var pets = Pet.samplePets

    var body: some View {
        List(pets) { pet in        // scrollable list
            HStack {
                // 固定的內容
                Label("Whiskers", systemImage: "cat.fill")
                Spacer()
                Text("Tightrope walking")
            }
        }
    }
}
```

{% endtab %}

{% tab title="3:07" %}

```swift
struct ContentView: View {

    // view state
    @State private var pets = Pet.samplePets

    var body: some View {
        List(pets) { pet in
            HStack {
                // 內容由 pet 決定
                Label(pet.name, systemImage: pet.kind.systemImage)
                Spacer()
                Text(pet.trick)
            }
        }
    }
}sw
```

{% endtab %}

{% tab title="4:42" %}

```swift
struct ContentView: View {

    // view state
    @State private var pets = Pet.samplePets    

    var body: some View {
    
        // action button
        Button("Add Pet") {
            // imperative code (4:24)
            pets.append(Pet("Toby", kind: .dog, trick: "WWDC Presenter"))
        }

        // declarative views (2:30)
        List(pets) { pet in
            HStack {
                Label(pet.name, systemImage: pet.kind.systemImage)
                Spacer()
                Text(pet.trick)
            }
        }
        
    }// body
}
```

{% endtab %}

{% tab title="5:41" %}

```swift
struct ContentView: View {
    var body: some View {
    
        // container view (5:41)
        HStack {                             
            Image(whiskers.profileImage)
            
            // container view (5:41)
            VStack(alignment: .leading) {    
                Label("Whiskers", systemImage: "cat.fill")
                Text("Tightrope walking")
            }
            
            Spacer()
        }
        
    }
}
```

{% endtab %}

{% tab title="6:23" %}

```swift
struct ContentView: View {
    var body: some View {
        Image(whiskers.profileImage)
            .clipShape(.circle)        // view modifier
            .shadow(radius: 3)         // view modifier
            .overlay {                 // view modifier
                Circle().stroke(.green, lineWidth: 2)
            }
    }
}
```

{% endtab %}

{% tab title="7:59" %}

```swift
struct ContentView: View {
    var model: PetStore
    var body: some View {
        List(model.allPets) { pet in    // List
            PetRowView(pet: pet)
        }
    }
}
```

{% endtab %}

{% tab title="8:14" %}

```swift
struct ContentView: View {
    var model: PetStore
    var body: some View {
        List {
            ForEach(model.allPets) { pet in    // ForEach
                PetRowView(pet: pet)
            }
        }
    }
}
```

{% endtab %}

{% tab title="8:27" %}

```swift
struct ContentView: View {
    var model: PetStore
    var body: some View {
        List {
            // ⭐️ List composition: sections
            Section("My Pets") {
                ForEach(model.myPets) { pet in
                    PetRowView(pet: pet)
                }
            }
            Section("Other Pets") {
                ForEach(model.otherPets) { pet in
                    PetRowView(pet: pet)
                }
            }
        }
    }
}
```

{% endtab %}

{% tab title="9:31" %}

```swift
struct ContentView: View {

    var model: PetStore
    
    var body: some View {
        List {
            Section("My Pets") {
                ForEach(model.myPets) { pet in
                    row(pet: pet)
                }
            }
            Section("Other Pets") {
                ForEach(model.otherPets) { pet in
                    row(pet: pet)
                }
            }
        }
    }

    // ⭐️ private view prperty
    private func row(pet: Pet) -> some View {
        PetRowView(pet: pet)
            // ⭐️ row action 
            .swipeActions(edge: .leading) {
                Button("Award", systemImage: "trophy") {
                    pet.giveAward()
                }
                .tint(.orange)

                ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
            }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="PetRowView" %}
{% tabs %}
{% tab title="7:20" %}

```swift
// PetRowView
struct PetRowView: View {
    var body: some View {
        HStack {
        
            Image(whiskers.profileImage)
                .clipShape(.circle)
                .shadow(radius: 3)
                .overlay {
                    Circle()
                        .stroke(.green, lineWidth: 2)
                }

            Text("Whiskers")

            Spacer()
        }
    }
}
```

{% endtab %}

{% tab title="7:24" %}

```swift
// PetRowView (custom view)(7:24, 7:34)
struct PetRowView: View {

    var body: some View {
        HStack {                // container view (5:33)
            
            // ⭐️ pet image
            profileImage        // private view property (7:24)
 
            // pet name + trick
            VStack(alignment: .leading) {    // container view (5:41)
                Text("Whiskers")
                Text("Tightrope walking")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }

            Spacer()
        }
    }

    // ⭐️ private view property (7:24)
    private var profileImage: some View {
        Image(whiskers.profileImage)
            .clipShape(.circle)        // view modifier (6:23)
            .shadow(radius: 3)         // view modifier
            .overlay {                 // view modifier
                Circle().stroke(.green, lineWidth: 2)
            }
    }
}
```

{% endtab %}

{% tab title="7:41" %}

```swift
// PetRowView(pet: pet)
struct PetRowView: View {
    
    // ⭐️ input property
    var pet: Pet
    
    var body: some View {
        HStack {
        
            profileImage

            VStack(alignment: .leading) {
                Text(pet.name)
                Text(pet.trick)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }

            Spacer()
        }
    }

    // private view property
    private var profileImage: some View {
        Image(pet.profileImage)
            .clipShape(.circle)
            .shadow(radius: 3)
            .overlay {
                Circle().stroke(pet.favoriteColor, lineWidth: 2)
            }
    }
}
```

{% endtab %}

{% tab title="9:31" %}

```swift
struct PetRowView: View {

    var pet: Pet
    
    var body: some View {
        HStack {
            profileImage

            VStack(alignment: .leading) {
                HStack(alignment: .firstTextBaseline) {
                    Text(pet.name)

                    // ⭐️ conditional view
                    if pet.hasAward {
                        Image(systemName: "trophy.fill")
                            .foregroundStyle(.orange)
                    }
                }
                Text(pet.trick)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }

            Spacer()
        }
    }

    private var profileImage: some View {
        Image(pet.profileImage)
            .clipShape(.circle)
            .shadow(radius: 3)
            .overlay {
                Circle().stroke(pet.favoriteColor, lineWidth: 2)
            }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="RatingView" %}
{% tabs %}
{% tab title="10:57" %}
10:57 - [State changes](https://developer.apple.com/videos/play/wwdc2024/10150/?time=657)

```swift
struct RatingView: View {

    // ⭐️ internal state
    @State var rating: Int = 5

    var body: some View {
        HStack {
        
            // ⭐️ button (-)
            Button("Decrease", systemImage: "minus.circle") {
                rating -= 1
            }
            .disabled(rating == 0)
            .labelStyle(.iconOnly)

            // ⭐️ 
            Text(rating, format: .number.precision(.integerLength(2)))
                .font(.title.bold())

            // ⭐️ button (+)
            Button("Increase", systemImage: "plus.circle") {
                rating += 1
            }
            .disabled(rating == 10)
            .labelStyle(.iconOnly)
        }
    }
}
```

{% endtab %}

{% tab title="11:51" %}
11:51 - [State changes: animation](https://developer.apple.com/videos/play/wwdc2024/10150/?time=711)

```swift
struct RatingView: View {

    @State var rating: Int = 5

    var body: some View {
        HStack {
            Button("Decrease", systemImage: "minus.circle") {
                withAnimation {    // ⭐️ animation
                    rating -= 1
                }
            }
            .disabled(rating == 0)
            .labelStyle(.iconOnly)

            Text(rating, format: .number.precision(.integerLength(2)))
                .font(.title.bold())

            Button("Increase", systemImage: "plus.circle") {
                withAnimation {    // ⭐️ animation
                    rating += 1
                }
            }
            .disabled(rating == 10)
            .labelStyle(.iconOnly)
        }
    }
}
```

{% endtab %}

{% tab title="12:05" %}
12:05 - [State changes: text content transition](https://developer.apple.com/videos/play/wwdc2024/10150/?time=725)

```swift
struct RatingView: View {

    @State var rating: Int = 5

    var body: some View {
        HStack {
            Button("Decrease", systemImage: "minus.circle") {
                withAnimation {
                    rating -= 1
                }
            }
            .disabled(rating == 0)
            .labelStyle(.iconOnly)

            Text(rating, format: .number.precision(.integerLength(2)))
                // ⭐️ text content transition
                .contentTransition(.numericText(value: Double(rating)))
                .font(.title.bold())

            Button("Increase", systemImage: "plus.circle") {
                withAnimation {
                    rating += 1
                }
            }
            .disabled(rating == 10)
            .labelStyle(.iconOnly)
        }
    }
}
```

{% endtab %}

{% tab title="12:45" %}
12:45 - [State changes: state and binding](https://developer.apple.com/videos/play/wwdc2024/10150/?time=765)

```swift
struct RatingView: View {

    // ⭐️ external binding
    @Binding var rating: Int

    var body: some View {
        HStack {
            Button("Decrease", systemImage: "minus.circle") {
                withAnimation {
                    rating -= 1
                }
            }
            .disabled(rating == 0)
            .labelStyle(.iconOnly)

            Text(rating, format: .number.precision(.integerLength(2)))
                .contentTransition(.numericText(value: Double(rating)))
                .font(.title.bold())

            Button("Increase", systemImage: "plus.circle") {
                withAnimation {
                    rating += 1
                }
            }
            .disabled(rating == 10)
            .labelStyle(.iconOnly)
        }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="RatingContainerView" %}
{% tabs %}
{% tab title="12:22" %}
12:22 - [State changes: multiple state](https://developer.apple.com/videos/play/wwdc2024/10150/?time=742)\
12:45 - [State changes: state and binding](https://developer.apple.com/videos/play/wwdc2024/10150/?time=765)

```swift
struct RatingContainerView: View {

    // ⭐️ internal state
    @State private var rating: Int = 5

    var body: some View {
        Gauge(value: Double(rating), in: 0...10) {
            Text("Rating")
        }
        RatingView()
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="PetListView" %}
{% tabs %}
{% tab title="15:19" %}
15:19 - [Searchable](https://developer.apple.com/videos/play/wwdc2024/10150/?time=919)

```swift
struct PetListView: View {

    // ⭐️ 
    @Bindable var viewModel: PetStoreViewModel

    var body: some View {
        List {
            Section("My Pets") {
                ForEach(viewModel.myPets) { pet in
                    row(pet: pet)
                }
            }
            Section("Other Pets") {
                ForEach(viewModel.otherPets) { pet in
                    row(pet: pet)
                }
            }
        }
        // ⭐️ searchable
        .searchable(text: $viewModel.searchText)
    }

    private func row(pet: Pet) -> some View {
        PetRowView(pet: pet)
            .swipeActions(edge: .leading) {
                Button("Reward", systemImage: "trophy") {
                    pet.giveAward()
                }
                .tint(.orange)

                ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
            }
    }
}
```

{% endtab %}

{% tab title="15:20" %}
15:20 - [Searchable: customization](https://developer.apple.com/videos/play/wwdc2024/10150/?time=920)

```swift
struct PetListView: View {

    @Bindable var viewModel: PetStoreViewModel

    var body: some View {
        List {
            Section("My Pets") {
                ForEach(viewModel.myPets) { pet in
                    row(pet: pet)
                }
            }
            Section("Other Pets") {
                ForEach(viewModel.otherPets) { pet in
                    row(pet: pet)
                }
            }
        }
        // ⭐️ searchable: customization
        .searchable(text: $viewModel.searchText, editableTokens: $viewModel.searchTokens) { $token in
            Label(token.kind.name, systemImage: token.kind.systemImage)
        }
        // ⭐️ searchable: customization
        .searchScopes($viewModel.searchScope) {
            Text("All Pets").tag(PetStoreViewModel.SearchScope.allPets)
            Text("My Pets").tag(PetStoreViewModel.SearchScope.myPets)
            Text("Other Pets").tag(PetStoreViewModel.SearchScope.otherPets)
        }
        // ⭐️ searchable: customization
        .searchSuggestions {
            PetSearchSuggestions(viewModel: viewModel)
        }
    }

    private func row(pet: Pet) -> some View {
        PetRowView(pet: pet)
            .swipeActions(edge: .leading) {
                Button("Reward", systemImage: "trophy") {
                    pet.giveAward()
                }
                .tint(.orange)

                ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
            }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

## Data Types

{% tabs %}
{% tab title="Types" %}

* Pet
* PetStore
* PetStoreViewModel
  {% endtab %}

{% tab title="Pet" %}
{% tabs %}
{% tab title="2:56" %}

```swift
// ⭐️ whiskers (global constant)
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")

// ⭐️ Pet
struct Pet: Identifiable {

    // -------- ⭐️ Pet.Kind --------
    enum Kind {
    
        // enum cases
        case cat
        case dog
        case fish
        case bird
        case lizard
        case turtle
        case rabbit
        case bug

        // ⭐️ pet.kind.systemImage
        var systemImage: String {
            switch self {
                case .cat: return "cat.fill"
                case .dog: return "dog.fill"
                case .fish: return "fish.fill"
                case .bird: return "bird.fill"
                case .lizard: return "lizard.fill"
                case .turtle: return "tortoise.fill"
                case .rabbit: return "rabbit.fill"
                case .bug: return "ant.fill"
            }
        }
        
    }// enum Kind

    // -------- pet properties --------
    let id = UUID()            // pet.id ⭐️ 
    var name: String           // pet.name
    var kind: Kind             // pet.kind
    var trick: String          // pet.trick
    var profileImage: String   // pet.profileImage

    // -------- init --------
    // ⭐️ Pet("name", .cat, trick: "trick", profileImage: "imageName")
    init(_ name: String, kind: Kind, trick: String, profileImage: String) {
        self.name = name
        self.kind = kind
        self.trick = trick
        self.profileImage = profileImage
    }
}
```

{% endtab %}

{% tab title="7:41" %}

```swift
import SwiftUI    // for Color ⭐️

// Pet
struct Pet: Identifiable {

    enum Kind {
    
        case cat
        case dog
        case fish
        case bird
        case lizard
        case turtle
        case rabbit
        case bug

        var systemImage: String {
            switch self {
                case .cat: return "cat.fill"
                case .dog: return "dog.fill"
                case .fish: return "fish.fill"
                case .bird: return "bird.fill"
                case .lizard: return "lizard.fill"
                case .turtle: return "tortoise.fill"
                case .rabbit: return "rabbit.fill"
                case .bug: return "ant.fill"
            }
        }
    }

    let id = UUID()
    var name: String
    var kind: Kind
    var trick: String
    var profileImage: String
    var favoriteColor: Color    // ⭐️ new prperty (7:41)

    // ⭐️ new initializer (7:41)
    init(
        _ name: String, 
        kind: Kind, 
        trick: String, 
        profileImage: String, 
        favoriteColor: Color    // ⭐️ new prperty (7:41)
    ) {
        self.name = name
        self.kind = kind
        self.trick = trick
        self.profileImage = profileImage
        self.favoriteColor = favoriteColor
    }
}
```

{% endtab %}

{% tab title="9:31" %}

```swift
@Observable
class Pet: Identifiable {

    // Pet.Kind
    enum Kind {
    
        case cat
        case dog
        case fish
        case bird
        case lizard
        case turtle
        case rabbit
        case bug

        var systemImage: String {
            switch self {
                case .cat: return "cat.fill"
                case .dog: return "dog.fill"
                case .fish: return "fish.fill"
                case .bird: return "bird.fill"
                case .lizard: return "lizard.fill"
                case .turtle: return "tortoise.fill"
                case .rabbit: return "rabbit.fill"
                case .bug: return "ant.fill"
            }
        }
    }

    var name: String
    var kind: Kind
    var trick: String
    var profileImage: String
    var favoriteColor: Color
    var hasAward: Bool = false    // ⭐️ new prperty (9:31)

    init(_ name: String, kind: Kind, trick: String, profileImage: String, favoriteColor: Color) {
        self.name = name
        self.kind = kind
        self.trick = trick
        self.profileImage = profileImage
        self.favoriteColor = favoriteColor
    }

    // ⭐️ new method (9:31)
    func giveAward() {
        hasAward = true
    }
}

// ⭐️ 
extension Pet: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation { $0.name }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="PetStore" %}
{% tabs %}
{% tab title="7:59" %}

```swift
// 7:59, 8:14
@Observable
class PetStore {
    var allPets: [Pet] = [
        Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
        Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
        Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
        Pet("Mango", kind: .bird,  trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
        Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
        Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
        Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
    ]
}
```

{% endtab %}

{% tab title="8:27" %}

```swift
// 8:27, 9:31
@Observable
class PetStore {
    
    var myPets: [Pet] = [
        Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
        Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
    ]

    var otherPets: [Pet] = [
        Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
        Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
        Pet("Mango", kind: .bird,  trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
        Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
        Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
    ]
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="PetStoreViewModel" %}
{% tabs %}
{% tab title="15:19" %}
15:19 - [Searchable](https://developer.apple.com/videos/play/wwdc2024/10150/?time=919)

```swift
@Observable
class PetStoreViewModel {

    var petStore: PetStore
    var searchText: String = ""

    init(petStore: PetStore) {
        self.petStore = petStore
    }

    var myPets: [Pet] {
        // For illustration purposes only. The filtered pets should be cached.
        petStore.myPets.filter { searchText.isEmpty || $0.name.contains(searchText) }
    }
    var otherPets: [Pet] {
        // For illustration purposes only. The filtered pets should be cached.
        petStore.otherPets.filter { searchText.isEmpty || $0.name.contains(searchText) }
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

## App Definition

{% tabs %}
{% tab title="App" %}
{% tabs %}
{% tab title="16:58" %}
16:58 - [App definition](https://developer.apple.com/videos/play/wwdc2024/10150/?time=1018)

```swift
@main
struct SwiftUIEssentialsApp: App {
    var body: some Scene {
        // ⭐️ app definition
        WindowGroup {
            ContentView()
        }
    }
}
```

{% endtab %}

{% tab title="17:15" %}
17:15 - [App definition: multiple scenes](https://developer.apple.com/videos/play/wwdc2024/10150/?time=1035)

```swift
@main
struct SwiftUIEssentialsApp: App {
    var body: some Scene {
        
        // ⭐️ multiple scenes
        WindowGroup {
            ContentView()
        }

        // ⭐️ multiple scenes
        WindowGroup("Training History", id: "history", for: TrainingHistory.ID.self) { $id in
            TrainingHistoryView(historyID: id)
        }

        // ⭐️ multiple scenes
        WindowGroup("Pet Detail", id: "detail", for: Pet.ID.self) { $id in
            PetDetailView(petID: id)
        }
   }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Widget" %}
{% tabs %}
{% tab title="17:23" %}
17:23 - [Widgets](https://developer.apple.com/videos/play/wwdc2024/10150/?time=1043)

```swift
struct ScoreboardWidget: Widget {
    var body: some WidgetConfiguration {
        // ...
    }
}

struct ScoreboardWidgetView: View {

    var petTrick: PetTrick
    
    var body: some View {
        ScoreCard(rating: petTrick.rating)
            .overlay(alignment: .bottom) {
                Text(petTrick.pet.name)
                    .padding()
            }
            .widgetURL(petTrick.pet.url)
    }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Apple Watch" %}
19:37 - [Digital Crown rotation](https://developer.apple.com/videos/play/wwdc2024/10150/?time=1177)

```swift
ScoreCardStack(rating: $rating)
   .focusable()
   #if os(watchOS)
   .digitalCrownRotation($rating, from: 0, through: 10)
   #endif
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/ios/swiftui/intro/essentials.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
