Add a ProgressView during a simple URLSession POST request in SwiftUI

I wanted to show a progress view during a simple URL POST request process. What I would like to do is the button to turn into a ProgressView spinner (usually it's default) as it's going through the requestTest function process. After the request is done, then the progress view goes away and turns back into a button.

here's the code.


struct ContentView: View {
    @State private var tweetID = ""
    @State private var tweetStatus = ""
    @State private var response = ""
    @State var showAlert = false
    @State var sendToWebhook = false
    
    var body: some View {
        NavigationView {
            Form {
                Section(footer: Text("Test")) {
                    TextField("Field to place response data", text: $response)
                    TextEditor( text: $tweetStatus)
                        .frame(height: 100)
                }
                Section {
                    
                    Button("Get Data") {

                    // Where progress should start before function 
                        ProgressView("Test", value: 100, total: 100)
                        requestTest() { results in
                            response = results
                            if response == "No Data!" {
                                showAlert = true
                            }
                        }
                    }
                    if self.requestTest {
                        ProgressView()
                    } 
                }
            }
            .alert(isPresented: $showAlert) {
                Alert(title: Text("Tweet Sent"), message: Text("Your Tweet is sent! Your Tweet ID is shown in the field"), dismissButton: .default(Text("OK")))
            }
        }
    }
    func requestTest(completion: @escaping(String) -> ()) {   
        if let url = URL(string: "https://requestbin.net/r/ag4ipg7n") {
            
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            
            var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
            components.queryItems = [ URLQueryItem(name: "TweetID", value: response), 
                                      URLQueryItem(name: "Status", value: tweetStatus)]
            
            if let query = components.url!.query {
                request.httpBody = Data(query.utf8)
            }
            
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                if let data = data,
                   let apiResponse = String(data: data, encoding: .utf8) {
                    
                    // IF Completed, these actions are shown below
                    completion(apiResponse)   
                    self.showAlert = true
                    tweetStatus = "" 
                } else {
                    completion("No Data!")  
                }
            }
            task.resume()
        }
    }
}

I thought if I tried to do as a if self.requestTest { ProgressView() }, but no avail as it throwed me a error that says Cannot convert value of type '(@escaping (String) -> ()) -> ()' to expected condition type 'Bool'.

Is there a way to do that?

Answered by OOPer in 700901022

In your code, requestTest is a name of a function. Using a function name in a condition of if-statement does not make sense.

To use if-statement, you need an expression returning Bool.

Please try something like this:

import SwiftUI

struct ContentView: View {
    @State private var tweetID = ""
    @State private var tweetStatus = ""
    @State private var response = ""
    @State var showAlert = false
    @State var sendToWebhook = false
    
    @State var isRequestInProgress: Bool = false //<-
    
    var body: some View {
        NavigationView {
            Form {
                Section(footer: Text("Test")) {
                    TextField("Field to place response data", text: $response)
                    TextEditor( text: $tweetStatus)
                        .frame(height: 100)
                }
                Section {
                    
                    Button("Get Data") {
                        
                        // Where progress should start before function
                        //↓Writing a View in an action closure does not make sensse
                        //ProgressView("Test", value: 100, total: 100)
                        requestTest() { results in
                            response = results
                            if response == "No Data!" {
                                showAlert = true
                            }
                        }
                    }
                    if isRequestInProgress { //<-
                        ProgressView()
                    }
                }
            }
            .alert(isPresented: $showAlert) {
                Alert(title: Text("Tweet Sent"), message: Text("Your Tweet is sent! Your Tweet ID is shown in the field"), dismissButton: .default(Text("OK")))
            }
        }
    }
    
    func requestTest(completion: @escaping(String) -> ()) {
        if let url = URL(string: "https://requestbin.net/r/ag4ipg7n") {
            
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            
            var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
            components.queryItems = [ URLQueryItem(name: "TweetID", value: response),
                                      URLQueryItem(name: "Status", value: tweetStatus)]
            
            if let query = components.url!.query {
                request.httpBody = Data(query.utf8)
            }
            
            self.isRequestInProgress = true //<-
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                //↓
                defer {
                    DispatchQueue.main.async {
                        self.isRequestInProgress = false
                    }
                }
                if let data = data,
                   let apiResponse = String(data: data, encoding: .utf8) {
                    
                    // IF Completed, these actions are shown below
                    completion(apiResponse)
                    self.showAlert = true
                    tweetStatus = ""
                } else {
                    completion("No Data!")
                }
            }
            task.resume()
        }
    }
}
Accepted Answer

In your code, requestTest is a name of a function. Using a function name in a condition of if-statement does not make sense.

To use if-statement, you need an expression returning Bool.

Please try something like this:

import SwiftUI

struct ContentView: View {
    @State private var tweetID = ""
    @State private var tweetStatus = ""
    @State private var response = ""
    @State var showAlert = false
    @State var sendToWebhook = false
    
    @State var isRequestInProgress: Bool = false //<-
    
    var body: some View {
        NavigationView {
            Form {
                Section(footer: Text("Test")) {
                    TextField("Field to place response data", text: $response)
                    TextEditor( text: $tweetStatus)
                        .frame(height: 100)
                }
                Section {
                    
                    Button("Get Data") {
                        
                        // Where progress should start before function
                        //↓Writing a View in an action closure does not make sensse
                        //ProgressView("Test", value: 100, total: 100)
                        requestTest() { results in
                            response = results
                            if response == "No Data!" {
                                showAlert = true
                            }
                        }
                    }
                    if isRequestInProgress { //<-
                        ProgressView()
                    }
                }
            }
            .alert(isPresented: $showAlert) {
                Alert(title: Text("Tweet Sent"), message: Text("Your Tweet is sent! Your Tweet ID is shown in the field"), dismissButton: .default(Text("OK")))
            }
        }
    }
    
    func requestTest(completion: @escaping(String) -> ()) {
        if let url = URL(string: "https://requestbin.net/r/ag4ipg7n") {
            
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            
            var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
            components.queryItems = [ URLQueryItem(name: "TweetID", value: response),
                                      URLQueryItem(name: "Status", value: tweetStatus)]
            
            if let query = components.url!.query {
                request.httpBody = Data(query.utf8)
            }
            
            self.isRequestInProgress = true //<-
            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                //↓
                defer {
                    DispatchQueue.main.async {
                        self.isRequestInProgress = false
                    }
                }
                if let data = data,
                   let apiResponse = String(data: data, encoding: .utf8) {
                    
                    // IF Completed, these actions are shown below
                    completion(apiResponse)
                    self.showAlert = true
                    tweetStatus = ""
                } else {
                    completion("No Data!")
                }
            }
            task.resume()
        }
    }
}
Add a ProgressView during a simple URLSession POST request in SwiftUI
 
 
Q