SwiftUI - Mettre à jour un avancement texte pendant une tâche

Bonjour,

Je n’arrive pas à mettre à jour l’affichage de l’avancement d’une tâche.
J’ai fait un exemple pour illustrer mon problème.

Voici le code de la vue:

struct ContentView: View {
    @ObservedObject var calculator = Calculator()
    
    var body: some View {
        VStack {
            
            Text("\(calculator.textToUpdate)")
            Spacer()
           
            Button("Go") {
                run()
            }
            .font(.title)
        }
    }
    
    func run() {
        Task {
             await calculator.makeHeavyCalculations()
        }
    }
}

Et voici le code de la classe qui fait la tâche longue:

import Foundation
import SwiftUI

class Calculator: ObservableObject {
    @Published var textToUpdate = "En attente..."
    
    func makeHeavyCalculations() async {
        for i in 0...10000 {
            DispatchQueue.main.async {
                self.textToUpdate = "\(i)"
            }
            sleep(1)
        }
    }
}

Le texte Text("(calculator.textToUpdate)") ne se met pas à jour au fur et à mesure de l’avancement de la tâche.

Merci par avance pour l’aide que vous saurez m’apporter !
Cordialement,
Nicolas

Je ne suis pas sûr d’avoir bien compris, mais j’aurai tendance à penser qu’avec await async le retour de fonction n’a lieu qu’à la fin, simplement d’autres tâches peuvent continuer d’être exécutées en attendant le résultat, comme par exemple un indicateur d’activité qui peut être codé après await mais dont le résultat sera visible avant celui du retour de fonction pour permettre à l’utilisateur final de patienter, ce qu’on ne lui demande que trop, d’ailleurs, à longueur de journée, partout, aussi hors de l’univers digital. Mais je ne me suis pas encore servi de await async, c’est juste une question.

1 « J'aime »

Bonjour,

Une autre manière de poser ma question: avec le code ci-dessous, complètement dans la vue principale, j’obtiens le résultat attendu. Le compteur s’incrémente bien de un en un une fois que j’ai appuyé sur « Go ».
Mais comme, dans mon véritable code, il ya plusieurs dizaines de lignes dans la fonction qui fait des calculs, je souhaite séparer les variables et déporter ces calculs dans une entité en dehors de la vue.

Cordialement,
Nicolas

struct ContentView: View {
    @State var textToUpdate = "En attente..."
    
    var body: some View {
        VStack {
            
            Text("\(textToUpdate)")
            Spacer()
            
            Button("Go") {
                run()
            }
            .font(.title)
        }
    }
    
    func run() {
        Task {
            await makeHeavyCalculations()
        }
    }
    
    func makeHeavyCalculations() async {
        for i in 0...10000 {
            DispatchQueue.main.async {
                textToUpdate = "\(i)"
            }
            sleep(1)
        }
    }
}

Bonjour,

Je me réponds à moi-même :grinning:

Le code suivant me permet d’obtenir le résultat recherché (ce n’est peut-être pas la « meilleure » manière, mais ça fonctionne !)

import SwiftUI

struct ContentView: View {
    //@State var textToUpdate = "En attente..."
    @ObservedObject var calculator = Calculator()
    @State var pressedBool = false
    
    var body: some View {
        VStack {
            Text(calculator.textToUpdate)
            Text("\(calculator.valueToUpdate)")
            ProgressView(value: calculator.progress)
            
            Spacer()
            Button("Go") {
                run()
            }
            .font(.title)
        }
    }
    
    func run() {
        calculator.makeCalculations()
    }
}

Et

import Foundation
import SwiftUI

class Calculator: ObservableObject {
    @Published var textToUpdate = "En attente..."
    @Published var progress = 0.0
    var valueToUpdate = 0
    var numberOfIterations = 500
    
    func makeCalculations()  {
        for _ in 1...numberOfIterations {
            Task {
                let intermediateValue = await getNewAsyncCalculus()
                await MainActor.run {
                    valueToUpdate += intermediateValue
                    textToUpdate = "\(valueToUpdate)"
                    progress += 1.0/Double(numberOfIterations)
                }
               
            }
        }
    }
}

Cordialement,
Nicolas