EnvironmentObject as progressValue in ProgressView()

The model increments through a range of dates converted to Double and scaled, ranging from 0 to 1.0

I wish to show a determinate progress bar.

As I understand it, the ProgressView() must be declared in the ContentView and its progress var must be declared as a @State var

The ContentView declares the model vars as @EnvironmentObjects

If I declare: @State private var = model.progressValue I get the error:"Cannot use instance member 'model' within property initializer; property initializers run before 'self' is available"

btw the model class declares: @Published var progressValue : Double

The model func (to increment through dates) is launched by a button in the ContentView.

idk how to get the incremented progressValue from the model to the ContentView State var.

Accepted Reply

Found a solution! Search: "quick-start/swiftui/how-to-show-progress-on-a-task-using-progress" Use a Timer to allow ProgressView to update from EnvironmentObject vars. Then you must config your app-file with the proper .environmentObject()

app-file:

import SwiftUI

@main
struct test_progressView_EnvObjApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(Model.init(progressValue: 0.0))
        }
    }
}


Model-file:

import Foundation
import SwiftUI

class Model: ObservableObject {
    @Published var t_step        : Double
    @Published var progressValue : Double
    
    init (progressValue: Double){
        self.t_step        = 0.0
        self.progressValue = 0.0
    }
    
    func stepThroughTime() {
        var when = DispatchTime.now()
        progressValue = 0.0
        let _ = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { timer in
            DispatchQueue.global().asyncAfter(deadline: when ) { [self] in
                DispatchQueue.main.async { [self] in
                    if progressValue >= 0.9 {  progressValue = 0.0 }
                    else {  progressValue = progressValue + 0.1 }
                }
                when = when + .seconds(2)
            }
        }
    }
}


ContentView-file:

import SwiftUI

struct ContentView: View {
    
    @EnvironmentObject var model: Model
    @State var progressValue  = 0.0
    let timer                 = Timer.publish(every: 2.0, on: .main, in: .common).autoconnect()    //  seconds
    
    var body: some View {
        VStack {
            Button(
                action: {
                    model.stepThroughTime()
                }
            )
            { Text("Search") }.environmentObject(model)
            ProgressView(value: progressValue ).onReceive(timer) { _ in
                progressValue = model.progressValue
            }.padding()
                .environmentObject(model)
        }.environmentObject(model)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Thanks to Claude31 for the DispatchQueue-ProgressValue portion. See my post on the subject.

Replies

Instead of commenting your code, you'd better show it.

Thought you might say that Claude31. The original is too large so I created this example code...

import SwiftUI
import Combine

class model: ObservableObject {
    @Published var t_step        : Double
    @Published var progressValue : Double
    
    init (progressValue: Double){
        self.t_step        = 0.0
        self.progressValue = 0.0
    }
    
    func stepThroughTime() {
        let start = 110.0
        let end   = 234.0
        while t_step < end {
            progressValue = (end - t_step)/(end - start)
            t_step += 1
        }
    }
}




struct ContentView: View {
    @EnvironmentObject var model: model
    
    var body: some View {
        VStack {
            Button(
                action: {
                    model.stepThroughTime()
                }
            )
            { Text("Search") }
            ProgressView(value: model.progressValue) // NOT A @STATE var... so doesn't work
        }
    }
}

  • The action of a button { inside the curly brackets } seems to act like a Dispatch.async

    I assume ProgressView() is still a work-in-progress at Apple. Their current example code is clearly non-real-world, i.e., useless.

Add a Comment

Found a solution! Search: "quick-start/swiftui/how-to-show-progress-on-a-task-using-progress" Use a Timer to allow ProgressView to update from EnvironmentObject vars. Then you must config your app-file with the proper .environmentObject()

app-file:

import SwiftUI

@main
struct test_progressView_EnvObjApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(Model.init(progressValue: 0.0))
        }
    }
}


Model-file:

import Foundation
import SwiftUI

class Model: ObservableObject {
    @Published var t_step        : Double
    @Published var progressValue : Double
    
    init (progressValue: Double){
        self.t_step        = 0.0
        self.progressValue = 0.0
    }
    
    func stepThroughTime() {
        var when = DispatchTime.now()
        progressValue = 0.0
        let _ = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { timer in
            DispatchQueue.global().asyncAfter(deadline: when ) { [self] in
                DispatchQueue.main.async { [self] in
                    if progressValue >= 0.9 {  progressValue = 0.0 }
                    else {  progressValue = progressValue + 0.1 }
                }
                when = when + .seconds(2)
            }
        }
    }
}


ContentView-file:

import SwiftUI

struct ContentView: View {
    
    @EnvironmentObject var model: Model
    @State var progressValue  = 0.0
    let timer                 = Timer.publish(every: 2.0, on: .main, in: .common).autoconnect()    //  seconds
    
    var body: some View {
        VStack {
            Button(
                action: {
                    model.stepThroughTime()
                }
            )
            { Text("Search") }.environmentObject(model)
            ProgressView(value: progressValue ).onReceive(timer) { _ in
                progressValue = model.progressValue
            }.padding()
                .environmentObject(model)
        }.environmentObject(model)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Thanks to Claude31 for the DispatchQueue-ProgressValue portion. See my post on the subject.