PromiseKit Documentation

Promises for Swift & ObjC.

14,211 stars Swift Swift 12 files · ~20,372 tokens #objc#promises#swift
RAW Doc

Technical Documentation: mxcl/PromiseKit

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)


Promises simplify asynchronous programming, freeing you up to focus on the more
important things. They are easy to learn, easy to master and result in clearer,
more readable code. Your co-workers will thank you.

swift
UIApplication.shared.isNetworkActivityIndicatorVisible = true

let fetchImage = URLSession.shared.dataTask(.promise, with: url).compactMap{ UIImage(data: $0.data) }
let fetchLocation = CLLocationManager.requestLocation().lastValue

firstly {
    when(fulfilled: fetchImage, fetchLocation)
}.done { image, location in
    self.imageView.image = image
    self.label.text = "\(location)"
}.ensure {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}.catch { error in
    self.show(UIAlertController(for: error), sender: self)
}

PromiseKit is a thoughtful and complete implementation of promises for any
platform that has a swiftc. It has excellent Objective-C bridging and
delightful specializations for iOS, macOS, tvOS and watchOS. It is a top-100
pod used in many of the most popular apps in the world.

Quick Start

In your Podfile:

ruby
use_frameworks!

target "Change Me!" do
  pod "PromiseKit", "~> 8"
end

PromiseKit 8 supports recent Xcodes (13+). Some Podspecs were
dropped as a result.
Pull requests are welcome.

PromiseKit 6, 5 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1,
3.2, 3.3, 3.4, 4.0, 4.1, 4.2, 4.3 and 5.0 (development snapshots); iOS, macOS,
tvOS, watchOS, Linux and Android; CocoaPods, Carthage and SwiftPM;
(CI Matrix).

For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our Installation Guide. We recommend
Carthage or
Accio.

PromiseKit and Swift 5.5+ Async/Await

As of Swift 5.5, the Swift language now offers support for built-in concurrency with async / await. See Async+ for a port of PromiseKit's most useful patterns to this new paradigm.

Professionally Supported PromiseKit is Now Available

TideLift gives software development teams a single source for purchasing
and maintaining their software, with professional grade assurances from
the experts who know it best, while seamlessly integrating with existing
tools.

Get Professional Support for PromiseKit with TideLift.

Documentation

Extensions

Promises are only as useful as the asynchronous tasks they represent. Thus, we
have converted (almost) all of Apple’s APIs to promises. The default CocoaPod
provides Promises and the extensions for Foundation and UIKit. The other
extensions are available by specifying additional subspecs in your Podfile,
e.g.:

ruby
pod "PromiseKit/MapKit"          # MKDirections().calculate().then { /*…*/ }
pod "PromiseKit/CoreLocation"    # CLLocationManager.requestLocation().then { /*…*/ }

All our extensions are separate repositories at the PromiseKit organization.

I don't want the extensions!

Then don’t have them:

ruby
pod "PromiseKit/CorePromise", "~> 8"

Note: Carthage installations come with no extensions by default.

Networking

Promise chains commonly start with a network operation. Thus, we offer
extensions for URLSession:

swift
// pod 'PromiseKit/Foundation'  # https://github.com/PromiseKit/Foundation

firstly {
    URLSession.shared.dataTask(.promise, with: try makeUrlRequest()).validate()
    // ^^ we provide `.validate()` so that eg. 404s get converted to errors
}.map {
    try JSONDecoder().decode(Foo.self, with: $0.data)
}.done { foo in
    //…
}.catch { error in
    //…
}

func makeUrlRequest() throws -> URLRequest {
    var rq = URLRequest(url: url)
    rq.httpMethod = "POST"
    rq.addValue("application/json", forHTTPHeaderField: "Content-Type")
    rq.addValue("application/json", forHTTPHeaderField: "Accept")
    rq.httpBody = try JSONEncoder().encode(obj)
    return rq
}

Support for Alamofire is welcome, please submit a PR.

Support

Please check our Troubleshooting Guide, and
if after that you still have a question, ask at our Gitter chat channel or on our bug tracker.

Security & Vulnerability Reporting or Disclosure

https://tidelift.com/security


Overview (`Documentation/README.md`)

Contents


Overview (`Tests/A+/README.md`)

Resources

Skipped

  • 2.3.3: Otherwise, if x is an object or function.
    This spec is a NOOP for Swift:
    • We have decided not to interact with other Promises A+ implementations
    • functions cannot have properties
  • 2.3.3.4: If then is not a function, fulfill promise with x.
    • See: The 2.3.4 suite.

Overview (`Tests/JS-A+/README.md`)

Promises/A+ Compliance Test Suite (JavaScript)

What is this?

This contains the necessary Swift and JS files to run the Promises/A+ compliance test suite from PromiseKit's unit tests.

Run tests

text
$ npm install
$ npm run build

then open PromiseKit.xcodeproj and run the PMKJSA+Tests unit test scheme.

Known limitations

See ignoredTests in index.js.

  • 2.3.3 is disabled: Otherwise, if x is an object or function. This spec is a NOOP for Swift:
  • We have decided not to interact with other Promises A+ implementations
  • functions cannot have properties

Upgrade the test suite

text
$ npm install --save promises-aplus-tests@latest
$ npm run build

Develop

JavaScriptCore is a bit tedious to work with so here are a couple tips in case you're trying to debug the test suite.

If you're editing JS files, enable live rebuilds:

text
$ npm run watch

If you're editing Swift files, a couple things you can do:

  • You can adjust testName in AllTests.swift to only run one test suite
  • You can call JSUtils.printCurrentStackTrace() at any time. It won't contain line numbers but some of the frame names might help.

How it works

The Promises/A+ test suite is written in JavaScript but PromiseKit is written in Swift/ObjC. For the test suite to run against swift code, we expose a promise wrapper JSPromise inside a JavaScriptCore context. This is done in a regular XCTestCase.

Since JavaScriptCore doesn't support CommonJS imports, we inline all the JavaScript code into build/build.js using webpack. This includes all the npm dependencies (promises-aplus-tests, mocha, sinon, etc) as well as the glue code in index.js.

build.js exposes one global variable runTests(adapter, onFail, onDone, [testName]). In our XCTestCase, a shared JavaScriptCore context is created, build.js is evaluated and now runTests is accessible from the Swift context.

In our swift test, we create a JS-bridged JSPromise which only has one method then(onFulfilled, onRejected) -> Promise. It wraps a swift Promise and delegates call then calls to it.

An adapter – plain JS object which provides revoled(value), rejected(reason), and deferred() – is passed to runTests to run the whole JavaScript test suite.

Errors and end events are reported back to Swift and piped to XCTFail() if necessary.

Since JavaScriptCore isn't a node/web environment, there is quite a bit of stubbing necessary for all this to work:

  • The fs module is stubbed with an empty function
  • console.log redirects to Swift.print and provides only basic format parsing
  • setTimeout/setInterval are implemented with Swift.Timer behind the scenes and stored in a [TimerID: Timer] map.

Documentation Appendix (`Documentation/Appendix.md`)

Common Misusage

Doubling up Promises

Don’t do this:

swift
func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
    return Promise { seal in
        firstly {
            setNetworkActivityIndicatorVisible(true)
            return funcToCall()
        }.then { result in
            seal.fulfill(result)
        }.always {
            setNetworkActivityIndicatorVisible(false)
        }.catch { err in
            seal.reject(err)
        }
    }
}

Do this:

swift
func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
    return firstly {
        setNetworkActivityIndicatorVisible(true)
        return funcToCall()
    }.always {
        setNetworkActivityIndicatorVisible(false)
    }
}

You already had a promise, you don’t need to wrap it in another promise.

Optionals in Promises

When we see Promise, it usually implies a misuse of promises. For
example:

swift
return firstly {
    getItems()
}.then { items -> Promise<[Item]?> in
    guard !items.isEmpty else {
        return .value(nil)
    }
    return Promise(value: items)
}

The second then chooses to return nil in some circumstances. This choice
imposes the need to check for nil on the consumer of the promise.

It's usually better to shunt these sorts of exceptions away from the
happy path and onto the error path. In this case, we can create a specific
error type for this condition:

