asynchronousswiftuiasync-await

Trying to pass a value from a global function


I'm trying to pass a Double value from a function called from another view and the function returns a value before the AlamoFire request completes

struct GetPrice {

    static func getThePrice(book: String) async -> Double? {
        
        var theNetPrice: Double = 0.0
        var iListPrice = ""
        let url = "https://example.com"
        
        let headers: HTTPHeaders = [
            "accept": "application/xml",
            "Authorization": "Basic \xxx",
           
            "Content-Type": "application/x-www-form-urlencoded"
        ]

        let body: [String: Any] = ["xxxxx"]

        AF.request(url, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
            .responseData { response in
            var statusCode = response.response?.statusCode
                //print("response.result ", response.result)
            switch response.result {
            case .success:
                let data = response.data
                let s = String(data: data!, encoding: .utf8)
                let xml = XMLHash.parse(String(s!))
                
                    iListPrice = xml["xxx"].element!.text
                    theNetPrice = Double(iListPrice)
                    print(theNetPrice) // this shows correct value
                    //return theNetPrice -- this would give error 'Cannot convert value of type 'Double' to closure result type 'Void''
            case .failure(let error):
                statusCode = error._code
                print("status code is: \(String(describing: statusCode))")
                
                print(error)
            }
        }
        
        return theNetPrice //returns 0.0
    }
}
'''

Solution

  • Try this approach using a continuation as shown in the example code.

    struct GetPrice {
    
        static func getThePrice(book: String) async -> Double? {
            //..
            return await withCheckedContinuation { continuation in
                AF.request(url, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
                    .responseData { response in
                        var statusCode = response.response?.statusCode
                        //print("response.result ", response.result)
                        switch response.result {
                        case .success:
                            let data = response.data
                            let s = String(data: data!, encoding: .utf8)
                            let xml = XMLHash.parse(String(s!))
                            
                            iListPrice = xml["xxx"].element!.text
                            theNetPrice = Double(iListPrice)
                            print(theNetPrice) // this shows correct value
    
                            continuation.resume(returning: theNetPrice)
    
                        case .failure(let error):
                            statusCode = error._code
                            print("status code is: \(String(describing: statusCode))")
                            print(error)
    
                            continuation.resume(returning: theNetPrice)
    
                        }
                    }
            }
    
        }
    }
    

    Use it with something like

     Task {
         let price = await GetPrice.getThePrice(book: "MyBook")
         if let price = price {
             print("The price is: \(price)")
         }
     }
    

    Or you could try using a completion handler, such as

     static func getThePrice(book: String, completion: @escaping (Double?) -> Void) {
         //..
             AF.request(url, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
                 .responseData { response in
                     var statusCode = response.response?.statusCode
                     //print("response.result ", response.result)
                     switch response.result {
                     case .success:
                         let data = response.data
                         let s = String(data: data!, encoding: .utf8)
                         let xml = XMLHash.parse(String(s!))
                         
                         iListPrice = xml["xxx"].element!.text
                         theNetPrice = Double(iListPrice)
                         print(theNetPrice) // this shows correct value
                         
                        completion(theNetPrice)
     
                     case .failure(let error):
                         statusCode = error._code
                         print("status code is: \(String(describing: statusCode))")
                         print(error)
     
                        completion(theNetPrice)
    
                     }
                 }
         }
    

    Use it like this:

     GetPrice.getThePrice(book: "MyBook") { price in
             if let price = price {
                 print("The price is: \(price)")
             }
         }