7ball
7ball

Reputation: 2315

Chaining Alamofire requests with Swift and PromiseKit

I have 2 API endpoints; the latter depends on the result of the first.

The first end point is /api/v1/regions/, which returns a list of region JSON like so

{
  region_id: 1,
  mayor_id: 9
},
{
  region_id: 1,
  mayor_id: 10
},

The second end point is /api/v1/mayor/<id>/, which returns a JSON about the mayor. My workflow right now is to make the the first API call to get all the regions, then I want to make a bunch of API calls to the /mayor/ endpoint based on the IDs I get from the first end point. So in this example, I'd like to make 2 more calls:

/api/v1/mayor/9/
/api/v1/mayor/10/

I've already set up 2 functions to make each API call and successfully got the JSON back for each.

func fetchRegions() -> Promise<[Region]> {
}

func fetchMayor(id: String) -> Promise<Mayor> {
}

Now I'd like to see how I could chain all of these together. This is what I have so far:

var fetchedRegions: [Region] = []
firstly {
  fetchRegions()
}.then { regions in
  fetchedRegions = regions
}.then {
  for r in fetchedRegions {
    self.fetchMayor(id: r.mayor_id).then { mayor in
      print(mayor)
    }.catch { error in
    }
  }
}.catch { error in // Error: Missing return in a closure expected to return 'AnyPromise'
  print(error)
}

Upvotes: 2

Views: 818

Answers (1)

Ihar Katkavets
Ihar Katkavets

Reputation: 1570

You need to use when(fulfilled:) operator. It waits for all promises in a set to fulfill.

when(fulfilled: promise1, promise2).then { results in
    //…
}.catch { error in
    switch error {
    case URLError.notConnectedToInternet:
    //…
    case CLError.denied:
    //…
    }
}

Note: If any of the provided promises reject, the returned promise is immediately rejected with that error.
Warning: In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject. This is the right pattern for getter style asynchronous tasks, but often for setter tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed, in such situations use when(resolved:).

In according to your example it looks following (I explicitly defined all in/out parameters):

    fetchRegions() // fetch your regions async
        .then { (regions: [Region])  -> Promise<[Mayor]> in // here you got array [Region]
            var tasks: [Promise<Mayor>] = [] // create an array of promises to fetch 'Mayor'
            for region in regions {
                tasks.append(self.fetchMayor(id: region.mayorId))
            }

            return when(fulfilled: tasks) // create promise which wait for all promises in a set to fulfill
        }.then { (mayours: [Mayor]) -> Void in // here you will get an array of `Mayor`
            // do here you want
        }.catch { error in 
            print(error)
    }

Upvotes: 1

Related Questions