2

Inside my app, I do few requests to my back-end. In one case I have a function that should return two parameters (may be Bool and Bool for example). But there comes my problem. To return those two parameters, I need to receive some info from back-end first, so it would look like this (will be written in pseudo-swift-code):

func request() -> (Bool, Bool) {
    var one: Bool?
    var two: Bool?

    Alamofire.request(url1).response { (response) in
        if let response = response { one = response }
    }

    Alamofire.request(url2).response { (response) in
        if let response = response { two = response }
    }

    if let one = one, let two = two {
        return (one, two)
    }

    return (nil, nil)
}

How do I achieve a function that waits for those two web requests?

mcgtrt
  • 657
  • 6
  • 22
  • The call is async and you are trying to return a data synchronous. this cant happen – Cerlin Jul 10 '18 at 10:02
  • 2
    I guess there are several ways to tackle this, one of which is any library that implements the async/await or promises pattern. [Here is a nice one](https://github.com/freshOS/then) – Alladinian Jul 10 '18 at 10:02

4 Answers4

4

Usually, when facing such a case, you would not be able to -directly- return a value from a function. I would suggest that you should follow the approach of making a callback for the method instead (using @escaping closure).

On the other hand, you'd need to execute the callback when both of the requests are done. One of the possible solutions is to use dispatch group (you could check: Call completion block when two other completion blocks have been called).

For putting all together, your function could be look like this:

func request(callback: @escaping (_ one: Bool?,  _ two: Bool?) -> Void) {
    var one: Bool?
    var two: Bool?

    let group = DispatchGroup()

    group.enter()
    Alamofire.request(url1).response { (response) in
        if let response = response { one = response }
        group.leave()
    }

    group.enter()
    Alamofire.request(url2).response { (response) in
        if let response = response { two = response }
        group.leave()
    }

    group.notify(queue: DispatchQueue.main) {
        if let one = one, let two = two {
            callback(one, two)
        } else {
            callback(nil, nil)
        }
    }
}

Thus you would call it as:

request { (one, two) in
    // ...
}
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
1
func request() -> (Bool?, Bool?) {
    var one: Bool?
    var two: Bool?
    let semaphore = DispatchSemaphore(value: 0)
    Alamofire.request(url1).response { (response) in
        if let response = response { one = response }
        semaphore.signal()
    }
    Alamofire.request(url2).response { (response) in
        if let response = response { two = response }
        semaphore.signal()
    }
    _ = semaphore.wait(timeout: DispatchTime.distantFuture)
    _ = semaphore.wait(timeout: DispatchTime.distantFuture)
    if let one = one, let two = two {
        return (one, two)
    }
    return (nil, nil)
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
  • I think reviewing [this one](https://stackoverflow.com/questions/49923810/when-to-use-semaphore-instead-of-dispatch-group) would lead to a pretty cool discussion ;) – Ahmad F Jul 10 '18 at 10:13
0

You can try something like this

var one: Bool?
var two: Bool?

func request() {

    Alamofire.request(url1).response { (response) in
        if let response = response { one = response }
        processResponse()
    }

    Alamofire.request(url2).response { (response) in
        if let response = response { two = response }
        processResponse()
    }
}

func processResponse() {
    if let one = one, let two = two {
        // do something
    }
}
Cerlin
  • 6,622
  • 1
  • 20
  • 28
0

Try below:

func request(completion: ((Bool?, Bool?) -> Void)?) {
    var one: Bool?
    var two: Bool?

    Alamofire.request(url1).response { (response) in
        if let response = response { one = response }
        Alamofire.request(url2).response { (response) in
             if let response = response { two = response }

             completion?(one, two)
        }
   }
}
Moayad Al kouz
  • 1,342
  • 1
  • 9
  • 19
  • This solution defines the direction of requests – Vyacheslav Jul 10 '18 at 10:13
  • 2
    2 comments on this solution: 1- it is **slower**, you are following the approach of serializing the process (which is not the wanted behavior). Instead, both of requests should work in parallel. 2- It's illegal! it won't work as expected because `completion` *not* escaping by default :) – Ahmad F Jul 10 '18 at 10:17