I'm trying to listen to changes of an element in a dictionary using @Published
.
I have a dictionary, containing a list of player scores, indexed by player id. It looks like this:
class PlayersScore: ObservableObject {
@Published var scores: [String: PlayerScore] = [:];
}
This dictionary is exposed through the property scores
of a shared instance of a class called Api
.
class Api: NSObject {
static var shared = Api()
@Published public var scores = PlayersScore()
func updatePlayerScore(playerId: String, score: PlayerScore) {
this.scores.scores[playerId] = score
}
}
Sometimes, the score of a player is updated.
I have a view dedicated to a Player that looks like this:
struct PlayerView: View {
var player: Player
@ObservedObject private var scores = Api.shared.scores
var body: some View {
HStack {
Text(player.name)
Text("Latest score: \(scores.scores[player.id] ?? "not found")")
}
}
}
When the score of a player is updated, my view re-render.
My problem is that ... when a player score is updated, ALL players views are re-rendered as they all react to changes of the main dictionary.
So, I would like to subscribe to changes on a specific player score in his corresponding view, so only the appropriate view re-renders when necessary.
The player score may not be yet initialised when subscribing or accessing (ie scores.scores[player.id]
may not exists yet).
Any idea how I could achieve this?
Thanks!
CodePudding user response:
Here is a possible approach - make PlayerScore
as a observable class, so PlayersScore will be updated only when new player added, and due to observed specific PlayerScore
explicitly only corresponding view will be updated on value
changes.
class PlayerScore: ObservableObject {
var id: String // say back ref to user
@Published var value: Int
// ... other published properties
}
struct PlayerView: View {
var player: Player
@ObservedObject private var scores = Api.shared.scores
var body: some View {
HStack {
Text(player.name)
if let score = scores.scores[player.id] {
PlayerScoreView(playerScore: score)
} else {
Text("No scores")
}
}
}
}
struct PlayerScoreView: View {
@ObservedObject var playerScore: PlayerScore
var body: some View {
Text("Latest score: \(playerScore.value)")
}
}
Note: this is just a sketch coded here, so might be typo