Synchronous Request Using Alamofire

How to make a synchronous request using Alamofire?

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.

Synchronous request using Alamofire

Don't try to make an asynchronous request synchronous

Instead use DispatchGroup,notify is called when all network requests are completed.

let group = DispatchGroup()
for index in 0..<nearbyRestaurants.count {
group.enter()
getGoogleMapsInfo(startLocation: currentLocation, restaurant: nearbyRestaurants[index]) { (distance, routePoints) in
nearbyRestaurants[index].distance = distance
nearbyRestaurants[index].routePoints = routePoints
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
print("all info data has been received")
}

Convert Alamofire request to synchronous?

You can use this extension and call sync request to Alamofire lib.

extension Alamofire.Manager {

func syncRequest(URLRequest: URLRequestConvertible) -> Response<AnyObject, NSError> {

var outResponse: Response<AnyObject, NSError>!
let semaphore: dispatch_semaphore_t! = dispatch_semaphore_create(0)

self.request(URLRequest).responseJSON { (response: Response<AnyObject, NSError>) -> Void in

outResponse = response
dispatch_semaphore_signal(semaphore)
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return outResponse
}
}

For Loop - Alamofire Requests in a synchronous way Swift4 - Execute for loop waiting till async request returns

try this code

for i in 0 ..< 5 {
print("Request Number" ,i)
let runLoop = CFRunLoopGetCurrent()
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
CFRunLoopStop(runLoop)
}
CFRunLoopRun()
}

making an asynchronous alamofire request synchronous

It's quite easy to implement a completion block in Swift.

This is your function with a completion block

func checkIfUserExistsInDB(userName: String, completion:(String) -> Void)
{
Alamofire.request(.POST, "http://blablabla.com/getuserdata", parameters: ["queryValue": userName,], encoding:.JSON).responseJSON { request, response, result in
switch result {
case .Success(let JSON):
let info = String(data: JSON.dataUsingEncoding(NSUTF8StringEncoding)!, encoding: NSUTF8StringEncoding)!
completion(info)

case .Failure(let data, _):
if let errorData = data, info = String(data: errorData, encoding: NSUTF8StringEncoding) {
completion(info)
}
}
}
}

and can be called with (info is the asynchronously returned string)

checkIfUserExistsInDB("string") { (info) in
print(info)
}

Alamofire synchronous request with a loop

You can run your requests sequentially in a serial queue, in which case they will be executed in the order you call them, which ensures they will be added to the array in order. However, this seems like a suboptimal solution, since you lose execution time by running your requests sequentially instead of concurrently.

If you still want to implement it like this, see the code below:

var marks = [JSON]()
let vnCount = studentVnCodes.count
marks = [JSON](repeating: JSON.null, count: vnCount)
let serialQueue = DispatchQueue(label: "serialQueue")
for vn in studentVnCodes {
serialQueue.async{
let url = "https://example.com/Student/Grade/GetFinalGrades?&vn=\(vn)&academic_year=All"
Alamofire.request(url).responseString { response in
var dataString: String = (response.result.value)!
dataString = cleanMarksJSON(string: dataString)
if let dict = convertToDictionary(text: dataString) {
marks.append(JSON(dict as Any))
if (vnCount == marks.count) {
completionHandler(marks)
}
}
}
}
}

A better solution would be to store the response in a data structure, where ordering doesn't matter, for example in a dictionary, where your keys are the indexes (which you would use for an array) and your values are the JSON response values. This way you can run the requests concurrently and access the responses in order.

Why is ssl pinning not working on synchronous requests?

Using Alamofire synchronously is not supported so any misbehaviors you see when doing this are unlikely to be fixed.

Additionally, that dependency is using Alamofire 4, where 5 is the latest version, so if you really want the behavior I suggest implementing it manually using the latest version.



Related Topics



Leave a reply



Submit