UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Changes to @State / @Binding variable does not effect view - I'm missing something here....

Forums > SwiftUI

@bci  

Here is a standalone program that shows my problem. When the user clicks on the "Save Report" button, I expect the button label to update through:

  • Save Report
  • Uploading 1...
  • Uploading 2...
  • Uploading 3...
  • Uploading 4...
  • Reports uploaded

    The problem is the button label is not changing, and completes with "Reports uploaded"

The @State var is init'ed to showProgressView: Bool = false When set to true (or via .toggle()), there is no change the button label view (Should show the ProgressView())

I've messed up somewhere, or do not understand the @State/@Binding interaction.

//
//  ContentView.swift
//  state-test

import SwiftUI

enum UploadJsonFileStatus {
    case success, failedToUpload, failedToRemove
}

struct ContentView: View {

    // Form support
    @State private var formValid: Bool = false
    @State private var submitted: Bool = false
    @State private var showProgressView: Bool = false
    @State private var jsonFilesProgress: Double = 0.0

    var body: some View {

        var jsonFiles = [URL]()
        var jsonFilesTotal: Double = 1.0

        NavigationView {
            Form {
                // Submit form
                Section(header: Text("Submit")) {

                    Button(action: {
                        // Get array if JSON file URLs
                        jsonFiles = getJsonFileURLs()
                        jsonFilesTotal = Double(jsonFiles.count)

                        // Set up @State var
                        jsonFilesProgress = 0.0
                        showProgressView = true // Turn on

                        // Walk through files, attempt to upload
                        for (i, jsonFileURL) in jsonFiles.enumerated() {
                            jsonFilesProgress = Double(i) // Update @State var
                            let xferStatus = uploadJsonFile(jsonFileURL: jsonFileURL)
                            print("\(xferStatus)")
                        }

                        // Update @State var
                        showProgressView = false // Turn off
                        submitted = true // Turn on

                    }, label: {
                        SubmitInformationView(submitted: $submitted,
                                              showProgressView: $showProgressView,
                                              jsonFilesProgress: $jsonFilesProgress,
                                              jsonFilesTotal: jsonFilesTotal)
                    }) // Button
                } // Section Submit
            } // form
        } // nav
    } // body
} // struct

struct SubmitInformationView : View {
    @Binding var submitted: Bool
    @Binding var showProgressView: Bool
    @Binding var jsonFilesProgress: Double
    var jsonFilesTotal: Double

    var body: some View {
        HStack {
            Spacer()
            if submitted {
                Text("Reports uploaded").foregroundColor(.green)
            } else {
                if showProgressView {
                    ProgressView("Uploading \(Int(jsonFilesProgress))...", value: jsonFilesProgress, total: jsonFilesTotal)
                } else {
                    Text("Save Report")
                }
            }
            Spacer()
        } // hstack
    } // body
} // struct

// Return an array of URLs
func getJsonFileURLs() -> Array<URL> {
    let fileURLs = [URL(string: "file1.json")!, URL(string: "file2.json")!, URL(string: "file3.json")!, URL(string: "file4.json")!]
    return fileURLs
}

// Upload one URL
func uploadJsonFile(jsonFileURL: URL) -> UploadJsonFileStatus {
    print("Uploading file \(jsonFileURL.lastPathComponent)")
    sleep(2)
    return UploadJsonFileStatus.success
}

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

3      

It's because you are doing everything in one action closure. It won't stop in the middle of a closure to rerender the View with showProgressView == true but will wait until the closure is finished and then rerender using the current state of the View's properties. And by that point, you've set showProgressView back to false so you don't get the ProgressView you were expecting.

3      

@bci  

Got it. I may need to use the async call for the file transfer to get out of the closure. Then update like I used to do with ObjC.

Thank you. I'll update with solution.

3      

@bci  

Works when Button(action: {}) is pushed to the background. Thank you @roosterboy!

Code update:

Button(action: {
                        // Get array if JSON file URLs
                        jsonFiles = getJsonFileURLs()
                        jsonFilesTotal = Double(jsonFiles.count)

                        DispatchQueue.global(qos: .background).async {

                            // Set up @State var
                            submitted = false
                            jsonFilesProgress = 0.0
                            showProgressView = true // Turn on

                           // Walk through files, attempt to upload
                            for (i, jsonFileURL) in jsonFiles.enumerated() {
                                jsonFilesProgress = Double(i) // Update @State var
                                let xferStatus = uploadJsonFile(jsonFileURL: jsonFileURL)
                                print("\(xferStatus)")
                            }

                            // Update @State var
                            showProgressView = false // Turn off
                            submitted = true // Turn on
                        }
                    }

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.