Home

Awesome

<img src="https://cloud.githubusercontent.com/assets/75655/5077599/2f2d9f8c-6ea5-11e4-98d2-cdb72f6686a8.png" width="170" alt="Promissum"> <hr>

Promissum is a promises library written in Swift. It features some known functions from Functional Programming like, map and flatMap.

It has useful combinators for working with promises like; whenAll for doing something when multiple promises complete, and whenAny for doing something when a single one of a list of promises completes. As well as their binary counterparts: whenBoth and whenEither.

Promissum really shines when used to combine asynchronous operations from different libraries. There are currently some basic extensions to UIKit and Alamofire, contributions for extensions to other libraries are very welcome.

This library has an extensive set of regression tests, documentation, and has been used in several high profile production apps at Q42.

Example

This example demonstrates the Alamofire+Promise extension.

In this example, JSON data is loaded from the Github API. It is then parsed, and stored into a local cache. If both those succeed the result is shown to the user, if either of those fail, a description of the error is shown to the user.

let url = "https://api.github.com/repos/tomlokhorst/Promissum"
Alamofire.request(url).responseJSONPromise()
  .map(parseJson)
  .flatMap(storeInLocalCache)
  .then { project in

    // Show project name and description
    self.nameLabel.text = project.name
    self.descriptionLabel.text = project.descr

    UIView.animate(withDuration: 0.5) {
      self.detailsView.alpha = 1
    }
  }
  .trap { e in

    // Either an Alamofire error or a LocalCache error occured
    self.errorLabel.text = e.localizedDescription
    self.errorView.alpha = 1
  }

Async/Await

Interop with Swift's async await is available via the .asyncValue and .asyncResult properties, as well as a custom Promise initializer.

// Go from Promise to async/await
func downloadProject(url: URL) async throws -> Project {
  let data = try await downloadPromise(url: url).asyncValue
  let project = try parse(data)
  return project
}


// Go from async/await to Promise
func submitProject(project: Project) -> Promise<Void, Error> {
  Promise {
    let data = try encode(project)
    try await upload(data)
  }
}

Cancellation

Promissum does not support cancellation, because cancellation does not work well with promises. Promises are future values, values can't be cancelled. If you do need cancellation (quite often useful), take a look at Tasks or Rx instead of promises. I don't have experience with any Swift Task/Rx libraries, so I can't recommend a specific one.

Although, if you're looking at adding cancellation to a PromiseSource, you could use the swift-cancellationtoken library I wrote. This is orthogonal to promises, however.

Combinators

Listed below are some of the methods and functions provided this library. More documentation is available inline.

Instance methods on Promise

Functions for dealing with Promises

Dispatch queues

Promises can call handlers on different threads or queues. Handlers are all closures supplied to methods like .then, .trap, .map, and .flatMap.

If nothing else is specified, by default, all handlers will be called on the main queue. This way, you're free to update the UI, without having to worry about manually calling dispatch_async.

However, it's easy to change the dispatch queue used by a promise. In one of two ways:

  1. Set the dispatch queue when creating a PromiseSource, e.g.:
let background = DispatchQueue.global(qos: .background)
let source = PromiseSource<Int, Never>(dispatch: .queue(background))
source.promise
  .then { x in
    // Handler is called on background queue
  }
  1. Or, create a new promise using the .dispatchOn combinator:
let background = DispatchQueue.global(qos: .background)
somePromise()
  .dispatch(on: background)
  .then { x in
    // Handler is called on background queue
  }

For convenience, there's also .dispatchMain to move back to the main queue, after doing some work on a background queue:

let background = DispatchQueue.global(qos: .background)
somePromise()
  .dispatch(on: background)
  .map { expensiveComputation($0) }
  .dispatchMain()
  .then { x in
    self.updateUi(x)
  }

Installation

Swift Package Manager

SPM is a dependency manager for Swift projects.

Once you have SPM setup, add a dependency using Xcode or by editing Package.swift:

dependencies: [
    .package(url: "https://github.com/tomlokhorst/Promissum.git", from: "7.1.0"),
]

Note: Previous versions of Promissum supported CocoaPods, this is no longer supported. If you still need pods support, you can use the 5.x.x versions of this package, while those still work.

Releases

Licence & Credits

Promissum is written by Tom Lokhorst and available under the MIT license, so feel free to use it in commercial and non-commercial projects.