Add PromiseKit dependency
- Added PromiseKit dependency
This commit is contained in:
424
Carthage/Checkouts/PromiseKit/Sources/Thenable.swift
vendored
Normal file
424
Carthage/Checkouts/PromiseKit/Sources/Thenable.swift
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
import Dispatch
|
||||
|
||||
/// Thenable represents an asynchronous operation that can be chained.
|
||||
public protocol Thenable: class {
|
||||
/// The type of the wrapped value
|
||||
associatedtype T
|
||||
|
||||
/// `pipe` is immediately executed when this `Thenable` is resolved
|
||||
func pipe(to: @escaping(Result<T>) -> Void)
|
||||
|
||||
/// The resolved result or nil if pending.
|
||||
var result: Result<T>? { get }
|
||||
}
|
||||
|
||||
public extension Thenable {
|
||||
/**
|
||||
The provided closure executes when this promise resolves.
|
||||
|
||||
This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that executes when this promise fulfills. It must return a promise.
|
||||
- Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url1)
|
||||
}.then { response in
|
||||
transform(data: response.data)
|
||||
}.done { transformation in
|
||||
//…
|
||||
}
|
||||
*/
|
||||
func then<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise<U.T> {
|
||||
let rp = Promise<U.T>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
let rv = try body(value)
|
||||
guard rv !== rp else { throw PMKError.returnedSelf }
|
||||
rv.pipe(to: rp.box.seal)
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is resolved.
|
||||
|
||||
This is like `then` but it requires the closure to return a non-promise.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise.
|
||||
- Returns: A new promise that is resolved with the value returned from the provided closure. For example:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url1)
|
||||
}.map { response in
|
||||
response.data.length
|
||||
}.done { length in
|
||||
//…
|
||||
}
|
||||
*/
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
rp.box.seal(.fulfilled(try transform(value)))
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is resolved.
|
||||
|
||||
In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value.
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url)
|
||||
}.compactMap {
|
||||
try JSONSerialization.jsonObject(with: $0.data) as? [String: String]
|
||||
}.done { dictionary in
|
||||
//…
|
||||
}.catch {
|
||||
// either `PMKError.compactMap` or a `JSONError`
|
||||
}
|
||||
*/
|
||||
func compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
if let rv = try transform(value) {
|
||||
rp.box.seal(.fulfilled(rv))
|
||||
} else {
|
||||
throw PMKError.compactMap(value, U.self)
|
||||
}
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is resolved.
|
||||
|
||||
Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift
|
||||
is happier and gives you less hassle about your closure’s qualification.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed when this Promise is fulfilled.
|
||||
- Returns: A new promise fulfilled as `Void`.
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url)
|
||||
}.done { response in
|
||||
print(response.data)
|
||||
}
|
||||
*/
|
||||
func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise<Void> {
|
||||
let rp = Promise<Void>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
try body(value)
|
||||
rp.box.seal(.fulfilled(()))
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is resolved.
|
||||
|
||||
This is like `done` but it returns the same value that the handler is fed.
|
||||
`get` immutably accesses the fulfilled value; the returned Promise maintains that value.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed when this Promise is fulfilled.
|
||||
- Returns: A new promise that is resolved with the value that the handler is fed. For example:
|
||||
|
||||
firstly {
|
||||
.value(1)
|
||||
}.get { foo in
|
||||
print(foo, " is 1")
|
||||
}.done { foo in
|
||||
print(foo, " is 1")
|
||||
}.done { foo in
|
||||
print(foo, " is Void")
|
||||
}
|
||||
*/
|
||||
func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise<T> {
|
||||
return map(on: on, flags: flags) {
|
||||
try body($0)
|
||||
return $0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed with promise result.
|
||||
|
||||
This is like `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.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed with Result of Promise.
|
||||
- Returns: A new promise that is resolved with the result that the handler is fed. For example:
|
||||
|
||||
promise.tap{ print($0) }.then{ /*…*/ }
|
||||
*/
|
||||
func tap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result<T>) -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
pipe { result in
|
||||
on.async(flags: flags) {
|
||||
body(result)
|
||||
seal.resolve(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// - Returns: a new promise chained off this promise but with its value discarded.
|
||||
func asVoid() -> Promise<Void> {
|
||||
return map(on: nil) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable {
|
||||
/**
|
||||
- Returns: The error with which this promise was rejected; `nil` if this promise is not rejected.
|
||||
*/
|
||||
var error: Error? {
|
||||
switch result {
|
||||
case .none:
|
||||
return nil
|
||||
case .some(.fulfilled):
|
||||
return nil
|
||||
case .some(.rejected(let error)):
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise has not yet resolved.
|
||||
*/
|
||||
var isPending: Bool {
|
||||
return result == nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise has resolved.
|
||||
*/
|
||||
var isResolved: Bool {
|
||||
return !isPending
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise was fulfilled.
|
||||
*/
|
||||
var isFulfilled: Bool {
|
||||
return value != nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise was rejected.
|
||||
*/
|
||||
var isRejected: Bool {
|
||||
return error != nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: The value with which this promise was fulfilled or `nil` if this promise is pending or rejected.
|
||||
*/
|
||||
var value: T? {
|
||||
switch result {
|
||||
case .none:
|
||||
return nil
|
||||
case .some(.fulfilled(let value)):
|
||||
return value
|
||||
case .some(.rejected):
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence {
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `U` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.mapValues { integer in
|
||||
integer * 2
|
||||
}.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags){ try $0.map(transform) }
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.flatMapValues { integer in
|
||||
[integer, integer]
|
||||
}.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func flatMapValues<U: Sequence>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> {
|
||||
return map(on: on, flags: flags){ (foo: T) in
|
||||
try foo.flatMap{ try transform($0) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `U?` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value(["1","2","a","3"])
|
||||
}.compactMapValues {
|
||||
Int($0)
|
||||
}.done {
|
||||
// $0 => [1,2,3]
|
||||
}
|
||||
*/
|
||||
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags) { foo -> [U] in
|
||||
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
|
||||
return try foo.flatMap(transform)
|
||||
#else
|
||||
return try foo.compactMap(transform)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `Promise<U>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.thenMap { integer in
|
||||
.value(integer * 2)
|
||||
}.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func thenMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: try $0.map(transform))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `Promise<[U]>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.thenFlatMap { integer in
|
||||
.value([integer, integer])
|
||||
}.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func thenFlatMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: try $0.map(transform))
|
||||
}.map(on: nil) {
|
||||
$0.flatMap{ $0 }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> Bool => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.filterValues {
|
||||
$0 > 1
|
||||
}.done {
|
||||
// $0 => [2,3]
|
||||
}
|
||||
*/
|
||||
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.filter(isIncluded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Collection {
|
||||
/// - Returns: a promise fulfilled with the first value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence.
|
||||
var firstValue: Promise<T.Iterator.Element> {
|
||||
return map(on: nil) { aa in
|
||||
if let a1 = aa.first {
|
||||
return a1
|
||||
} else {
|
||||
throw PMKError.emptySequence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func firstValue(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise<T.Iterator.Element> {
|
||||
return map(on: on, flags: flags) {
|
||||
for x in $0 where test(x) {
|
||||
return x
|
||||
}
|
||||
throw PMKError.emptySequence
|
||||
}
|
||||
}
|
||||
|
||||
/// - Returns: a promise fulfilled with the last value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence.
|
||||
var lastValue: Promise<T.Iterator.Element> {
|
||||
return map(on: nil) { aa in
|
||||
if aa.isEmpty {
|
||||
throw PMKError.emptySequence
|
||||
} else {
|
||||
let i = aa.index(aa.endIndex, offsetBy: -1)
|
||||
return aa[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence, T.Iterator.Element: Comparable {
|
||||
/// - Returns: a promise fulfilled with the sorted values of this `Sequence`.
|
||||
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags){ $0.sorted() }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user