-1

I am trying to make dispatchgroup work in my code

let dispatchQueue:DispatchQueue = DispatchQueue(label: "com.dispatchgroup", attributes: .concurrent, target: .main)
var dispatchGroup:DispatchGroup = DispatchGroup()
func renewLoginIfRequired()->String{
    self.dispatchGroup.enter()
    dispatchQueue.async(group:dispatchGroup){
        self.authorizeApplication(completionHandler: {
            self.dispatchGroup.leave()
        })
    }
     }
    self.dispatchGroup.wait() // Stops execution here
    return "Login Success"
}

Above code stops execution at self.dispatchGroup.wait(). I have tried the same code without dispatchQueue.async(group:dispatchGroup) around self.authorizeApplication as well with no luck I am not sure what am i doing wrong. One thing to mention here is that self.authorizeApplication will make an async web service request within that function

Edit 1:

To elaborate the problem even more. I will be returning a session token (String) from this method. Here is the function which is calling this

    func processPOSTRequest(_ urlString : String, isAnAuthReq:Bool = false, requestObject: Data?, onSuccess: @escaping (NSDictionary?, NSError?) -> Void, onFailure: @escaping (Data?, NSError?) -> Void){
    let manager = AFHTTPSessionManager()
    let url = URL(string: urlString);
    var request = URLRequest(url: url!);
    if !isAnAuthReq{
        let token = self.renewLoginIfRequired() //-- Get new token before processing a webservice request
        request.setValue(token, forHTTPHeaderField: "Authorization")
    }
    print("processing URl : "+urlString)
    request.httpMethod="POST";
    request.httpBody = requestObject
    request.setValue("application/json; charset=utf-8", forHTTPHeaderField:"Content-Type")
    request.setValue("application/json; charset=utf-8", forHTTPHeaderField:"Accept")
    let task = manager.dataTask(with: request, uploadProgress: nil, downloadProgress: nil, completionHandler:{ data, response, error in
        if(error == nil){
            if let responseCode = (data as? HTTPURLResponse)?.statusCode{
                if responseCode != 200 || !((response as? [String: Any])?["success"] as? Bool)!{
                    let errorResponse = NSError()
                    print("Response received for URL: "+urlString)
                    onFailure(nil, errorResponse.addItemsToUserInfo(newUserInfo: ["errorCode":String(responseCode)]))
                }
                else{
                    onSuccess(response as! NSDictionary?, nil)
                }
            }

        }
        else{
            onFailure(nil, error as NSError?)

        }
    })
    task.resume()
}

If i use Notify or closure. How can i do that? I have tried both of them

sachin.j
  • 125
  • 1
  • 6
  • Don't do that. Use a completion handler. – vadian Apr 04 '17 at 19:13
  • It seems to me that you try to implement semaphore here. Why not use `DispatchSemaphore` for your purpose? – The Dreams Wind Apr 04 '17 at 19:15
  • 1
    Have you tried using `dispatchGroup.notify()` instead of `dispatchGroup.wait()`? – Pedro Castilho Apr 04 '17 at 19:15
  • @AleksandrMedvedev semaphores are for blocking simultaneous access to a resource that should be accessed only atomically. In this case (block synchronization), a DispatchGroup is the correct abstraction to use. – Pedro Castilho Apr 04 '17 at 19:17
  • i wont be able to return anything from the function if i use notify. i want the function to wait until self.authorizeApplication is completed – sachin.j Apr 04 '17 at 19:17
  • @sachin.j your `self.dispatchGroup.wait()` statement seems to be _outside_ of the function though... Check your curly braces. – Pedro Castilho Apr 04 '17 at 19:19
  • It seems that for what are you trying to achieve, there is no need to implement the whole thing, escaping closure should be enough... – Ahmad F Apr 04 '17 at 19:19
  • Using a `DispatchGroup` for this case seems indeed overkill. `DispatchGroup`s are for when you want to synchronize multiple blocks. For a single block, you could use a simple callback. You could pass your returned `String` to that callback instead of returning it. – Pedro Castilho Apr 04 '17 at 19:21
  • @Pedro Castilho How can i wait for the auth call to be completed with a callback – sachin.j Apr 04 '17 at 19:29
  • In this case authorize **before** calling `processPOSTRequest` and pass the token as an (optional) parameter. Then you can do the entire workflow asynchronously. No need to wait. – vadian Apr 04 '17 at 19:31
  • @ vadian . Good call, That could be another way i can implement this. However this will require passing token before every webservice request through out my project – sachin.j Apr 04 '17 at 19:41

1 Answers1

1

I don't know what is under the hood of the authorizeApplication request, but it's likely that its callback performs in the main thread, that is, in the main queue. So you enter, leave and wait in the same queue, therefore you can't reach the self.dispatchGroup.leave() after you've invoked self.dispatchGroup.wait().

To handle that you need to redesign the async request and call self.dispatchGroup.leave() in a background queue.

The Dreams Wind
  • 8,416
  • 2
  • 19
  • 49