0

I have:

class Exercise: ObservableObject {
  @Published var currentSet: Int = 1
  func start() { somehow changing currentSet }
}

class ExerciseProgram: ObservableObject {
  @Published var currentExercise: Exercise? = nil

  func start() { ...self.currentExercise = self.exercises[exerciseIndex + 1]... }
}

struct Neck: View {
  @ObservedObject var program: ExerciseProgram = ExerciseProgram(exercises: neckExercises)

  var body: some View {
    Text(\(self.program.currentExercise!.currentSet))
  }
}

The problem is that my View is updated only when the currentExercise of the ExerciseProgram changes, and the currentExercise itself has a currentSet property, and when it changes, my view is not updated. In principle, I understand the logic of why we work exactly as it works: I specified that the view should be updated when currentExercise changes, but I did not say that the view should be updated when the properties of the currentExercise entity change. And so I don't understand how to do it. And I can't change Exercise as struct

1

1 Answer 1

2

You just have to observe the object at the appropriate level.

Each @Published only triggers a refresh if the object as a whole has changed.

In you example the array will change if you replace the array or add/remove objects.

import SwiftUI
struct ExerciseProgramView: View {
    //At this level you will see the entire program
    @StateObject var program: ExerciseProgram = ExerciseProgram()
    
    var body: some View {
        VStack{
            if program.currentExercise != nil{
                ExerciseView(exercise: program.currentExercise!)
            }else{
                Text("Ready?")
            }
            Spacer()
            HStack{
                if program.currentExercise == nil{
                    Button("start program", action: {
                        program.start()
                    })
                }else{
                    Button("stop", action: {
                        program.stop()
                    })
                    Button("next", action: {
                        program.next()
                    })
                }
                
            }
        }
    }
}
struct ExerciseView: View {
    //At this level you will see the changes for the exercise
    @ObservedObject var exercise: Exercise
    
    var body: some View {
        VStack{
            Text("\(exercise.name)")
            Text("\(exercise.currentSet)")
            if exercise.timer == nil{
                Button("start exercise", action: {
                    exercise.start()
                })
            }else{
                Button("stop exercise", action: {
                    exercise.stop()
                })
            }
        }.onDisappear(perform: {
            exercise.stop()
        })
    }
}

struct ExerciseProgramView_Previews: PreviewProvider {
    static var previews: some View {
        ExerciseProgramView()
    }
}
class Exercise: ObservableObject, Identifiable {
    let id: UUID = UUID()
    let name: String
    @Published var currentSet: Int = 1
    var timer : Timer?
    init(name: String){
        self.name = name
    }
    func start() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
            
            self.currentSet += 1
            
            if self.currentSet >= 10{
                timer.invalidate()
                self.timer = nil
            }
            
        })
    }
    
    func stop(){
        timer?.invalidate()
        timer = nil
    }
}

class ExerciseProgram: ObservableObject {
    @Published var currentExercise: Exercise? = nil
    @Published var exercises: [Exercise] = [Exercise(name: "neck"), Exercise(name: "arm"), Exercise(name: "leg"), Exercise(name: "abs")]
    @Published var exerciseIndex: Int = 0
    func start() {
        self.currentExercise = self.exercises[exerciseIndex]
    }
    
    func next(){
        if exerciseIndex < exercises.count{
            self.exerciseIndex += 1
        }else{
            self.exerciseIndex = 0
        }
        start()
    }
    
    func stop(){
        exerciseIndex = 0
        currentExercise = nil
    }
}

Also, notice how the ObservableObjects have been initialized.

@StateObject is used when the object has to be initialized in a View

@ObservedObject is used to pass an ObservableObject to a child View but the object was created inside a class, specifically class ExerciseProgram.

https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.