I'm following the I am following the CS193P Stanford class to learn SwiftUI after working with Storyboards for more than 3 years.
Already having a bit of experience I allowed myself to do things quickly and not exactly like the professor does. This lead me to discovering a behaviour that I can't understand.
Consider the following code:
struct ContentView: View {
@ObservedObject var viewModel: EmojiMemoryGame
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
ForEach(viewModel.cards) { card in
CardView(card: card)
.onTapGesture {
viewModel.choose(card)
}
}
}
}
}
}
Where a Card is defined like this:
struct Card: Identifiable, Equatable {
static func == (lhs: MemoryGame<CardContent>.Card, rhs: MemoryGame<CardContent>.Card) -> Bool {
lhs.id == rhs.id
}
var isFaceUp = true
var isMatched = false
var content: CardContent
var id = UUID()
}
mutating func choose(_ card: Card) {
guard let index = cards.firstIndex(where: { $0 == card }) else { return }
cards[index].isFaceUp.toggle()
}
Why doesn't the view update when the viewModel.cards
array is updated?
If I remove only the Equatable protocol, leaving the rest of the app untouched:
struct Card: Identifiable { //..
it works just fine.
Since the id never changes (you are initializing it with a UUID) and your == func is only looking at the id of each card to determine equality, the ForEach will never see any change in the other attributes. I had to write the == func as below, and it worked for me.
static func == (lhs: MemoryGame.Card, rhs: MemoryGame.Card) -> Bool {
return lhs.isFaceUp == rhs.isFaceUp &&
lhs.isMatched == rhs.isMatched &&
lhs.content == rhs.content &&
lhs.id == rhs.id
}