swift
return firstly {
    getItems()
}.map { items -> [Item]> in
    guard !items.isEmpty else {
        throw MyError.emptyItems
    }
    return items
}

Note: Use compactMap when an API outside your control returns an Optional and you want to generate an error instead of propagating nil.

Tips n’ Tricks

Background-Loaded Member Variables

swift
class MyViewController: UIViewController {
    private let ambience: Promise<AVAudioPlayer> = DispatchQueue.global().async(.promise) {
        guard let asset = NSDataAsset(name: "CreepyPad") else { throw PMKError.badInput }
        let player =  try AVAudioPlayer(data: asset.data)
        player.prepareToPlay()
        return player
    }
}

Chaining Animations

swift
firstly {
    UIView.animate(.promise, duration: 0.3) {
        self.button1.alpha = 0
    }
}.then {
    UIView.animate(.promise, duration: 0.3) {
        self.button2.alpha = 1
    }
}.then {
    UIView.animate(.promise, duration: 0.3) {
        adjustConstraints()
        self.view.layoutIfNeeded()
    }
}

Voiding Promises

It is often convenient to erase the type of a promise to facilitate chaining.
For example, UIView.animate(.promise) returns Guarantee because UIKit’s
completion API supplies a Bool. However, we usually don’t need this value and
can chain more simply if it is discarded (that is, converted to Void). We can use
asVoid() to achieve this conversion:

swift
UIView.animate(.promise, duration: 0.3) {
    self.button1.alpha = 0
}.asVoid().done(self.nextStep)

For situations in which we are combining many promises into a when, asVoid()
becomes essential:

swift
let p1 = foo()
let p2 = bar()
let p3 = baz()
//…
let p10 = fluff()

when(fulfilled: p1.asVoid(), p2.asVoid(), /*…*/, p10.asVoid()).then {
    let value1 = p1.value!  // safe bang since all the promises fulfilled
    // …
    let value10 = p10.value!
}.catch {
    //…
}

You normally don't have to do this explicitly because when does it for you
for up to 5 parameters.

Blocking (Await)

Sometimes you have to block the main thread to await completion of an asynchronous task.
In these cases, you can (with caution) use wait:

swift
public extension UNUserNotificationCenter {
    var wasPushRequested: Bool {
        let settings = Guarantee(resolver: getNotificationSettings).wait()
        return settings != .notDetermined
    }
}

The task under the promise must not call back onto the current thread or it
will deadlock.

Starting a Chain on a Background Queue/Thread

firstly deliberately does not take a queue. A detailed rationale for this choice
can be found in the ticket tracker.

So, if you want to start a chain by dispatching to the background, you have to use
DispatchQueue.async:

swift
DispatchQueue.global().async(.promise) {
    return value  
}.done { value in
    //…
}

However, this function cannot return a promise because of Swift compiler ambiguity
issues. Thus, if you must start a promise on a background queue, you need to
do something like this:

swift
Promise { seal in
    DispatchQueue.global().async {
        seal(value)
    }  
}.done { value in
    //…
}

Or more simply (though with caveats; see the documentation for wait):

swift
DispatchQueue.global().async(.promise) {
    return try fetch().wait()
}.done { value in
    //…
}

However, you shouldn't need to do this often. If you find yourself wanting to use
this technique, perhaps you should instead modify the code for fetch to make it do
its work on a background thread.

Promises abstract asynchronicity, so exploit and support that model. Design your
APIs so that consumers don’t have to care what queue your functions run on.


Documentation CommonPatterns (`Documentation/CommonPatterns.md`)

Common Patterns

One feature of promises that makes them particularly useful is that they are composable.
This fact enables complex, yet safe asynchronous patterns that would otherwise be quite
intimidating when implemented with traditional methods.

Chaining

The most common pattern is chaining:

swift
firstly {
    fetch()
}.then {
    map($0)
}.then {
    set($0)
    return animate()
}.ensure {
    // something that should happen whatever the outcome
}.catch {
    handle(error: $0)
}

If you return a promise in a then, the next then waits on that promise
before continuing. This is the essence of promises.

Promises are easy to compose, so they encourage you to develop highly asynchronous
apps without fear of the spaghetti code (and associated refactoring pains) of
asynchronous systems that use completion handlers.

APIs That Use Promises

Promises are composable, so return them instead of accepting completion blocks:

swift
class MyRestAPI {
    func user() -> Promise<User> {
        return firstly {
            URLSession.shared.dataTask(.promise, with: url)
        }.compactMap {
            try JSONSerialization.jsonObject(with: $0.data) as? [String: Any]
        }.map { dict in
            User(dict: dict)
        }
    }

    func avatar() -> Promise<UIImage> {
        return user().then { user in
            URLSession.shared.dataTask(.promise, with: user.imageUrl)
        }.compactMap {
            UIImage(data: $0.data)
        }
    }
}

This way, asynchronous chains can cleanly and seamlessly incorporate code from all over
your app without violating architectural boundaries.

Note: We provide promises for Alamofire too!

Background Work

swift
class MyRestAPI {
    func avatar() -> Promise<UIImage> {
        let bgq = DispatchQueue.global(qos: .userInitiated)

        return firstly {
            user()
        }.then(on: bgq) { user in
            URLSession.shared.dataTask(.promise, with: user.imageUrl)
        }.compactMap(on: bgq) {
            UIImage(data: $0)
        }
    }
}

All PromiseKit handlers take an on parameter that lets you designate the dispatch queue
on which to run the handler. The default is always the main queue.

PromiseKit is entirely thread safe.

Tip: With caution, you can have all then, map, compactMap, etc., run on
a background queue. See PromiseKit.conf. Note that we suggest only changing
the queue for the map suite of functions, so done and catch will
continue to run on the main queue, which is usually what you want.

Failing Chains

If an error occurs mid-chain, simply throw an error:

swift
firstly {
    foo()
}.then { baz in
    bar(baz)
}.then { result in
    guard !result.isBad else { throw MyError.myIssue }
    //…
    return doOtherThing()
}

The error will surface at the next catch handler.

Since promises handle thrown errors, you don't have to wrap calls to throwing functions
in a do block unless you really want to handle the errors locally:

swift
foo().then { baz in
    bar(baz)
}.then { result in
    try doOtherThing()
}.catch { error in
    // if doOtherThing() throws, we end up here
}

Tip: Swift lets you define an inline enum Error inside the function you
are working on. This isn’t great coding practice, but it's better than
avoiding throwing an error because you couldn't be bothered to define a good global
Error enum.

Abstracting Away Asynchronicity

swift
var fetch = API.fetch()

override func viewDidAppear() {
    fetch.then { items in
        //…
    }
}

func buttonPressed() {
    fetch.then { items in
        //…
    }
}

func refresh() -> Promise {
    // ensure only one fetch operation happens at a time

    if fetch.isResolved {
        startSpinner()
        fetch = API.fetch().ensure {
            stopSpinner()
        }
    }
    return fetch
}

With promises, you don’t need to worry about when your asynchronous operation
finishes. Just act like it already has.

Above, we see that you can call then as many times on a promise as you
like. All the blocks will be executed in the order they were added.

Chaining Sequences

When you have a series of tasks to perform on an array of data:

swift
// fade all visible table cells one by one in a “cascading” effect

var fade = Guarantee()
for cell in tableView.visibleCells {
    fade = fade.then {
        UIView.animate(.promise, duration: 0.1) {
            cell.alpha = 0
        }
    }
}
fade.done {
    // finish
}

Or if you have an array of closures that return promises:

swift
var foo = Promise()
for nextPromise in arrayOfClosuresThatReturnPromises {
    foo = foo.then(nextPromise)
    // ^^ you rarely would want an array of promises instead, since then
    // they have all already started, you may as well use `when()`
}
foo.done {
    // finish
}

Note: You usually want when(), since when executes all of its
component promises in parallel and so completes much faster. Use the pattern
shown above in situations where tasks must be run sequentially; animation
is a good example.

We also provide when(concurrently:), which lets you schedule more than
one promise at a time if you need to.

Timeout

swift
let fetches: [Promise<T>] = makeFetches()
let timeout = after(seconds: 4)

race(when(fulfilled: fetches).asVoid(), timeout).then {
    //…
}

