I am following Stanfords' CS193p Developing Apps for iOS online course. I'm trying to do the Assignment 6 (Memorize Themes.pdf).
When I run my app in simulator, I get the following fatal error: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I think I probably understand why it gives this error - because gamesBeingPlayed is initialized to empty, and it is assigned proper value in onAppear, which runs AFTER var body.
So my question is: How can I initialize @State private var gamesBeingPlayed?
- I can't do this in init(), because @EnvironmentObject is injected after view constructor call
- I can't do this in .onAppear{ } as well, because it is run after view body.
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: ""))"
}
}
If I use nil coalescing operator, like this however:
NavigationLink(destination: EmojiMemoryGameView(game: gamesBeingPlayed[theme.id] ?? EmojiMemoryGame(theme: themeStore.themes[0]))) {
... then when I tap to navigate to the chosen game theme, it always is this first one: themeStore.themes[0]
. I have no idea why to be honest. I thought onAppear should set gamesBeingPlayed by the time I tap on a View in the List to navigate to.
Please help me