When hitting the button, a player is added to the game and I want to observe the changes in the game through the view model. When I hit the button, the counter doesn't change.
It's almost as if I need the game within the ContentViewModel
to be both @ObservedObject
and @Published
.
Can someone help me understand the fundamentals of why this is setup wrong and how I can fix it?
import SwiftUI
import Combine
class Game: ObservableObject {
@Published var players: [String] = []
func addPlayer(_ player: String) {
players.append(player)
}
}
class ContentViewModel: ObservableObject {
@Published var game: Game {
didSet {
subscription = game.objectWillChange.sink { [weak self] _ in
self?.objectWillChange.send()
}
}
}
var subscription: AnyCancellable?
init(game: Game) {
self.game = game
}
}
struct ContentView: View {
@ObservedObject var viewModel: ContentViewModel
var body: some View {
Text("Num players: \(viewModel.game.players.count)")
.padding()
Button("Add player") {
viewModel.game.addPlayer("player")
}
}
}
You want to set the subscription
in the init
. This will make sure that every time the game
object instance changes, you will trigger ContentViewModel
to change as well.
Your code doesn't work because only the object instance is mutating - not the object reference. So game
won't trigger the didSet
, therefore you will never set subscription
.
Code:
class ContentViewModel: ObservableObject {
@Published var game: Game
var subscription: AnyCancellable?
init(game: Game) {
self.game = game
subscription = game.objectWillChange.sink { [weak self] _ in
self?.objectWillChange.send()
}
}
}