Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/vadymmarkov/When

:alarm_clock: A lightweight implementation of Promises in Swift
https://github.com/vadymmarkov/When

promise swift

Last synced: 3 months ago
JSON representation

:alarm_clock: A lightweight implementation of Promises in Swift

Awesome Lists containing this project

README

        

![When](https://github.com/vadymmarkov/When/blob/master/Resources/WhenPresentation.png)

[![CI Status](http://img.shields.io/travis/vadymmarkov/When.svg?style=flat)](https://travis-ci.org/vadymmarkov/When)
[![Version](https://img.shields.io/cocoapods/v/When.svg?style=flat)](http://cocoadocs.org/docsets/When)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License](https://img.shields.io/cocoapods/l/When.svg?style=flat)](http://cocoadocs.org/docsets/When)
[![Platform](https://img.shields.io/cocoapods/p/When.svg?style=flat)](http://cocoadocs.org/docsets/When)

## Description

**When** is a lightweight implementation of [Promises](https://en.wikipedia.org/wiki/Futures_and_promises)
in Swift. It doesn't include any helper functions for iOS and OSX and it's
intentional, to remove redundant complexity and give you more freedom and
flexibility in your choices. It is type safe, thanks Swift generics, so you
could create ***promises*** with whatever type you want.

**When** can easily be integrated into your projects and libraries to move your
asynchronous code up to the next level.

## Table of Contents

When Icon

* [Why?](#why)
* [Usage](#usage)
* [Promise](#promise)
* [Done](#done)
* [Fail](#fail)
* [Always](#always)
* [Then](#then)
* [Recover](#recover)
* [When](#when)
* [Reactive extensions](#reactive-extensions)
* [Installation](#installation)
* [Author](#author)
* [Credits](#credits)
* [Contributing](#contributing)
* [License](#license)

## Why?

To make asynchronous code more readable and standardized:

```swift
fetchJSON().then({ data: NSData -> [[String: AnyObject]] in
// Convert to JSON
return json
}).then({ json: [[String: AnyObject]] -> [Entity] in
// Map JSON
// Save to database
return items
}).done({ items: [Entity] in
self.items = items
self.tableView.reloadData()
}).error({ error in
print(error)
})
```

## Usage

### Promise
A ***promise*** represents the future value of a task. Promises start in a pending
state and then could be resolved with a value or rejected with an error.

```swift
// Creates a new promise that could be resolved with a String value
let promise = Promise()
// Resolves the promise
promise.resolve("String")
// Or rejects the promise
promise.reject(Error.notFound)
```

```swift
// Creates a new promise that is resolved with a String value
let promise = Promise({
return "String"
})
```

```swift
// Creates a new promise that is rejected with an Error
let promise = Promise({
//...
throw Error.notFound
})
```

Callbacks of the current ***promise*** and all the chained promises
(created with [then](#then)) are executed on the main queue by default, but
you can always specify the needed queue in `init`:

```swift
let promise = Promise(queue: dispatch_get_main_queue())
```

### Done
Adds a handler to be called when the ***promise*** object is resolved with a value:

```swift
// Create a new promise in a pending state
let promise = Promise()
// Add done callback
promise.done({ value in
print(value)
})
// Resolve the promise
promise.resolve("String")
```

### Fail
Adds a handler to be called when the ***promise*** object is rejected with
an `Error`:

```swift
// Create a new promise in a pending state
let promise = Promise()
// Add fail callback
promise.fail({ error in
print(error)
})
// Reject the promise
promise.reject(Error.notFound)
```

It's also possible to cancel a promise, which means it will be rejected with
`PromiseError.cancelled` error. `FailurePolicy` can be used if you want to
ignore this error in your `fail` handler:

```swift
// Create a new promise in a pending state
let promise = Promise()
// This callback will not be called when a promise is cancelled
promise.fail({ error in
print(error)
})
// This callback will be called when a promise is cancelled
promise.fail(policy: .allErrors, { error in
print(error)
})
// Cancel the promise
promise.cancel()
```

### Always
Adds a handler to be called when the ***promise*** object is either resolved or
rejected. This callback will be called after [done](#done) or [fail](#fail)
handlers.

```swift
// Create a new promise in a pending state
let promise = Promise()
// Add always callback
promise.always({ result in
switch result {
case let .success(value):
print(value)
case let .failure(error):
print(error)
}
})
// Resolve or reject the promise
promise.resolve("String") // promise.reject(Error.notFound)
```

### Then
Returns a new ***promise*** that can use the result value of the current
promise. It means that you could easily create chains of ***promises*** to
simplify complex asynchronous operations into clear and simple to understand
logic.

A new ***promise*** is resolved with the value returned from the provided
closure:

```swift
let promise = Promise()

promise
.then({ data -> Int in
return data.length
}).then({ length -> Bool in
return length > 5
}).done({ value in
print(value)
})

promise.resolve("String".dataUsingEncoding(NSUTF8StringEncoding)!)
```

A new ***promise*** is resolved when the ***promise*** returned from the
provided closure resolves:
```swift
struct Networking {
static func GET(url: NSURL) -> Promise {
let promise = Promise()
//...
return promise
}
}

Networking.GET(url1)
.then({ data -> Promise in
//...
return Networking.GET(url2)
}).then({ data -> Int in
return data.length
}).done({ value in
print(value)
})
```

***then*** closure is executed on the main queue by default, but you can pass a
needed queue as a parameter:

```swift
promise.then(on: dispatch_get_global_queue(QOS_CLASS_UTILITY, 0))({ data -> Int in
//...
})
```

If you want to use background queue there are the helper methods for this case:

```swift
promise1.thenInBackground({ data -> Int in
//...
})

promise2.thenInBackground({ data -> Promise in
//...
})
```

### Recover
Returns a new ***promise*** that can be used to continue the chain when an
error was thrown.

```swift
let promise = Promise()
// Recover the chain
promise
.recover({ error -> Promise in
return Promise({
return "Recovered"
})
})
.done({ string in
print(string) // Recovered
})
// Reject the promise
promise.reject(Error.notFound)
```

### When
Provides a way to execute callback functions based on one or more
***promises***. The ***when*** method returns a new "master" ***promise*** that
tracks the aggregate state of all the passed ***promises***. The method will
resolve its "master" ***promise*** as soon as all the ***promises*** resolve,
or reject the "master" ***promise*** as soon as one of the ***promises*** is
rejected. If the "master" ***promise*** is resolved, the ***done*** callback is
executed with resolved values for each of the ***promises***:

```swift
let promise1 = Promise()
let promise2 = Promise()
let promise3 = Promise()

when(promise1, promise2, promise3)
.done({ value1, value2, value3 in
print(value1)
print(value2)
print(value3)
})

promise1.resolve(1)
promise2.resolve("String")
promise3.resolve(3)
```

## Reactive extensions

Use the following extension in order to integrate **When** with [RxSwift](https://github.com/ReactiveX/RxSwift):

```swift
import RxSwift

extension Promise: ObservableConvertibleType {
public func asObservable() -> Observable {
return Observable.create({ observer in
self
.done({ value in
observer.onNext(value)
})
.fail({ error in
observer.onError(error)
})
.always({ _ in
observer.onCompleted()
})

return Disposables.create()
})
}
}
```

## Installation

**When** is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:

```ruby
pod 'When'
```

For `RxSwift` extensions you can use CocoaPods subspecs:

```ruby
pod 'When/RxSwift'
```

**When** is also available through [Carthage](https://github.com/Carthage/Carthage).
To install just write into your Cartfile:

```ruby
github "vadymmarkov/When"
```

## Author

Vadym Markov, [email protected]

## Credits

Credits for inspiration go to [PromiseKit](https://github.com/mxcl/PromiseKit)
and [Then](https://github.com/onmyway133/Then).

## Contributing

Check the [CONTRIBUTING](https://github.com/vadymmarkov/When/blob/master/CONTRIBUTING.md) file for more info.

## License

**When** is available under the MIT license. See the [LICENSE](https://github.com/vadymmarkov/When/blob/master/LICENSE.md) file for more info.