Home > Enterprise >  SwiftUI: How to initialize a new StateObject in a parent view?
SwiftUI: How to initialize a new StateObject in a parent view?

Time:11-09

I have an app architecture similar to the below (simplified) code. I use a WorkoutManager StateObject which I initialize in the set up view, then pass down to its children via EnvironmentObject. The problem is that upon dismissing the .sheet there isn't any life cycle event which initializes a new WorkoutManager, which I need in order to be able to start new workouts consecutively. How in this example below can I give WorkoutView the ability to reinitialize WorkoutManager so that it is a clean object?

import SwiftUI
import HealthKit

class WorkoutManager: ObservableObject {
    
    var workout: HKWorkout?
}

struct ContentView: View {
    
    @StateObject var workoutManager = WorkoutManager()
    
    @State var showingWorkoutView = false
    
    var body: some View {
      
        Button {
            showingWorkoutView.toggle()
        } label: {
            Text("Start Workout")
        }
        .sheet(isPresented: $showingWorkoutView) {
            WorkoutView(showingWorkoutView: $showingWorkoutView)
        }
        
    }
}

struct WorkoutView: View {
    
    @EnvironmentObject var workoutManager:  WorkoutManager
    @Binding var showingWorkoutView: Bool
    
    var body: some View {
        Text("Workout Started")
            .padding()
        Button {
            showingWorkoutView.toggle()
            //Here I want to initialize a new WorkoutManager to clear out the previous workout's state, how? 
        } label: {
            Text("End Workout")
        }

    }
}

CodePudding user response:

As mentioned in the comments already, the route you probably want to take is reseting the state within the same WorkoutManager. You wouldn't be able to assign a new object to a @StateObject anyway -- you'll end up with compiler errors because of the View's immutable self.

Secondly, I'd suggest that you probably don't want to rely on the Button in your WorkoutView to do this. For example, if the user dismissed the sheet by swiping, that wouldn't get called. Instead, you could listen for the sheet's state in onChange (another method would be using the onDismiss parameter of sheet):

class WorkoutManager: ObservableObject {
    var workout: HKWorkout?
    
    func resetState() {
        //do whatever you need to do to reset the state
        print("Reset state")
    }
}

struct ContentView: View {
    
    @StateObject var workoutManager = WorkoutManager()
    
    @State var showingWorkoutView = false
    
    var body: some View {
      
        Button {
            showingWorkoutView.toggle()
        } label: {
            Text("Start Workout")
        }
        .sheet(isPresented: $showingWorkoutView) {
            WorkoutView(showingWorkoutView: $showingWorkoutView)
        }
        .onChange(of: showingWorkoutView) { newValue in
            if !newValue {
                workoutManager.resetState()
            }
        }
    }
}

struct WorkoutView: View {
    
    @EnvironmentObject var workoutManager:  WorkoutManager
    @Binding var showingWorkoutView: Bool
    
    var body: some View {
        Text("Workout Started")
            .padding()
        Button {
            showingWorkoutView.toggle()
        } label: {
            Text("End Workout")
        }

    }
}
  • Related