如何在不使用 .onAppear 的情况下使用 @EnvironmentObject 来初始化 @State?

How can I use @EnvironmentObject to initialize @State, without using .onAppear?

我正在关注 Stanfords 的 iOS 在线课程的 CS193p 开发应用程序。 我正在尝试执行 Assignment 6 (Memorize Themes.pdf).

当我在模拟器中 运行 我的应用程序时,出现以下致命错误: 线程 1:致命错误:在展开可选值时意外发现 nil

我想我可能理解为什么会出现此错误 - 因为 gamesBeingPlayed 被初始化为空,并且在 onAppear 中分配了正确的值,运行s AFTER body。

所以我的问题是:如何初始化@State private var gamesBeingPlayed?

import SwiftUI

struct ThemeChooserView: View {
    @EnvironmentObject var themeStore: ThemeStore
    @State private var gamesBeingPlayed: Dictionary<Theme.ID, EmojiMemoryGame> = [:]
        
    var body: some View {
        NavigationView {
            List {
                ForEach(themeStore.themes) { theme in
                    NavigationLink(destination: EmojiMemoryGameView(game: gamesBeingPlayed[theme.id]!)) {
    // Here I get error: "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
                        VStack(alignment: .leading) {
                            Text(theme.name)
                                .foregroundColor(theme.color)
                                .font(.title)
                            Text(themeCardsDescription(theme: theme))
                        }
                    }
                }
            }
            .navigationTitle("Memorize")
        }
        .onAppear {
            var games: Dictionary<Theme.ID, EmojiMemoryGame> = [:]
            for theme in themeStore.themes {
                games[theme.id] = EmojiMemoryGame(theme: theme)
            }
            gamesBeingPlayed = games
        }
    }
    
    private func themeCardsDescription(theme: Theme) -> String {
        let numberOrAll = theme.numberOfPairsOfCards == theme.emojis.count ? "All" : "\(theme.numberOfPairsOfCards)"
        return numberOrAll + " pairs from \(theme.emojis.joined(separator: ""))"
    }
    
}

如果我使用 nil 合并运算符,就像这样:

NavigationLink(destination: EmojiMemoryGameView(game: gamesBeingPlayed[theme.id] ?? EmojiMemoryGame(theme: themeStore.themes[0]))) {

...然后当我点击导航到所选的游戏主题时,它总是第一个:themeStore.themes[0]。老实说,我不知道为什么。我认为 onAppear 应该在我点击列表中的视图导航时设置 gamesBeingPlayed。

请帮帮我

如果按假设回答问题

How can I use @EnvironmentObject to initialize @State, without using .onAppear?

那么这是一种可能的方法 - 将所有内容移动到接受环境对象作为输入参数的内部视图(因为它已经存在于正文中),例如

struct ThemeChooserView: View {
    @EnvironmentObject var themeStore: ThemeStore
 
    var body: some View {
      ThemeChooserViewImp(themeStore)      // << here !!
    }

    private struct ThemeChooserViewImp: View {
       @State private var gamesBeingPlayed: Dictionary<Theme.ID, EmojiMemoryGame>   // << declare only

       init(_ themeStore: ThemeStore) {
         _gamesBeingPlayed = State(initialValue: ...) // << use themeStore
       }
 
       // ... other content here

    }
}