race continues as soon as one of the promises it is watching finishes.

Make sure the promises you pass to race are all of the same type. The easiest way
to ensure this is to use asVoid().

Note that if any component promise rejects, the race will reject, too.

Minimum Duration

Sometimes you need a task to take at least a certain amount of time. (For example,
you want to show a progress spinner, but if it shows for less than 0.3 seconds, the UI
appears broken to the user.)

swift
let waitAtLeast = after(seconds: 0.3)

firstly {
    foo()
}.then {
    waitAtLeast
}.done {
    //…
}

The code above works because we create the delay before we do work in foo(). By the
time we get to waiting on that promise, either it will have already timed out or we will wait
for whatever remains of the 0.3 seconds before continuing the chain.

Cancellation

Promises don’t have a cancel function, but they do support cancellation through a
special error type that conforms to the CancellableError protocol.

swift
func foo() -> (Promise<Void>, cancel: () -> Void) {
    let task = Task(…)
    var cancelme = false

    let promise = Promise<Void> { seal in
        task.completion = { value in
            guard !cancelme else { return reject(PMKError.cancelled) }
            seal.fulfill(value)
        }
        task.start()
    }

    let cancel = {
        cancelme = true
        task.cancel()
    }

    return (promise, cancel)
}

Promises don’t have a cancel function because you don’t want code outside of
your control to be able to cancel your operations--unless, of course, you explicitly
want to enable that behavior. In cases where you do want cancellation, the exact way
that it should work will vary depending on how the underlying task supports cancellation.
PromiseKit provides cancellation primitives but no concrete API.

Important: Errors which conform to the CancellableError protocol do not normally trigger the .catch block.
Cancelation is neither success nor failure, so cancelled chains do not call catch handlers by default.
However you can intercept cancellation if you like:

swift
foo.then {
    //…
}.catch(policy: .allErrors) {
    // cancelled errors are handled *as well*
}

Important: Canceling a promise chain is not the same as canceling the underlying
asynchronous task. Promises are wrappers around asynchronicity, but they have no
control over the underlying tasks. If you need to cancel an underlying task, you
need to cancel the underlying task!

The library CancellablePromiseKit extends the concept of Promises to fully cover cancellable tasks.

Retry / Polling

swift
func attempt<T>(maximumRetryCount: Int = 3, delayBeforeRetry: DispatchTimeInterval = .seconds(2), _ body: @escaping () -> Promise<T>) -> Promise<T> {
    var attempts = 0
    func attempt() -> Promise<T> {
        attempts += 1
        return body().recover { error -> Promise<T> in
            guard attempts < maximumRetryCount else { throw error }
            return after(delayBeforeRetry).then(attempt)
        }
    }
    return attempt()
}

attempt(maximumRetryCount: 3) {
    flakeyTask(parameters: foo)
}.then {
    //…
}.catch { _ in
    // we attempted three times but still failed
}

In most cases, you should probably supplement the code above so that it re-attempts only for
specific error conditions.

Wrapping Delegate Systems

Be careful with Promises and delegate systems, as they are not always compatible.
Promises complete once, whereas most delegate systems may notify their delegate many
times. This is why, for example, there is no PromiseKit extension for a
UIButton.

A good example of an appropriate time to wrap delegation is when you need a
single CLLocation lookup:

swift
extension CLLocationManager {
    static func promise() -> Promise<CLLocation> {
        return PMKCLLocationManagerProxy().promise
    }
}

class PMKCLLocationManagerProxy: NSObject, CLLocationManagerDelegate {
    private let (promise, seal) = Promise<[CLLocation]>.pending()
    private var retainCycle: PMKCLLocationManagerProxy?
    private let manager = CLLocationManager()

    init() {
        super.init()
        retainCycle = self
        manager.delegate = self // does not retain hence the `retainCycle` property

        promise.ensure {
            // ensure we break the retain cycle
            self.retainCycle = nil
        }
    }

    @objc fileprivate func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        seal.fulfill(locations)
    }

    @objc func locationManager(_: CLLocationManager, didFailWithError error: Error) {
        seal.reject(error)
    }
}

// use:

CLLocationManager.promise().then { locations in
    //…
}.catch { error in
    //…
}

Please note: we provide this promise with our CoreLocation extensions at
https://github.com/PromiseKit/CoreLocation

Recovery

Sometimes you don’t want an error to cascade. Instead, you want to supply a default result:

swift
CLLocationManager.requestLocation().recover { error -> Promise<CLLocation> in
    guard error == MyError.airplaneMode else {
        throw error
    }
    return .value(CLLocation.savannah)
}.done { location in
    //…
}

Be careful not to ignore all errors, though! Recover only those errors that make sense to recover.

Promises for Modal View Controllers

swift
class ViewController: UIViewController {

    private let (promise, seal) = Guarantee<…>.pending()  // use Promise if your flow can fail

    func show(in: UIViewController) -> Promise<…> {
        in.show(self, sender: in)
        return promise
    }

    func done() {
        dismiss(animated: true)
        seal.fulfill(…)
    }
}

// use:

ViewController().show(in: self).done {
    //…
}.catch { error in
    //…
}

This is the best approach we have found, which is a pity as it requires the
presentee to control the presentation and requires the presentee to dismiss itself
explicitly.

Nothing seems to beat storyboard segues for decoupling an app's controllers.

Saving Previous Results

Let’s say you have:

swift
login().then { username in
    fetch(avatar: username)
}.done { image in
    //…
}

What if you want access to both username and image in your done?

The most obvious way is to use nesting:

swift
login().then { username in
    fetch(avatar: username).done { image in
        // we have access to both `image` and `username`
    }
}.done {
    // the chain still continues as you'd expect
}

However, such nesting reduces the clarity of the chain. Instead, we could use Swift
tuples:

swift
login().then { username in
    fetch(avatar: username).map { ($0, username) }
}.then { image, username in
    //…
}

The code above simply maps Promise<String> into Promise<(UIImage, String)>.

Waiting on Multiple Promises, Whatever Their Result

Use when(resolved:):

swift
when(resolved: a, b).done { (results: [Result<T>]) in
    // `Result` is an enum of `.fulfilled` or `.rejected`
}

// ^^ cannot call `catch` as `when(resolved:)` returns a `Guarantee`

Generally, you don't want this! People ask for it a lot, but usually because
they are trying to ignore errors. What they really need is to use recover on one of the
promises. Errors happen, so they should be handled; you usually don't want to ignore them.


Documentation FAQ (`Documentation/FAQ.md`)

FAQ

Why should I use PromiseKit over X-Promises-Foo?

  • PromiseKit has a heavy focus on developer experience. You’re a developer; do you care about your experience? Yes? Then pick PromiseKit.
  • Do you care about having any bugs you find fixed? Then pick PromiseKit.
  • Do you care about having your input heard and reacted to in a fast fashion? Then pick PromiseKit.
  • Do you want a library that has been maintained continuously and passionately for 6 years? Then pick PromiseKit.
  • Do you want a library that the community has chosen to be their №1 Promises/Futures library? Then pick PromiseKit.
  • Do you want to be able to use Promises with Apple’s SDKs rather than having to do all the work of writing the Promise implementations yourself? Then pick PromiseKit.
  • Do you want to be able to use Promises with Swift 3.x, Swift 4.x, ObjC, iOS, tvOS, watchOS, macOS, Android & Linux? Then pick PromiseKit.
  • PromiseKit verifies its correctness by testing against the entire Promises/A+ test suite.

How do I create a fulfilled `Void` promise?

swift
let foo = Promise()

// or:

let bar = Promise.value(())

How do I “early `return`”?

swift
func foo() -> Promise<Void> {
   guard thingy else {
       return Promise()
   }

    //…
}

func bar() -> Promise<SomethingNotVoid> {
    guard thingy else {
        return .value(instanceOfSomethingNotVoid)
    }
    
    //…
}

Do I need to worry about retain cycles?

Generally, no. Once a promise completes, all handlers are released and so
any references to self are also released.

However, if your chain contains side effects that you would typically
not want to happen after, say, a view controller is popped, then you should still
use weak self (and check for self == nil) to prevent any such side effects.

However, in our experience most things that developers consider side effects that
should be protected against are in fact not side effects.

