0

It is a very common question for people to ask "How do I delay a function or a chunk of code?" but that is not what I need here.

I need my code to wait until a certain task is complete, otherwise my function receives an error that I have no access_token (as the code doesn't wait for fetching the data from the Spotify server).

Here is my code so far, with the attempt to add a DispatchGroup:

func getAccessToken() throws -> Spotify.JSONStandard {
    var accessToken: Spotify.JSONStandard!

    let group = DispatchGroup() // <- Create group
    group.enter() // <- Enter group

    Alamofire.request("https://accounts.spotify.com/api/token", method: .post, parameters: spotify.parameters, headers: nil).responseJSON(completionHandler: {
        response in
        // Check if response is valid
        if let newValue = response.result.value as? Spotify.JSONStandard {
            accessToken = newValue
        }

        group.leave() // <- Leave group
    })

    group.wait() // <- Wait until task is completed

    // \/ Delay this code until complete \/
    if accessToken != nil {
        return accessToken
    }
    else {
        throw SpotifyError.failedToGetAccessToken
    }
    // /\                                /\
}

Without the groups, my code throws SpotifyError.failedToGetAccessToken (the access_token is nil).

However, after adding the groups, my code just hangs and waits forever. How can I delay this code from being completed?

I know getting the token has no issues, as if I remove the return and place a print within the request, I get my expected result.

If you have any questions, please ask

George
  • 25,988
  • 10
  • 79
  • 133
  • 1
    Don't wait, notify. Your approach is wrong. The `DispatchGroup` API is for synchronizing multiple asynchronous tasks in a loop. You have to write a completion handler. Please learn to understand asynchronous data processing. – vadian Jun 25 '18 at 18:53
  • @vadian And how would I do a completion handler? Can you send a link or a chunk of code? – George Jun 25 '18 at 18:55
  • I wrote an answer. – vadian Jun 25 '18 at 19:01

1 Answers1

3

Don't try to make an asynchronous task synchronous

This is a solution with a completion handler and a custom enum for convenience

enum Result {
    case success(Spotify.JSONStandard), failure(Error)
}

func getAccessToken(completion: @escaping (Result)->()) {
    Alamofire.request("https://accounts.spotify.com/api/token", method: .post, parameters: spotify.parameters, headers: nil).responseJSON(completionHandler: {
        response in
        // Check if response is valid
        if let newValue = response.result.value as? Spotify.JSONStandard {
            completion(.success(newValue)
        } else {
            completion(.failure(SpotifyError.failedToGetAccessToken))
        }
    })
}

and call it

getAccessToken { result in
   switch result {
     case .success(let token) : // do something with the token
     case .failure(let error) : // do something with the error
   }
}
vadian
  • 274,689
  • 30
  • 353
  • 361