I have a Companion WatchOS App with a WatchViewModel, that currently holds all my variables that get send from the App. I have to views, each should display another variable. In one view (ActivityDistributionView) everything works perfectly fine, but in the other one (LeaderboardView) the value is the init value and is not getting changed. Actually no value gets updated in the LeaderboardView, but in the ActivityDistributionView, all values work fine.
What I found out is, that if I comment out the declaration of the ViewModel in ActivityDistribution, then my code Works. Does this mean I can only declare a working instance of the class once?
ContentView body:
var body: some View {
TabView{
ScoreTabView(dailyScore: viewModel.scores["score"] ?? 0, postureScore: viewModel.scores["posture"] ?? 0, movementScore: viewModel.scores["movement"] ?? 0)
NavigationStack{
List{
Section("Last message received:"){
Text("\(formatDate(date: viewModel.lastMessageTime))").font(.body)
//Text("\(viewModel.lastMessage.keys.sorted().formatted())").padding()
}
NavigationLink("Leaderboard"){
LeaderboardViews()
}
NavigationLink("Activity Distribution"){
ActivityDistributionView()
}
}
}
}
}
struct LeaderboardViews: View {
@State private var viewModel: WatchViewModel = WatchViewModel()
var body: some View {
Text("\(viewModel.leaderboardMessage)")
}
}
struct ActivityDistributionView: View {
@State private var viewModel: WatchViewModel = WatchViewModel()
@State private var vibrates = false
let activities = ["sitting", "walking", "standing", "onDesk", "driving", "lying"]
var body: some View {
NavigationStack{
ScrollView{
Text("\(viewModel.leaderboardMessage)")
}
.navigationTitle("Acitivity Distribution")
.navigationBarTitleDisplayMode(.inline)
}
}
}
@Observable
class WatchViewModel: NSObject{
var session: WCSession
var activityDistribution: [String: Any] = [:]
var scores: [String: Double] = [:]
var leaderboardMessage: LeaderboardMessage = LeaderboardMessage(method: "InitExampleLeaderboardMessage", payload: [:])
//more code
}
//Sends Data from WatchOS to AppDelegate (to later push to Flutter)
extension WatchViewModel: WCSessionDelegate {
//code that works
case .LeaderboardMessage:
self.lastMessage = message["payload"] as! [String : Any]
//message gets converted into JSON
let messageAsJSON = self.encodeJSONData(encode: message)
//message gets decoded into the proper struct
let decodedMessage = self.decodeJSONData(decode: messageAsJSON!) ?? LeaderboardMessage(method: "ExampleLeaderboardData", payload: ["Date" : [ExerciseDataWrapper(name: "Nobody", data: ExerciseData(duration: ExerciseData.Duration(displayString: "/", value: 0), exerciseScore: ExerciseData.ExerciseScore(displayString: "/", value: 0), movement: ExerciseData.Movement(displayString: "/", value: 0), posture: ExerciseData.Posture(displayString: "/", value: 0), postureAboveThresholdDuration: ExerciseData.PostureAboveThresholdDuration(displayString: "/", value: 0), score: ExerciseData.Score(displayString: "/", value: 0)))]])
//leaderboardMessage gets updated to the last send leaderboardMessage
self.leaderboardMessage = decodedMessage
//assigning works but not in the LeaderboardsView()
}
I tried changing the name and the location of the NavLink or trying to pass the viewModel value from my ContentView through to my LeaderboardView but it didn't work.
You should have one instance on the ContentView and pass it down from there. For some reason swift decided to use the ViewModel-Instance declared in the ActivityView rather than the one in my ContentView, when I tried passing it down from the ContentView to the other views, and they never updated.
So the correct code would be to have the viewModel in my ContentView and pass it down from there to my other views.
struct LeaderboardViews: View {
let leaderboardMessage: LeaderboardMessage
var body: some View {
Text("\(leaderboardMessage.method)")
}
}
struct ActivityDistributionView: View {
@State private var vibrates = false
let activities = ["sitting", "walking", "standing", "onDesk", "driving", "lying"]
let activityDistribution: [String: Double]
var body: some View {
NavigationStack{
/*ForEach(viewModel.leaderboardData.keys.sorted(), id: \.self) { day in
Text("Day: \(viewModel.leaderboardData[day] ?? "No Date")")
}*/
ScrollView{
//code
}
.navigationTitle("Acitivity Distribution")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct ContentView: View {
@State private var viewModel: WatchViewModel = WatchViewModel()
@State private var vibrates = false
let activities = ["sitting", "walking", "standing", "onDesk"]
var body: some View {
TabView{
ScoreTabView(dailyScore: viewModel.scores["score"] ?? 0, postureScore: viewModel.scores["posture"] ?? 0, movementScore: viewModel.scores["movement"] ?? 0)
NavigationStack{
List{
Section("Last message received:"){
Text("\(formatDate(date: viewModel.lastMessageTime))").font(.body)
//Text("\(viewModel.lastMessage.keys.sorted().formatted())").padding()
}
NavigationLink("Leaderboard"){
LeaderboardViews(leaderboardMessage: viewModel.leaderboardMessage)
}
NavigationLink("Activity Distribution"){
ActivityDistributionView( activityDistribution: viewModel.activityDistribution as! [String: Double])
}
}
}
}
}
}