Side effects include changes to global application state. They do not include
changing the display state of a view-controller. So, protect against setting UserDefaults or
modifying the application database, and don't bother protecting against changing
the text in a UILabel.

This StackOverflow question
has some good discussion on this topic.

Do I need to retain my promises?

No. Every promise handler retains its promise until the handler is executed. Once
all handlers have been executed, the promise is deallocated. So you only need to retain
the promise if you need to refer to its final value after its chain has completed.

Where should I put my `catch`?

catch deliberately terminates the chain. You should put it low in your promise
hierarchy at a point as close to the root as possible. Typically, this would be
somewhere such as a view controller, where your catch can then display a message
to the user.

This means you should be writing one catch for many thens and returning
promises that do not have internal catch handlers of their own.

This is obviously a guideline; do what is necessary.

How do branched chains work?

Suppose you have a promise:

text
let promise = foo()

And you call then twice:

text
promise.then {
    // branch A
}

promise.then {
    // branch B
}

You now have a branched chain. When promise resolves, both chains receive its
value. However, the two chains are entirely separate and Swift will prompt you
to ensure that both have catch handlers.

You can most likely ignore the catch for one of these branches, but be careful:
in these situations, Swift cannot help you ensure that your chains are error-handled.

text
promise.then {
    // branch A
}.catch { error in
    //…
}

_ = promise.then {
    print("foo")
    
    // ignoring errors here as print cannot error and we handle errors above
}

It may be safer to recombine the two branches into a single chain again:

text
let p1 = promise.then {
    // branch A
}

let p2 = promise.then {
    // branch B
}

when(fulfilled: p1, p2).catch { error in
    //…
}

It's worth noting that you can add multiple catch handlers to a promise, too.
And indeed, both will be called if the chain is rejected.

Is PromiseKit “heavy”?

No. PromiseKit contains hardly any source code. In fact, it is quite lightweight. Any
“weight” relative to other promise implementations derives from 6 years of bug fixes
and tuning, from the fact that we have stellar Objective-C-to-Swift bridging and
from important things such as Zalgo prevention
that hobby-project implementations don’t consider.

Why is debugging hard?

Because promises always execute via dispatch, the backtrace you see at the point of
an error has less information than is usually required to trace the path of execution.

One solution is to turn off dispatch during debugging:

swift
// Swift
DispatchQueue.default = zalgo

//ObjC
PMKSetDefaultDispatchQueue(zalgo)

Don’t leave this on. In normal use, we always dispatch to avoid you accidentally writing
a common bug pattern. See this blog post.

Where is `all()`?

Some promise libraries provide all for awaiting multiple results. We call this function
when, but it is the same thing. We chose when because it's the more common term and
because we think it reads better in code.

How can I test APIs that return promises?

You need to use XCTestExpectation.

We also define wait() and hang(). Use them if you must, but be careful because they
block the current thread!

Is PromiseKit thread-safe?

Yes, entirely.

However the code you write in your thens might not be!

Just make sure you don’t access state outside the chain from concurrent queues.
By default, PromiseKit handlers run on the main thread, which is serial, so
you typically won't have to worry about this.

Why are there separate classes for Objective-C and Swift?

Promise<T> is generic and and so cannot be represented by Objective-C.

Does PromiseKit conform to Promises/A+?

Yes. We have tests that prove this.

How do PromiseKit and RxSwift/ReactiveSwift differ?

PromiseKit is a lot simpler.

The top-level difference between PromiseKit and RxSwift is that RxSwift Observables (roughly
analogous to PromiseKit Promises) do not necessarily return a single result: they may emit
zero, one, or an infinite stream of values. This small conceptual change leads to an API
that's both surprisingly powerful and surprisingly complex.

RxSwift requires commitment to a paradigm shift in how you program. It proposes that you
restructure your code as a matrix of interacting value pipelines. When applied properly
to a suitable problem, RxSwift can yield great benefits in robustness and simplicity.
But not all applications are suitable for RxSwift.

By contrast, PromiseKit selectively applies the best parts of reactive programming
to the hardest part of pure Swift development, the management of asynchronicity. It's a broadly
applicable tool. Most asynchronous code can be clarified, simplified and made more robust
just by converting it to use promises. (And the conversion process is easy.)

Promises make for code that is clear to most developers. RxSwift, perhaps not. Take a look at this
sign-up panel
implemented in RxSwift and see what you think. (Note that this is one of RxSwift's own examples.)

Even where PromiseKit and RxSwift are broadly similar, there are many differences in implementation:

  • RxSwift has a separate API for chain-terminating elements ("subscribers") versus interior
    elements. In PromiseKit, all elements of a chain use roughly the same code pattern.

  • The RxSwift API to define an interior element of a chain (an "operator") is hair-raisingly complex.
    So, RxSwift tries hard to supply every operator you might ever want to use right off the shelf. There are
    hundreds. PromiseKit supplies a few utilities to help with specific scenarios, but because it's trivial
    to write your own chain elements, there's no need for all this extra code in the library.

  • PromiseKit dispatches the execution of every block. RxSwift dispatches only when told to do so. Moreover, the
    current dispatching state is an attribute of the chain, not the specific block, as it is in PromiseKit.
    The RxSwift system is more powerful but more complex. PromiseKit is simple, predictable and safe.

  • In PromiseKit, both sides of a branched chain refer back to their shared common ancestors. In RxSwift,
    branching normally creates a duplicate parallel chain that reruns the code at the head of the chain...except
    when it doesn't. The rules for determining what will actually happen are complex, and given
    a chain created by another chunk of code, you can't really tell what the behavior will be.

  • Because RxSwift chains don't necessarily terminate on their own, RxSwift needs you to take on some
    explicit garbage collection duties to ensure that pipelines that are no longer needed are properly
    deallocated. All promises yield a single value, terminate and then automatically deallocate themselves.

You can find some additional discussion in this ticket.

Why can’t I return from a catch like I can in JavaScript?

Swift demands that functions have one purpose. Thus, we have two error handlers:

  • catch: ends the chain and handles errors
  • recover: attempts to recover from errors in a chain

You want recover.

When do promises “start”?

Often people are confused about when Promises “start”. Is it immediately? Is it
later? Is it when you call then?

The answer is: The promise body executes during initialization of the promise, on the current thread.
As an example, "Executing the promise body" will be printed to the console right after the promise is created,
without having to call then on the promise.

swift
let testPromise = Promise<Bool> {
    print("Executing the promise body.")
    return $0.fulfill(true)
}

But what about asynchronous tasks that you create in your promise's body? They behave the same way as they would
without using PromiseKit. Here's a simple example:

swift
let testPromise = Promise<Bool> { seal in
    print("Executing the promise body.")
    DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
        print("Executing asyncAfter.")
        return seal.fulfill(true)
    }
}

The message "Executing the promise body." is being logged right away, but the message "Executing asyncAfter."
is only logged three seconds later. In this case DispatchQueue is responsible for deciding when to execute
the task you pass to it, PromiseKit has nothing to do with it.

What is a good way to use Firebase with PromiseKit

There is no good way to use Firebase with PromiseKit. See the next question for
a more detailed rationale.

The best option is to embed your chain in your Firebase handler:

text
foo.observe(.value) { snapshot in
    firstly {
        bar(with: snapshot)
    }.then {
        baz()
    }.then {
        baffle()
    }.catch {
        //…
    }
}

I need my `then` to fire multiple times

Then we’re afraid you cannot use PromiseKit for that event. Promises only
resolve once. This is the fundamental nature of promises and it is considered a
feature because it gives you guarantees about the flow of your chains.

How do I change the default queues that handlers run on?

You can change the values of PromiseKit.conf.Q. There are two variables that
change the default queues that the two kinds of handler run on. A typical
pattern is to change all your then-type handlers to run on a background queue
and to have all your “finalizers” run on the main queue:

text
PromiseKit.conf.Q.map = .global()
PromiseKit.conf.Q.return = .main  //NOTE this is the default

Be very careful about setting either of these queues to nil. It has the
effect of running immediately, and this is not what you usually want to do in
your application. This is, however, useful when you are running specs and want
your promises to resolve immediately. (This is basically the same idea as "stubbing"
an HTTP request.)

