Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a synchronous request using Alamofire?

I am trying to do a synchronous request using Alamofire. I have looked on Stackoverflow and found this question: making an asynchronous alamofire request synchronous.

I saw that the accepted answer uses completion to make Alamofire request synchronous but I cannot make it to work. This is my simplified code:

func loadData(completion: (Bool)) -> (Int, [String], [String], [String]){

    Alamofire.request(url!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { response in

        switch(response.result) {
        case .success(_):
            if let JSON = response.result.value as! [[String : AnyObject]]!{
                 //Here I retrieve the data
            }

            completion(true)
            break

        case .failure(_):
            print("Error")
            completion(false)
            break  
        }
   }

   return (numberRows, nameArray, ageArray, birthdayArray)
}

With this code I am getting an error when trying to make completion(bool value). The error that I am getting is the following:

Cannot call value of non-function type 'Bool'

I have tried using a lot of examples using completion to get the values synchronously (because I need to retrieve the data before to show it on a table and at the same time get the number of rows of that table) without success.

How can I use that completion to get a synchronous response?

Thanks in advance!

like image 245
Francisco Romero Avatar asked Oct 10 '16 14:10

Francisco Romero


People also ask

Is Alamofire asynchronous?

Everything with Alamofire is asynchronous, which means you'll update the UI in an asynchronous manner: Hide the upload button, and show the progress view and activity view. While the file uploads, you call the progress handler with an updated percent.

What is the use of Alamofire in Swift?

Alamofire is an HTTP networking library written in Swift. Alamofire helps to improve the quality of code. It is a simpler way to consume REST services. Alamofire is the basic tool for hundreds of projects.


1 Answers

Updated:

You can use a Semaphore to freeze the calling thread until the task has returned a value: Ref


func performSynchronously(request: URLRequest) -> (data: Data?, response: URLResponse?, error: Error?) {
        let semaphore = DispatchSemaphore(value: 0)

        var data: Data?
        var response: URLResponse?
        var error: Error?

        let task = self.dataTask(with: request) {
            data = $0
            response = $1
            error = $2
            semaphore.signal()
        }

        task.resume()
        semaphore.wait()

        return (data, response, error)
    }

Now, let’s say that we wanted to render the items loaded by the above WWDCItemsLoader within a SwiftUI view. An initial idea on how to do that might be to do something like this: Ref

struct WWDCItemsList: View {
    var loader: WWDCItemsLoader
    @State private var loadingState = LoadingState<[WWDCItem]>.idle

    var body: some View {
        switch loadingState {
        case .idle:
            Color.clear.onAppear(perform: loadItems)
        case .loading:
            ProgressView()
        case .loaded(let items):
            List(items) { item in
                // Rendering each item
                ...
            }
        case .failed(let error):
            ErrorView(error: error, reloadHandler: loadItems)
        }
    }

    private func loadItems() async {
        loadingState = .loading
        
        do {
            let items = try await loader.load()
            loadingState = .loaded(items)
        } catch {
            loadingState = .failed(error)
        }
    }
}

Old Answer: (Swift 2.0)

when you use completion handler do not use return.

func loadData(completion: @escaping (_ number: Int, _ strArr1: [String], _ strArr2: [String], _ strArr3: [String]) -> ()){

  Alamofire.request(url!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { response in
    
    switch(response.result) {
    case .success(_):
        if let JSON = response.result.value as! [[String : AnyObject]]!{
            //Here I retrieve the data
        }
        completion(number: numberRows, strArr1 : nameArray, strArr2 : ageArray, strArr3: birthdayArray)
        break
        
    case .failure(_):
        print("Error")
        completion(number: numberRows, strArr1 : nameArray, strArr2 : ageArray, strArr3: birthdayArray)
        break
    }
  }
}

loadData (completion: { (number, strArr1, strArr2, strArr3) in
    // do it
    // for exapmple
    self.number = number
    self.strArr1 = strArr1
    // and so on
    
})

or if you want return any value in closure you must use completion handler for return any value or some thing like, for example if you want return Boolean value:

func loadData(completion:(number: numberRows, strArr1 : nameArray, strArr2 : ageArray, strArr3: birthdayArray) -> (Bool))

and in the loadData

loadData( completion: { ( number, strArr1, strArr2, strArr3 ) -> (Bool) in
       # code 
       return False
})

or some think else.

I use swift 3. but if you want another version of swift careful about External Parameter Names and internal parameter names, like: @escaping (_ number: Int, _ strArr1: [String], _ strArr2: [String], _ strArr3: [String]) -> ())

if you want set external parameter names, just need drop _ and set name for parameters.

like image 106
Ali ZahediGol Avatar answered Sep 23 '22 13:09

Ali ZahediGol