1

I'm dealing with a weird API where I receive a list of IDs, and I need to request data on each of those IDs individually. I don't need to chain these requests one after the other, I just want to grab them all at once, but I'm having trouble figuring out how to do this in a clean way.

I've made the method for getting one ID, it produces a Promise<DataObject>. How do I turn my array of IDs, into a collection of promises that will then give me [DataObject]

func fetchDataObject(_ id: Int64) -> Promise<DataObject> {
    return makeURL("\(id)")
        .then {
            URLSession.shared.dataTask(.promise, with: $0)
        }.compactMap { (data, response) -> DataObject in
            // ...
            return try decoder.decode(DataObject.self, from: data)
    }
}

// get the list of IDs and turn them into DataObjects

func fetchNew() -> Promise<[DataObject]> { // desired output
    return makeURL("all_ids").then {
        URLSession.shared.dataTask(.promise, with: $0)
        }.compactMap { (data, response) -> [Int64] in
            // ...
            return try decoder.decode([Int64].self, from: data)
        }.then({ (ids) -> [DataObject] in
            // now what???
        })
}

I think I should be using .when but I can't find a clear choice for which method signature... and how to create an array of promises to pass in.

ray
  • 1,966
  • 4
  • 24
  • 39
  • Conceptually, you're looking for `all`, but, reading through the [FAQ](https://github.com/mxcl/PromiseKit/blob/master/Documentation/FAQ.md#where-is-all), PromiseKit calls it `when`, which takes you back the [CookBook](https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md#when) for an example. This may only allow you to pass in a small number of request (2-3 for example), but I don't use PromiseKit anymore (it was a little buggy when I first tried it) and moved to [Hydra](https://github.com/malcommac/Hydra) instead so I'm only guessing – MadProgrammer Sep 06 '18 at 23:35

2 Answers2

6

Here’s your answer, but as requested “cleaner“.

func fetchObjects() -> Promise<[DataObject]> {
    return firstly {
        makeURL("all_ids")
    }.then {
        URLSession.shared.dataTask(.promise, with: $0).validate()
    }.map {
        try decoder.decode([Int64].self, from: $0.data)
    }.thenMap {
        self.fetchDataObject($0)
    }
}

The key change is the thenMap which applies a promise to each element of a sequence returning a Promise<[T]>.

Also using Swift one-line closure syntaxes to remove returns and parameter names where they don’t add much value.

firstly to make reading the chain clearer.

Using validate() on the URLSession promise to reject the chain if the HTTP returns a non-2xx response.

Replacing compactMap with map since decode() doesn’t return an optional.

mxcl
  • 26,392
  • 12
  • 99
  • 98
  • Thanks, I had different problem, but couldn't figure out ".thenMap" part. Your answered solved my problem too! – bojan Sep 15 '20 at 14:12
0

Okay, I got something that works and I better understand then and when(fulfilled:) now.

You need to return the when in your then.

func fetchObjects() -> Promise<[DataObject]> {
    return makeURL("all_ids")
        .then {
            URLSession.shared.dataTask(.promise, with: $0)
        }.compactMap { (data, response) -> [Int64] in
            // ..
            return try decoder.decode([Int64].self, from: data)
        }.then { (ids) -> Promise<[DataObject]> in
            var requests: [Promise<DataObject>] = []
            for id in ids {
                requests.append(self.fetchDataObject(id))
            }
            return when(fulfilled: requests)
    }
}
ray
  • 1,966
  • 4
  • 24
  • 39