swift
// in your test suite setup code
PromiseKit.conf.Q.map = nil
PromiseKit.conf.Q.return = nil

How do I use PromiseKit on the server side?

If your server framework requires that the main queue remain unused (e.g., Kitura),
then you must use PromiseKit 6 and you must tell PromiseKit not to dispatch to the
main queue by default. This is easy enough:

swift
PromiseKit.conf.Q = (map: DispatchQueue.global(), return: DispatchQueue.global())

Note, we recommend using your own queue rather than .global(), we've seen better performance this way.

Here’s a more complete example:

swift
import Foundation
import HeliumLogger
import Kitura
import LoggerAPI
import PromiseKit

HeliumLogger.use(.info)

let pmkQ = DispatchQueue(label: "pmkQ", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem)
PromiseKit.conf.Q = (map: pmkQ, return: pmkQ)

let router = Router()
router.get("/") { _, response, next in
    Log.info("Request received")
    after(seconds: 1.0).done {
        Log.info("Sending response")
        response.send("OK")
        next()
    }
}

Log.info("Starting server")
Kitura.addHTTPServer(onPort: 8888, with: router)
Kitura.run()

How do I control console output?

By default PromiseKit emits console messages when certain events occur. These events include:

  • A promise or guarantee has blocked the main thread
  • A promise has been deallocated without being fulfilled
  • An error which occurred while fulfilling a promise was swallowed using cauterize

You may turn off or redirect this output by setting a thread safe closure in PMKConfiguration before processing any promises. For example, to turn off console output:

swift
conf.logHandler = { event in }

My question was not answered

Please open a ticket.


Documentation GettingStarted (`Documentation/GettingStarted.md`)

`then` and `done`

Here is a typical promise chain:

swift
firstly {
    login()
}.then { creds in
    fetch(avatar: creds.user)
}.done { image in
    self.imageView = image
}

If this code used completion handlers, it would look like this:

swift
login { creds, error in
    if let creds = creds {
        fetch(avatar: creds.user) { image, error in
            if let image = image {
                self.imageView = image
            }
        }
    }
}

then is just another way to structure completion handlers, but it is also quite a
bit more. At this initial stage of our understanding, it mostly helps
readability. The promise chain above is easy to scan and understand: one asynchronous
operation leads into the other, line by line. It's as close to
procedural code as we can easily come given the current state of Swift.

done is the same as then but you cannot return a promise. It is
typically the end of the “success” part of the chain. Above, you can see that we
receive the final image in our done and use it to set up the UI.

Let’s compare the signatures of the two login methods:

swift
func login() -> Promise<Creds>
    
// Compared with:

func login(completion: (Creds?, Error?) -> Void)
                        // ^^ ugh. Optionals. Double optionals.

The distinction is that with promises, your functions return promises instead
of accepting and running callbacks. Each handler in a chain returns a promise.
Promise objects define the then method, which waits for the completion of the
promise before continuing the chain. Chains resolve procedurally, one promise
at a time.

A Promise represents the future value of an asynchronous task. It has a type
that represents the type of object it wraps. For example, in the example above,
login is a function that returns a Promise that will represent an instance
of Creds.

Note: done is new to PromiseKit 5. We previously defined a variant of then that
did not require you to return a promise. Unfortunately, this convention often confused
Swift and led to odd and hard-to-debug error messages. It also made using PromiseKit
more painful. The introduction of done lets you type out promise chains that
compile without additional qualification to help the compiler figure out type information.


You may notice that unlike the completion pattern, the promise chain appears to
ignore errors. This is not the case! In fact, it has the opposite effect: the promise
chain makes error handling more accessible and makes errors harder to ignore.

`catch`

With promises, errors cascade along the promise chain, ensuring that your apps are
robust and your code is clear:

swift
firstly {
    login()
}.then { creds in
    fetch(avatar: creds.user)
}.done { image in
    self.imageView = image
}.catch {
    // any errors in the whole chain land here
}

Swift emits a warning if you forget to catch a chain. But we'll
talk about that in more detail later.

Each promise is an object that represents an individual, asynchronous task.
If a task fails, its promise becomes rejected. Chains that contain rejected
promises skip all subsequent thens. Instead, the next catch is executed.
(Strictly speaking, all subsequent catch handlers are executed.)

For fun, let’s compare this pattern with its completion handler equivalent:

swift
func handle(error: Error) {
    //…
}

login { creds, error in
    guard let creds = creds else { return handle(error: error!) }
    fetch(avatar: creds.user) { image, error in
        guard let image = image else { return handle(error: error!) }
        self.imageView.image = image
    }
}

The use of guard and a consolidated error handler help, but the promise chain’s
readability speaks for itself.

`ensure`

We have learned to compose asynchronicity. Next let’s extend our primitives:

swift
firstly {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    return login()
}.then {
    fetch(avatar: $0.user)
}.done {
    self.imageView = $0
}.ensure {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}.catch {
    //…
}

No matter the outcome of your chain—-failure or success—-your ensure
handler is always called.

Let’s compare this pattern with its completion handler equivalent:

swift
UIApplication.shared.isNetworkActivityIndicatorVisible = true

func handle(error: Error) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    //…
}

login { creds, error in
    guard let creds = creds else { return handle(error: error!) }
    fetch(avatar: creds.user) { image, error in
        guard let image = image else { return handle(error: error!) }
        self.imageView.image = image
        UIApplication.shared.isNetworkActivityIndicatorVisible = false
    }
}

It would be very easy for someone to amend this code and forget to unset
the activity indicator, leading to a bug. With promises, this type of error is
almost impossible: the Swift compiler resists your supplementing the chain without
using promises. You almost won’t need to review the pull requests.

Note: PromiseKit has perhaps capriciously switched between the names always
and ensure for this function several times in the past. Sorry about this. We suck.

You can also use finally as an ensure that terminates the promise chain and does not return a value:

text
spinner(visible: true)

firstly {
    foo()
}.done {
    //…
}.catch {
    //…
}.finally {
    self.spinner(visible: false)
}

`when`

With completion handlers, reacting to multiple asynchronous operations is either
slow or hard. Slow means doing it serially:

swift
operation1 { result1 in
    operation2 { result2 in
        finish(result1, result2)
    }
}

The fast (parallel) path code makes the code less clear:

swift
var result1: …!
var result2: …!
let group = DispatchGroup()
group.enter()
group.enter()
operation1 {
    result1 = $0
    group.leave()
}
operation2 {
    result2 = $0
    group.leave()
}
group.notify(queue: .main) {
    finish(result1, result2)
}

Promises are easier:

swift
firstly {
    when(fulfilled: operation1(), operation2())
}.done { result1, result2 in
    //…
}

when takes promises, waits for them to resolve and returns a promise containing the results.

As with any promise chain, if any of the component promises fail, the chain calls the next catch.

PromiseKit Extensions

When we made PromiseKit, we understood that we wanted to use only promises to implement
asynchronous behavior. So wherever possible, we offer extensions to Apple’s APIs that reframe
the API in terms of promises. For example:

swift
firstly {
    CLLocationManager.promise()
}.then { location in
    CLGeocoder.reverseGeocode(location)
}.done { placemarks in
    self.placemark.text = "\(placemarks.first)"
}

To use these extensions, you need to specify subspecs:

ruby
pod "PromiseKit"
pod "PromiseKit/CoreLocation"
pod "PromiseKit/MapKit"

All of these extensions are available at the PromiseKit organization.
Go there to see what's available and to read the source code and documentation. Every file and function
has been copiously documented.

We also provide extensions for common libraries such as Alamofire.

Making Promises

The standard extensions will take you a long way, but sometimes you'll still need to start chains
of your own. Maybe you're using a third party API that doesn’t provide promises, or perhaps you wrote
your own asynchronous system. Either way, it's easy to add promises. If you look at the code of the
standard extensions, you'll see that it uses the same approach described below.

Let’s say we have the following method:

swift
func fetch(completion: (String?, Error?) -> Void)

How do we convert this to a promise? Well, it's easy:

swift
func fetch() -> Promise<String> {
    return Promise { fetch(completion: $0.resolve) }
}

You may find the expanded version more readable:

