1

Say I have two promises I want to combine with a when(resolved:). I want to reject the promise if there was a problem with the first promise, but resolve otherwise. Essentially, this is what I want to do:

func personAndPetPromise() -> Promise<(Person, Pet?)> {
    let personPromise: Promise<Person> = ...
    let petPromise: Promise<Pet> = ...

    when(resolved: personPromise, petPromise).then { _ -> (Person, Pet?) in
        if let error = personPromise.error {
            return Promise(error: error) // syntax error
        }
        guard let person = personPromise.value else {
            return Promise(error: myError) // syntax error
        }
        return (person, petPromise.value)
    }
}

such that externally I can do something like this:

personAndPetPromise().then { person, pet in
    doSomethingWith(person, pet)
}.catch { error in
    showError(error)
}

The problem lies within the the then { _ in block in personAndPetPromise. There's no way that method can return both a Promise(error:) and a (Person, Pet?).

How else can I reject the block?

Senseful
  • 86,719
  • 67
  • 308
  • 465

1 Answers1

2

The problem is that there are two overloads of the then function:

public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> U) -> Promise<U>
public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> Promise<U>) -> Promise<U>

The first one's body returns a U and causes then to return Promise<U>.

The second one's body returns a Promise<U> and causes then to return Promise<U>.

Since in this case we want to return an error or a valid response, we're forced to use the second overload.

Here's a working version. The main difference is I changed it from -> (Person, Pet?) to -> Promise<(Person, Pet?)>:

func personAndPetPromise() -> Promise<(Person, Pet?)> {
    let personPromise: Promise<Person> = ...
    let petPromise: Promise<Pet> = ...

    when(resolved: personPromise, petPromise).then { _ -> Promise<(Person, Pet?)> in
        if let error = personPromise.error {
            return Promise(error: error)
        }
        guard let person = personPromise.value else {
            return Promise(error: myError)
        }
        return Promise(value: (person, petPromise.value))
    }
}

Another way to do the same thing is by throwing the error rather than attempting to return it:

func personAndPetPromise() -> Promise<(Person, Pet?)> {
    let personPromise: Promise<Person> = ...
    let petPromise: Promise<Pet> = ...

    when(resolved: personPromise, petPromise).then { _ -> (Person, Pet?) in
        if let error = personPromise.error {
            throw error
        }
        guard let person = personPromise.value else {
            throw myError
        }
        return (person, petPromise.value)
    }
}
Senseful
  • 86,719
  • 67
  • 308
  • 465