1

I need to wait until Alamofire request finishes getting data. (Error or value). I'm calling Alamofire function inside a for loop in another function so that Alamofire requests should be finished before second for loop called. For example; first loop -> first request -> second loop -> second request...so on. Now It goes first loop -> second loop -> and after all loops finishes requests response is turning.

Request Function:

 func like(sender_id: String, completion: @escaping (String?) -> ()){
    let group = DispatchGroup()
    if let key = api_key{
        let headers: HTTPHeaders = [
            "X-Auth-Token": key,
            "Accept": "application/json"
        ]
        group.enter()
        Alamofire.request(baseUrl + likeURL + sender_id, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).validate()
            .responseJSON { (response) -> Void in
                guard response.result.isSuccess else {
                    print("Error while doing : \(response.result.error)")
                    completion(nil)
                    group.leave()
                    return
                }
                if let error = response.error{
                    completion(nil)
                    group.leave()
                    print("Error Occured with Request: \(error)")
                }
                if let jsonData = response.data{
                    let json = JSON(data: jsonData)
                    print(json)
                    group.leave()
                    if let matched = json["match"].bool{
                        completion(matched.description)
                        print("Liked!: \(matched)")
                        if(matched){
                        }
                    }else{
                        group.leave()
                        "There is an error with JSON"
                    }
                }}
    }
}

Where I call:

 func like_all(completion: @escaping() -> ()){
    for user in SharedUsers.sharedUser.users{
        if let id = user.id{
            Network.sharedInstance.like(sender_id: id) { result in
                print(result)
            }
        }
        else{
            continue
        }
    }
    completion()
}
Mr Some Dev.
  • 315
  • 4
  • 19
  • Unrelated, in your completion handler, you have a path of execution in which you call `leave` twice. You should fix that. Or, better, if you stay with this dispatch group pattern, I'd suggest using `defer { group.leave() }` at the start of the closure, and then pull out all the other `leave` calls, avoiding any risk of having a path of execution where you fail to call `leave`. Frankly, this is moot, as you shouldn't be using dispatch group at all, but just FYI. – Rob Dec 08 '17 at 16:37
  • I looked promisekit but I'm not sure If It is the right way for my problem. – Mr Some Dev. Dec 08 '17 at 17:05
  • Below I outlined two alternative approaches for how to do this, but PromiseKit is definitely yet another way to solve this. The key idea is that you never want to `wait` for some asynchronous process before starting another, but use one of these patterns to asynchronously trigger one request when the prior one finishes. This avoids blocking a thread (and eliminates any deadlock risks). – Rob Dec 08 '17 at 17:23

2 Answers2

1

You are using dispatch group, obviously with the intent on waiting for the group at the end of the function. But you're not waiting for it, so you're not getting the synchronous behavior you were looking for.

But that's good, because if you wait for that group (or semaphore, the other pattern to achieve this behavior) on the main thread, not only will you be blocking the main thread (which results in horrible UX and risk having your app killed by watchdog process), you're going to deadlock because responseJSON uses the main queue for it's completion handler. So before you add the wait() call on your dispatch group/semaphore, make sure you dispatch this whole for loop asynchronously to some background thread. That avoids blocking the main thread and eliminates the deadlock risk.

But this whole pattern is fundamentally flawed, as you really shouldn't use dispatch groups or semaphores to make it synchronous at all. That raises a few questions:

The first question is why you want to make this synchronous. Network requests have inherent latency, so you performing a sequence of them will be very slow. Only do this sequence of requests if you absolutely have to (e.g. each request cannot be formed because it needs something from the response of the prior request). But that doesn't appear to be the case here. So why make this process unnecessary so.

But let's assume for a second that you absolutely have to perform these sequentially (not true here, from what I can tell, but let's tease this out). Then there are two patterns for performing a series of requests consecutively rather than concurrently:

  • You can either lose this for loop entirely, and simply have a routine that sends the n-th request and sends request n+1 in its completion handler. That completely eliminates the need for dispatch groups/semaphores to block a thread.

  • Or you can wrap this in operation (e.g. https://stackoverflow.com/a/27022598/1271826) and use operation queue.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I want to do it because I have a User class which is populated by my first request and then I start to do second request with the information from user class. After like_all function returns It agains do first request and then again like_all so on.... – Mr Some Dev. Dec 08 '17 at 17:07
  • Can you please give me a example for this one: "You can either lose this for loop entirely, and simply have a routine that sends the n-th request and sends request n+1 in its completion handler. That completely eliminates the need for dispatch groups/semaphores to block a thread."? – Mr Some Dev. Dec 09 '17 at 08:32
0

I solve it by calling like function with index + 1 everytime Alamofire returns value. I also call function in completion part of fetch request.

Here is the code:

    @objc func action(_ sender: LGButton) {
        sender.titleString = "Started to Like :)"
        Network.sharedInstance.get_rec(completion: { (result) in
            if(result != nil)
            {
                Network.sharedInstance.like(sender: 0, completion: { (result) in
                    //print(result)
                })
            }
        })
}

Like Function:

 func like(sender: Int, completion: @escaping (String?) -> ()){
    if let key = api_key{
        let headers: HTTPHeaders = [
            "X-Auth-Token": key,
            "Accept": "application/json"
        ]
        print(sender)
        print(SharedUsers.sharedUser.users.count)
        if(sender < SharedUsers.sharedUser.users.count){
            if let user_id = SharedUsers.sharedUser.users[sender].id{
                Alamofire.request(baseUrl + likeURL + user_id, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).validate()
                    .responseJSON { (response) -> Void in
                        guard response.result.isSuccess else {
                            print("Error while doing : \(response.result.error)")
                            completion(nil)
                            return
                        }
                        if let error = response.error{
                            completion(nil)
                            print("Error Occured with Request: \(error)")
                        }
                        if let jsonData = response.data{
                            let json = JSON(data: jsonData)
                            if let matched = json["match"].bool{
                                completion(matched.description)
                                print("Liked!: \(matched)")
                                if(sender <= SharedUsers.sharedUser.users.count){
                                    self.like(sender: sender + 1, completion: {result in
                                        //print(result)
                                    })
                                }else{
                                    return
                                }
                                if(matched){
                                }
                            }else{
                                "There is an error with JSON"
                            }}}
            }else{return}
        }}}
Mr Some Dev.
  • 315
  • 4
  • 19