swift
func fetch() -> Promise<String> {
    return Promise { seal in
        fetch { result, error in
            seal.resolve(result, error)
        }
    }
}

The seal object that the Promise initializer provides to you defines
many methods for handling garden-variety completion handlers. It even
covers a variety of rarer situations, thus making it easy for you to add
promises to an existing codebase.

Note: We tried to make it so that you could just do Promise(fetch), but we
were not able to make this simpler pattern work universally without requiring
extra disambiguation for the Swift compiler. Sorry; we tried.

Note: In PMK 4, this initializer provided two parameters to your closure:
fulfill and reject. PMK 5 and 6 give you an object that has both fulfill and
reject methods, but also many variants of the method resolve. You can
typically just pass completion handler parameters to resolve and let Swift figure
out which variant to apply to your particular case (as shown in the example above).

Note Guarantees (below) have a slightly different initializer (since they
cannot error) so the parameter to the initializer closure is just a closure. Not
a Resolver object. Thus do seal(value) rather than seal.fulfill(value). This
is because there is no variations in what guarantees can be sealed with, they can
only fulfill.

`Guarantee`

Since PromiseKit 5, we have provided Guarantee as a supplementary class to
Promise. We do this to complement Swift’s strong error handling system.

Guarantees never fail, so they cannot be rejected. A good example is after:

text
firstly {
    after(seconds: 0.1)
}.done {
    // there is no way to add a `catch` because after cannot fail.
}

Swift warns you if you don’t terminate a regular Promise chain (i.e., not
a Guarantee chain). You're expected to silence this warning by supplying
either a catch or a return. (In the latter case, you will then have to catch
at the point where you receive that promise.)

Use Guarantees wherever possible so that your code has error handling where
it's required and no error handling where it's not required.

In general, you should be able to use Guarantees and Promises interchangeably,
We have gone to great lengths to try and ensure this, so please open a ticket
if you find an issue.


If you are creating your own guarantees the syntax is simpler than that of promises;

swift
func fetch() -> Promise<String> {
    return Guarantee { seal in
        fetch { result in
            seal(result)
        }
    }
}

Which could be reduced to:

swift
func fetch() -> Promise<String> {
    return Guarantee(resolver: fetch)
}

`map`, `compactMap`, etc.

then provides you with the result of the previous promise and requires you to return
another promise.

map provides you with the result of the previous promise and requires you to return
an object or value type.

compactMap provides you with the result of the previous promise and requires you
to return an Optional. If you return nil, the chain fails with
PMKError.compactMap.

Rationale: Before PromiseKit 4, then handled all these cases, and it was
painful. We hoped the pain would disappear with new Swift versions. However,
it has become clear that the various pain points are here to stay. In fact, we
as library authors are expected to disambiguate at the naming level of our API.
Therefore, we have split the three main kinds of then into then, map and
done. After using these new functions, we realized this is much nicer in practice,
so we added compactMap as well (modeled on Optional.compactMap).

compactMap facilitates quick composition of promise chains. For example:

swift
firstly {
    URLSession.shared.dataTask(.promise, with: rq)
}.compactMap {
    try JSONSerialization.jsonObject($0.data) as? [String]
}.done { arrayOfStrings in
    //…
}.catch { error in
    // Foundation.JSONError if JSON was badly formed
    // PMKError.compactMap if JSON was of different type
}

Tip: We also provide most of the functional methods you would expect for sequences,
e.g., map, thenMap, compactMapValues, firstValue, etc.

`get`

We provide get as a done that returns the value fed to get.

swift
firstly {
    foo()
}.get { foo in
    //…
}.done { foo in
    // same foo!
}

`tap`

We provide tap for debugging. It's the same as get but provides the
Result<T> of the Promise so you can inspect the value of the chain at this
point without causing any side effects:

swift
firstly {
    foo()
}.tap {
    print($0)
}.done {
    //…
}.catch {
    //…
}

Supplement

`firstly`

We've used firstly several times on this page, but what is it, really? In fact,
it is just syntactic sugar.
You don’t really need it, but it helps to make your chains more readable. Instead of:

swift
firstly {
    login()
}.then { creds in
    //…
}

You could just do:

swift
login().then { creds in
    //…
}

Here is a key understanding: login() returns a Promise, and all Promises have a then function. firstly returns a Promise, and then returns a Promise, too! But don’t worry too much about these details. Learn the patterns to start with. Then, when you are ready to advance, learn the underlying architecture.

`when` Variants

when is one of PromiseKit’s more useful functions, and so we offer several variants.

  • The default when, and the one you should typically use, is when(fulfilled:). This variant
    waits on all its component promises, but if any fail, when fails too, and thus the chain rejects.
    It's important to note that all promises in the when continue. Promises have no control over
    the tasks they represent. Promises are just wrappers around tasks.

  • when(resolved:) waits even if one or more of its component promises fails. The value produced
    by this variant of when is an array of Result<T>. Consequently, this variant requires all its
    component promises to have the same generic type. See our advanced patterns guide for work-arounds
    for this limitation.

  • The race variant lets you race several promises. Whichever finishes first is the result. See the
    advanced patterns guide for typical usage.

Swift Closure Inference

Swift automatically infers returns and return types for one-line closures.
The following two forms are the same:

swift
foo.then {
    bar($0)
}

// is the same as:

foo.then { baz -> Promise<String> in
    return bar(baz)
}

Our documentation often omits the return for clarity.

However, this shorthand is both a blessing and a curse. You may find that the Swift compiler
often fails to infer return types properly. See our Troubleshooting Guide if
you require further assistance.

By adding done to PromiseKit 5, we have managed to avoid many of these common
pain points in using PromiseKit and Swift.

Further Reading

The above information is the 90% you will use. We strongly suggest reading the
API Reference.
There are numerous little
functions that may be useful to you, and the documentation for everything outlined above
is more thorough at the source.

In Xcode, don’t forget to option-click on PromiseKit functions to access this
documentation while you're coding.

Here are some recent articles that document PromiseKit 5+:

Careful with general online references, many of them refer to PMK < 5 which has a subtly
different API (sorry about that, but Swift has changed a lot over the years and thus
we had to too).


Documentation Installation (`Documentation/Installation.md`)

Xcode 8.3, 9.x or 10.x / Swift 3 or 4

We recommend Carthage over CocoaPods, but both installation methods are supported.

CocoaPods

ruby
use_frameworks!

target "Change Me!" do
  pod "PromiseKit", "~> 6.8"
end

If the generated Xcode project gives you a warning that PromiseKit needs to be upgraded to
Swift 4.0 or Swift 4.2, then add the following:

ruby
post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.name == 'PromiseKit'
      target.build_configurations.each do |config|
        config.build_settings['SWIFT_VERSION'] = '4.2'
      end
    end
  end
end

Adjust the value for SWIFT_VERSION as needed.

CocoaPods are aware of this issue.

Carthage

ruby
github "mxcl/PromiseKit" ~> 6.8

Please note, since PromiseKit 6.8.1 our Carthage support has transitioned to
Swift 4 and above only. Strictly we do still support Swift 3.1 for Carthage,
and if you like you could edit the PromiseKit project.pbxproj file during
carthage bootstrap to make this possible. This change was involuntary and due
to Xcode 10.2 dropping support for Swift 3.

From Xcode 12, you will likely need to build using --use-xcframeworks, eg:

text
carthage build --use-xcframeworks

Accio

Add the following to your Package.swift:

swift
.package(url: "https://github.com/mxcl/PromiseKit.git", .upToNextMajor(from: "6.8.4")),

Next, add PromiseKit to your App targets dependencies like so:

swift
.target(
    name: "App",
    dependencies: [
        "PromiseKit",
    ]
),

Then run accio update.

SwiftPM

swift
package.dependencies.append(
    .package(url: "https://github.com/mxcl/PromiseKit", from: "6.8.0")
)

Manually

You can just drop PromiseKit.xcodeproj into your project and then add
PromiseKit.framework to your app’s embedded frameworks.

PromiseKit vs. Xcode

PromiseKit contains Swift, so there have been rev-lock issues with Xcode:

PromiseKit Swift Xcode CI Status Release Notes
6 3.2, 3.3, 4.x, 5.x 8.3, 9.x, 10.x ci-master 2018/02
5 3.1, 3.2, 3.3, 4.x 8.3, 9.x, 10.1 Deprecated n/a
4 3.0, 3.1, 3.2, 3.3, 4.x 8.x, 9.x, 10.1 ci-master 2016/09
3 2.x 7.x, 8.0 ci-swift2 2015/10
2 1.x 7.x Deprecated 2015/10
1† N/A * ci-legacy –

† PromiseKit 1 is pure Objective-C and thus can be used with any Xcode, it is
also your only choice if you need to support iOS 7 or below.


We also maintain a series of branches to aid migration for PromiseKit 2:

Xcode Swift PromiseKit Branch CI Status
8.0 2.3 2 swift-2.3-minimal-changes ci-23
7.3 2.2 2 swift-2.2-minimal-changes ci-22
7.2 2.2 2 swift-2.2-minimal-changes ci-22
7.1 2.1 2 swift-2.0-minimal-changes ci-20
7.0 2.0 2 swift-2.0-minimal-changes ci-20

We do not usually backport fixes to these branches, but pull requests are welcome.

Xcode 8 / Swift 2.3 or Xcode 7

ruby
# CocoaPods
swift_version = "2.3"
pod "PromiseKit", "~> 3.5"

# Carthage
github "mxcl/PromiseKit" ~> 3.5

Using Git Submodules for PromiseKit’s Extensions

Note: This is a more advanced technique.

If you use CocoaPods and a few PromiseKit extensions, then importing PromiseKit
causes that module to import all the extension frameworks. Thus, if you have an
app and a few app extensions (e.g., iOS app, iOS watch extension, iOS Today
extension) then all your final products that use PromiseKit will have forced
dependencies on all the Apple frameworks that PromiseKit provides extensions
for.

This isn’t that bad, but every framework that loads entails overhead and
lengthens startup time.

It’s both better and worse with Carthage. We build individual micro-frameworks
for each PromiseKit extension, so your final products link
against only the Apple frameworks that they actually need. However, Apple has
advised that apps link only against “about 12” frameworks for performance
reasons. So with Carthage, we are worse off on this metric.

The solution is to instead import only CorePromise:

ruby
# CocoaPods
pod "PromiseKit/CorePromise"

# Carthage
github "mxcl/PromiseKit"
# ^^ for Carthage *only* have this

And to use the extensions you need via git submodules:

text
git submodule init
git submodule add https://github.com/PromiseKit/UIKit Submodules/PMKUIKit

Then in Xcode you can add these sources to your targets on a per-target basis.

Then when you pod update, ensure that you also update your submodules:

text
pod update && git submodule update --recursive --remote

Release History

[6.0](https://github.com/mxcl/PromiseKit/releases/tag/6.0.0) Feb 13th, 2018

[4.0](https://github.com/mxcl/PromiseKit/releases/tag/4.0.0)

[3.0](https://github.com/mxcl/PromiseKit/releases/tag/3.0.0) Oct 1st, 2015

In Swift 2.0 catch and defer became reserved keywords mandating we rename
our functions with these names. This forced a major semantic version change on
PromiseKit and thus we took the opportunity to make other minor (source
compatibility breaking) improvements.

Thus if you cannot afford to adapt to PromiseKit 3 but still want to use
Xcode-7.0/Swift-2.0 we provide a minimal changes branch where catch and
defer are renamed catch_ and defer_ and all other changes are the bare
minimum to make PromiseKit 2 compile against Swift 2.

If you still are using Xcode 6 and Swift 1.2 then use PromiseKit 2.

[2.0](https://github.com/mxcl/PromiseKit/releases/tag/2.0.0) May 14th, 2015

PromiseKit 2 announcement post.

[1.5](https://github.com/mxcl/PromiseKit/releases/tag/1.5.0)

Swift 1.2 support. Xcode 6.3 required.


Documentation ObjectiveC (`Documentation/ObjectiveC.md`)

Objective-C

PromiseKit has two promise classes:

  • Promise<T> (Swift)
  • AnyPromise (Objective-C)

Each is designed to be an appropriate promise implementation for the strong points of its language:

  • Promise<T> is strict, defined and precise.
  • AnyPromise is loose and dynamic.

Unlike most libraries, we have extensive bridging support, you can use PromiseKit
in mixed projects with mixed language targets and mixed language libraries.

Using PromiseKit with Objective-C

AnyPromise is our promise class for Objective-C. It behaves almost identically to Promise<T>, our Swift promise class.

objc
myPromise.then(^(NSString *bar){
    return anotherPromise;
}).then(^{
    //…
}).catch(^(NSError *error){
    //…
});

You make new promises using promiseWithResolverBlock:

objc
- (AnyPromise *)myPromise {
    return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve){
        resolve(foo);  // if foo is an NSError, rejects, else, resolves
    }];
}

You reject promises by throwing errors:

objc
myPromise.then(^{
    @throw [NSError errorWithDomain:domain code:code userInfo:nil];
}).catch(^(NSError *error){
    //…
});

One important feature is the syntactic flexibility of your handlers:

objc
myPromise.then(^{
    // no parameters is fine
});

myPromise.then(^(id foo){
    // one parameter is fine
});

myPromise.then(^(id a, id b, id c){
    // up to three parameter is fine, no crash!
});

myPromise.then(^{
    return @1; // return anything or nothing, it's fine, no crash
});

We do runtime inspection of the block you pass to achieve this magic.


Another important distinction is that the equivalent function to Swift’s recover is combined with AnyPromise’s catch. This is typical to other “dynamic” promise implementations and thus achieves our goal that AnyPromise is loose and dynamic while Promise<T> is strict and specific.

A sometimes unexpected consequence of this fact is that returning nothing from a catch resolves the returned promise:

objc
myPromise.catch(^{
    [UIAlertView …];
}).then(^{
    // always executes!
});

Another important distinction is that the value property returns even if the promise is rejected; in that case, it returns the NSError object with which the promise was rejected.

Bridging Between Objective-C & Swift

Let’s say you have:

objc
@interface Foo
- (AnyPromise *)myPromise;
@end

Ensure that this interface is included in your bridging header. You can now use the
following pattern in your Swift code:

swift
let foo = Foo()
foo.myPromise.then { (obj: AnyObject?) -> Int in
    // it is not necessary to specify the type of `obj`
    // we just do that for demonstrative purposes
}

Let’s say you have:

swift
@objc class Foo: NSObject {
    func stringPromise() -> Promise<String>    
    func barPromise() -> Promise<Bar>
}

@objc class Bar: NSObject { /*…*/ }

Ensure that your project is generating a …-Swift.h header so that Objective-C can see your Swift code.

If you built this project and opened the …-Swift.h header, you would only see this:

objc
@interface Foo
@end

@interface Bar
@end

That's because Objective-C cannot import Swift objects that are generic. So we need to write some stubs:

swift
@objc class Foo: NSObject {
    @objc func stringPromise() -> AnyPromise {
        return AnyPromise(stringPromise())
    }
    @objc func barPromise() -> AnyPromise {
        return AnyPromise(barPromise())
    }
}

If we built this and opened our generated header, we would now see:

objc
@interface Foo
- (AnyPromise *)stringPromise;
- (AnyPromise *)barPromise;
@end

@interface Bar
@end

Perfect.

Note that AnyPromise can only bridge objects that conform to AnyObject or derive from NSObject. This is a limitation of Objective-C.

Using ObjC AnyPromises from Swift

Simply use them, the type of your handler parameter is Any:

objective-c
- (AnyPromise *)fetchThings {
    return [AnyPromise promiseWithValue:@[@"a", @"b", @"c"]];
}

Since ObjC is not type-safe and Swift is, you will (probably) need to cast the Any to whatever it is you actually are feeding:

swift
Foo.fetchThings().done { any in
    let bar = any as! [String]
}

:warning: Caution:

ARC in Objective-C, unlike in Objective-C++, is not exception-safe by default.
So, throwing an error will result in keeping a strong reference to the closure
that contains the throw statement.
This pattern will consequently result in memory leaks if you're not careful.

Note: Only having a strong reference to the closure would result in memory leaks.
In our case, PromiseKit automatically keeps a strong reference to the closure until it's released.

Workarounds:

  1. Return a Promise with value NSError
    Instead of throwing a normal error, you can return a Promise with value NSError instead.
objc
myPromise.then(^{
    return [AnyPromise promiseWithValue:[NSError myCustomError]];
}).catch(^(NSError *error){
    if ([error isEqual:[NSError myCustomError]]) {
        // In case, same error as the one we thrown
        return;
    }
    //…
});
  1. Enable ARC for exceptions in Objective-C (not recommended)
    You can add this -fobjc-arc-exceptions to your to your compiler flags to enable ARC for exceptions.
    This is not recommended unless you've read the Apple documentation and are comfortable with the caveats.

For more details on ARC and exceptions:
https://clang.llvm.org/docs/AutomaticReferenceCounting.html#exceptions


Documentation Troubleshooting (`Documentation/Troubleshooting.md`)

Troubleshooting

Compilation errors

99% of compilation issues involving PromiseKit can be addressed or diagnosed by one of the fixes below.

Check your handler

swift
return firstly {
      URLSession.shared.dataTask(.promise, with: url)
}.compactMap {
    JSONSerialization.jsonObject(with: $0.data) as? [String: Any]
}.then { dict in
    User(dict: dict)
}

Swift (unhelpfully) says:

Cannot convert value of type '([String : Any]) -> User' to expected argument type '([String : Any]) -> _'

What’s the real problem? then must return a Promise, and you're trying to return something else. What you really want is map:

swift
return firstly {
      URLSession.shared.dataTask(.promise, with: url)
}.compactMap {
    JSONSerialization.jsonObject(with: $0.data) as? [String: Any]
}.map { dict in
    User(dict: dict)
}

Specify closure parameters **and** return type

For example:

swift
return firstly {
    foo()
}.then { user in
    //…
    return bar()
}

This code may compile if you specify the type of user:

swift
return firstly {
    foo()
}.then { (user: User) in
    //…
    return bar()
}

If it still doesn't compile, perhaps you need to specify the return type, too:

swift
return firstly {
    foo()
}.then { (user: User) -> Promise<Bar> in
    //…
    return bar()
}

We have made great effort to reduce the need for explicit typing in PromiseKit 6,
but as with all Swift functions that return a generic type (e.g., Array.map),
you may need to explicitly tell Swift what a closure returns if the closure's body is
longer than one line.

Tip: Sometimes you can force a one-liner by using semicolons.

Acknowledge all incoming closure parameters

Swift does not permit you to silently ignore a closure's parameters. For example, this code:

swift
func _() -> Promise<Void> {
    return firstly {
        proc.launch(.promise)      // proc: Foundation.Process
    }.then {
        when(fulfilled: p1, p2)    // both p1 & p2 are `Promise<Void>`
    }
}

Fails to compile with the error:

text
Cannot invoke 'then' with an argument list of type '(() -> _)

What's the problem? Well, Process.launch(.promise) returns
Promise<(String, String)>, and we are ignoring this value in our then closure.
If we’d referenced $0 or named the parameter, Swift would have been satisfied.

Assuming that we really do want to ignore the argument, the fix is to explicitly
acknowledge its existence by assigning it the name "_". That's Swift-ese for "I
know there's a value here, but I'm ignoring it."

swift
func _() -> Promise<Void> {
    return firstly {
        proc.launch(.promise)
    }.then { _ in
        when(fulfilled: p1, p2)
    }
}

In this situation, you won't always receive an error message that's as clear as the
one shown above. Sometimes, a missing closure parameter sends Swift scurrying off
into type inference limbo. When it finally concludes that there's no way for it to make
all the inferred types work together, it may end up assigning blame to some other
closure entirely and giving you an error message that makes no sense at all.

When faced with this kind of enigmatic complaint, a good rule of thumb is to
double-check your argument and return types carefully. If everything looks OK,
temporarily add explicit type information as shown above, just to rule
out misinference as a possible cause.

Try moving code to a temporary inline function

Try taking the code out of a closure and putting it in a standalone function. Now Swift
will give you the real error message. For example:

swift
func doStuff() {
    firstly {
        foo()
    }.then {
        let bar = bar()
        let baz = baz()
        when(fulfilled: bar, baz)
    }
}

Becomes:

swift
func doStuff() {
    func fluff() -> Promise<…> {
        let bar = bar()
        let baz = baz()
        when(fulfilled: bar, baz)  
    }

    firstly {
        foo()
    }.then {
        fluff()
    }
}

An inline function like this is all you need. Here, the problem is that you
forgot to mark the last line of the closure with an explicit return. It's required
here because the closure is longer than one line.

You copied code off the Internet that doesn’t work

Swift has changed a lot over the years and so PromiseKit has had to change to keep
up. The code you copied is probably for an older PromiseKit. Read the definitions of the
functions.
It's easy to do this in Xcode by option-clicking or command-clicking function names.
All PromiseKit functions are documented and provide examples.

"Context type for closure argument expects 1 argument, which cannot be implicitly ignored"

You have a then; you want a done.

"Missing argument for parameter #1 in call"

This is part of Swift 4’s “tuplegate”.

You must specify your Void parameter:

swift
seal.fulfill(())

Yes: we hope they revert this change in Swift 5 too.

"Ambiguous reference to 'firstly(execute:)'"

Remove the firstly, e.g.:

swift
firstly {
    foo()
}.then {
    //…
}

becomes:

swift
foo().then {
    //…
}

Rebuild and Swift should now tell you the real error.

Other issues

`Pending Promise Deallocated!`

If you see this warning, you have a path in your Promise initializer that allows
the promise to escape without being sealed:

swift
Promise<String> { seal in
    task { value, error in
        if let value = value as? String {
            seal.fulfill(value)
        } else if let error = error {
            seal.reject(error)
        }
    }
}

There are two missing paths here, and if either occurs, the promise will soon be
deallocated without resolving. This will manifest itself as a bug in your app,
probably the awful infinite spinner.

So let’s be thorough:

swift
Promise<String> { seal in
    task { value, error in
        if let value = value as? String {
            fulfill(value)
        } else if let error = error {
            reject(error)
        } else if value != nil {
            reject(MyError.valueNotString)
        } else {
            // should never happen, but we have an `PMKError` for task being called with `nil`, `nil`
            reject(PMKError.invalidCallingConvention)
        }
    }
}

If this seems tedious, it shouldn’t. You would have to be this thorough without promises, too.
The difference is that without promises, you wouldn’t get a warning in the console notifying
you of your mistake!

Slow compilation / compiler cannot solve in reasonable time

Add return types to your closures.

My promise never resolves

There are several potential causes:

1. Check to be sure that your asynchronous task even *starts*

You’d be surprised how often this is the cause.

For example, if you are using URLSession without our extension (but
don’t do that; use our extension! we know all the pitfalls), did you forget
to call resume on the task? If so, the task never actually starts, and so of
course it never finishes, either.

2. Check that all paths in your custom Promise initializers are handled

See “Pending Promise Deallocated” above. Usually you will see this warning if
you are not handling a path, but that requires your promise deallocate, so you
may not see this warning yet you are still not handling all paths.

Unhandled paths mean the promise will not resolve.

3. Ensure the queue your promise handler runs upon is not blocked

If the thread is blocked the handlers cannot execute. Commonly you can see this
if you are using our wait() function. Please read the documentation for wait()
for suggestions and caveats.

4. Your promise returned a cancellation error

Cancelation is neither success nor failure. So this is the correct behavior.
Use a finally if you need to do some clean up.

`Result of call to 'done(on:_:)' is unused`, `Result of call to 'then(on:_:)' is unused`

PromiseKit deliberately avoids the @discardableResult annotation because the
unused result warning is a hint that you have not handled the error in your
chain. So do one of these:

  1. Add a catch
  2. return the promise (thus punting the error handling to the caller)
  3. Use cauterize() to silence the warning.

Obviously, do 1 or 2 in preference to 3.


Documentation Examples ImageCache (`Documentation/Examples/ImageCache.md`)

Image Cache with Promises

Here is an example of a simple image cache that uses promises to simplify the
state machine:

text
/* Detailed source-code truncated for AI context efficiency. */

--- METRICS ---