firebase log level

This commit is contained in:
oscarz
2024-08-29 18:25:13 +08:00
parent 8500300d18
commit 27c160beaf
1165 changed files with 122916 additions and 1 deletions

View File

@ -0,0 +1,182 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Dispatch
/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with the same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: Promise of an array containing the values of input promises in the same order.
public func all<Value>(
on queue: DispatchQueue = .promises,
_ promises: Promise<Value>...
) -> Promise<[Value]> {
return all(on: queue, promises)
}
/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: Promise of an array containing the values of input promises in the same order.
public func all<Value, Container: Sequence>(
on queue: DispatchQueue = .promises,
_ promises: Container
) -> Promise<[Value]> where Container.Element == Promise<Value> {
let promises = promises.map { $0.objCPromise }
let promise = Promise<[Value]>(
Promise<[Value]>.ObjCPromise<AnyObject>.__onQueue(queue, all: promises)
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - returns: Promise of a tuple containing the values of input promises in the same order.
public func all<A, B>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>
) -> Promise<(A, B)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise
]
let promise = Promise<(A, B)>(
Promise<(A, B)>.ObjCPromise<AnyObject>.__onQueue(
queue,
all: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]?,
let valueA = Promise<A>.asValue(values[0]),
let valueB = Promise<B>.asValue(values[1])
else {
preconditionFailure("Cannot convert \(type(of: objCValues)) to \((A, B).self)")
}
return (valueA, valueB)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - promiseC: Promise of type `C`.
/// - returns: Promise of a tuple containing the values of input promises in the same order.
public func all<A, B, C>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>,
_ promiseC: Promise<C>
) -> Promise<(A, B, C)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise,
promiseC.objCPromise
]
let promise = Promise<(A, B, C)>(
Promise<(A, B, C)>.ObjCPromise<AnyObject>.__onQueue(
queue,
all: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]?,
let valueA = Promise<A>.asValue(values[0]),
let valueB = Promise<B>.asValue(values[1]),
let valueC = Promise<C>.asValue(values[2])
else {
preconditionFailure("Cannot convert \(type(of: objCValues)) to \((A, B, C).self)")
}
return (valueA, valueB, valueC)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - promiseC: Promise of type `C`.
/// - promiseD: Promise of type `D`.
/// - returns: Promise of a tuple containing the values of input promises in the same order.
public func all<A, B, C, D>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>,
_ promiseC: Promise<C>,
_ promiseD: Promise<D>
) -> Promise<(A, B, C, D)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise,
promiseC.objCPromise,
promiseD.objCPromise
]
let promise = Promise<(A, B, C, D)>(
Promise<(A, B, C, D)>.ObjCPromise<AnyObject>.__onQueue(
queue,
all: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]?,
let valueA = Promise<A>.asValue(values[0]),
let valueB = Promise<B>.asValue(values[1]),
let valueC = Promise<C>.asValue(values[2]),
let valueD = Promise<D>.asValue(values[3])
else {
preconditionFailure("Cannot convert \(type(of: objCValues)) to \((A, B, C, D).self)")
}
return (valueA, valueB, valueC, valueD)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}

View File

@ -0,0 +1,31 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Dispatch
public extension Promise {
/// Provides a way to always execute a given chained block.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - work: A block that always executes, no matter if `self` is rejected or fulfilled.
/// - returns: A new pending promise to be resolved with same resolution as `self`.
@discardableResult
func always(on queue: DispatchQueue = .promises, _ work: @escaping () -> Void) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, always: work))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,276 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Waits for all of the given promises to be fulfilled or rejected.
/// If all promises are rejected, then the returned promise is rejected with same error
/// as the last one rejected.
/// If at least one of the promises is fulfilled, the resulting promise is fulfilled with an array
/// of `Maybe` enums containing values or `Error`s, matching the original order of fulfilled or
/// rejected promises respectively.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: Promise of an array of `Maybe` enums containing the values or `Error`s of input
/// promises in their original order.
public func any<Value>(
on queue: DispatchQueue = .promises,
_ promises: Promise<Value>...
) -> Promise<[Maybe<Value>]> {
return any(on: queue, promises)
}
/// Waits for all of the given promises to be fulfilled or rejected.
/// If all promises are rejected, then the returned promise is rejected with same error
/// as the last one rejected.
/// If at least one of the promises is fulfilled, the resulting promise is fulfilled with an array
/// of `Maybe` enums containing values or `Error`s, matching the original order of fulfilled or
/// rejected promises respectively.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: Promise of an array of `Maybe` enums containing the values or `Error`s of input
/// promises in their original order.
public func any<Value, Container: Sequence>(
on queue: DispatchQueue = .promises,
_ promises: Container
) -> Promise<[Maybe<Value>]> where Container.Element == Promise<Value> {
let promises = promises.map { $0.objCPromise }
let promise = Promise<[Maybe<Value>]>(
Promise<[Maybe<Value>]>.ObjCPromise<AnyObject>.__onQueue(
queue,
any: promises
).__onQueue(queue, then: { values in
guard let values = values as [AnyObject]? else { preconditionFailure() }
return Promise<[Maybe<Value>]>.asAnyObject(values.map { asMaybe($0) as Maybe<Value> })
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Waits for all of the given promises to be fulfilled or rejected.
/// If all promises are rejected, then the returned promise is rejected with same error
/// as the last one rejected.
/// If at least one of the promises is fulfilled, the resulting promise is fulfilled with a tuple
/// of `Maybe` enums containing values or `Error`s, matching the original order of fulfilled or
/// rejected promises respectively.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - returns: Promise of a tuple of `Maybe` enums containing the values or `Error`s of input
/// promises in their original order.
public func any<A, B>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>
) -> Promise<(Maybe<A>, Maybe<B>)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise
]
let promise = Promise<(Maybe<A>, Maybe<B>)>(
Promise<(Maybe<A>, Maybe<B>)>.ObjCPromise<AnyObject>.__onQueue(
queue,
any: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]? else { preconditionFailure() }
let valueA = asMaybe(values[0]) as Maybe<A>
let valueB = asMaybe(values[1]) as Maybe<B>
return (valueA, valueB)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Waits for all of the given promises to be fulfilled or rejected.
/// If all promises are rejected, then the returned promise is rejected with same error
/// as the last one rejected.
/// If at least one of the promises is fulfilled, the resulting promise is fulfilled with a tuple
/// of `Maybe` enums containing values or `Error`s, matching the original order of fulfilled or
/// rejected promises respectively.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - promiseC: Promise of type `C`.
/// - returns: Promise of a tuple of `Maybe` enums containing the values or `Error`s of input
/// promises in their original order.
public func any<A, B, C>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>,
_ promiseC: Promise<C>
) -> Promise<(Maybe<A>, Maybe<B>, Maybe<C>)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise,
promiseC.objCPromise
]
let promise = Promise<(Maybe<A>, Maybe<B>, Maybe<C>)>(
Promise<(Maybe<A>, Maybe<B>, Maybe<C>)>.ObjCPromise<AnyObject>.__onQueue(
queue,
any: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]? else { preconditionFailure() }
let valueA = asMaybe(values[0]) as Maybe<A>
let valueB = asMaybe(values[1]) as Maybe<B>
let valueC = asMaybe(values[2]) as Maybe<C>
return (valueA, valueB, valueC)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}
/// Wrapper enum for `any` results.
/// - value: Contains the value that corresponding promise was fulfilled with.
/// - error: Contains the error that corresponding promise was rejected with.
public enum Maybe<Value> {
case value(Value)
case error(Error)
public init(_ value: Value) { self = .value(value) }
public init(_ error: Error) { self = .error(error) }
public var value: Value? {
if case .value(let value) = self { return value } else { return nil }
}
public var error: Error? {
if case .error(let error) = self { return error } else { return nil }
}
}
// MARK: - Conversion
/// Helper functions that facilitates conversion of `Promise.any` results to the results normally
/// expected from `ObjCPromise.any`.
///
/// Convert a promise created with `any` in Swift to Objective-C:
///
/// any([promise1, promise2, promise3]).then { arrayOfMaybeEnums in
/// return arrayOfMaybeEnums.map { $0.asAnyObject() }
/// }.asObjCPromise() as Promise<[AnyObject?]>.ObjCPromise<AnyObject>
///
/// Convert a promise created with `any` in Objective-C to Swift:
///
/// Promise<[AnyObject]>(objCPromise).then { arrayOfAnyObjects in
/// return arrayOfAnyObjects.map { asMaybe($0) as Maybe<SomeValue> }
/// }
public extension Maybe {
/// Converts generic `Value` to `AnyObject`.
func asAnyObject() -> AnyObject? {
switch self {
case .value(let value):
return Promise<Value>.asAnyObject(value)
case .error(let error):
return error as NSError
}
}
}
/// Helper function to wrap the results of `ObjCPromise.any` with the safe `Maybe` enum.
public func asMaybe<Value>(_ value: AnyObject) -> Maybe<Value> {
if type(of: value) is NSError.Type {
return .error(value as! NSError)
} else {
guard let value = Promise<Value>.asValue(value) else { preconditionFailure() }
return .value(value)
}
}
// MARK: - Equatable
/// Equality operators for `Maybe`.
#if !swift(>=4.1)
extension Maybe where Value: Equatable {}
#else
extension Maybe: Equatable where Value: Equatable {}
#endif // !swift(>=4.1)
public func == <Value: Equatable>(lhs: Maybe<Value>, rhs: Maybe<Value>) -> Bool {
switch (lhs, rhs) {
case (.value(let lhs), .value(let rhs)):
return lhs == rhs
case (.error(let lhs), .error(let rhs)):
return (lhs as NSError).isEqual(rhs as NSError)
case (.value, .error), (.error, .value):
return false
}
}
public func != <Value: Equatable>(lhs: Maybe<Value>, rhs: Maybe<Value>) -> Bool {
return !(lhs == rhs)
}
#if !swift(>=4.1)
public func == <Value: Equatable>(lhs: Maybe<Value?>, rhs: Maybe<Value?>) -> Bool {
switch (lhs, rhs) {
case (.value(let lhs), .value(let rhs)):
switch (lhs, rhs) {
case (nil, nil):
return true
case (nil, _?), (_?, nil):
return false
case let (lhs?, rhs?):
return lhs == rhs
}
case (.error(let lhs), .error(let rhs)):
return (lhs as NSError).isEqual(rhs as NSError)
case (.value, .error), (.error, .value):
return false
}
}
public func != <Value: Equatable>(lhs: Maybe<Value?>, rhs: Maybe<Value?>) -> Bool {
return !(lhs == rhs)
}
public func == <Value: Equatable>(lhs: [Maybe<Value>], rhs: [Maybe<Value>]) -> Bool {
if lhs.count != rhs.count { return false }
for (lhs, rhs) in zip(lhs, rhs) where lhs != rhs { return false }
return true
}
public func != <Value: Equatable>(lhs: [Maybe<Value>], rhs: [Maybe<Value>]) -> Bool {
return !(lhs == rhs)
}
public func == <Value: Equatable>(lhs: [Maybe<Value?>], rhs: [Maybe<Value?>]) -> Bool {
if lhs.count != rhs.count { return false }
for (lhs, rhs) in zip(lhs, rhs) where lhs != rhs { return false }
return true
}
public func != <Value: Equatable>(lhs: [Maybe<Value?>], rhs: [Maybe<Value?>]) -> Bool {
return !(lhs == rhs)
}
#endif // !swift(>=4.1)

View File

@ -0,0 +1,42 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
typealias Async = (@escaping (Value) -> Void, @escaping (Error) -> Void) throws -> Void
/// Creates a pending promise and executes `work` block asynchronously on the given `queue`.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block to perform any operations needed to resolve the promise.
convenience init(on queue: DispatchQueue = .promises, _ work: @escaping Async) {
let objCPromise = ObjCPromise<AnyObject>.__onQueue(queue) { fulfill, reject in
do {
try work({ value in
if type(of: value) is NSError.Type {
reject(value as! NSError)
} else {
fulfill(Promise<Value>.asAnyObject(value))
}
}, reject)
} catch let error {
reject(error as NSError)
}
}
self.init(objCPromise)
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(self)
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import FBLPromises
/// Waits for promise resolution. The current thread blocks until the promise is resolved.
/// - parameters:
/// - promise: Promise to wait for.
/// - throws: Error the promise was rejected with.
/// - returns: Value the promise was fulfilled with.
public func awaitPromise<Value>(_ promise: Promise<Value>) throws -> Value {
var outError: NSError?
let outValue = __FBLPromiseAwait(promise.objCPromise, &outError) as AnyObject
if let error = outError { throw error }
guard let value = Promise<Value>.asValue(outValue) else { preconditionFailure() }
return value
}

View File

@ -0,0 +1,37 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
typealias Catch = (Error) -> Void
/// Creates a pending promise which eventually gets resolved with same resolution as `self`.
/// If `self` is rejected, then `reject` block is executed asynchronously on the given queue.
/// - parameters:
/// - queue: A queue to invoke the `reject` block on.
/// - reject: A block to handle the error that `self` was rejected with.
/// - returns: A new pending promise.
@discardableResult
func `catch`(on queue: DispatchQueue = .promises, _ reject: @escaping Catch) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, catch: {
// Convert `NSError` to `PromiseError`, if applicable.
let error = PromiseError($0) ?? $0
return reject(error as NSError)
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,35 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
/// Creates a new pending promise that fulfills with the same value as `self` after the `delay`,
/// or rejects with the same error immediately.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - interval: Time to wait in seconds.
/// - returns: A new pending promise that fulfills at least `interval` seconds later than `self`,
/// or rejects with the same error immediately.
func delay(
on queue: DispatchQueue = .promises,
_ interval: TimeInterval
) -> Promise<Value> {
let promise = Promise(objCPromise.__onQueue(queue, delay: interval))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,61 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
// swiftlint:disable:next type_name
typealias Do<T> = () throws -> T
/// Creates a pending promise to be resolved with the return value of `work` block which is
/// executed asynchronously on the given `queue`.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block that returns a value used to resolve the new promise.
convenience init<T>(on queue: DispatchQueue = .promises, _ work: @escaping Do<T>) {
let objCPromise = ObjCPromise<AnyObject>.__onQueue(queue) {
do {
let resolution = try work()
return type(of: resolution) is NSError.Type
? resolution as! NSError : Promise<T>.asAnyObject(resolution)
} catch let error {
return error as NSError
}
}
self.init(objCPromise)
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(self)
}
/// Creates a pending promise to be resolved with the same resolution as the promise returned from
/// `work` block which is executed asynchronously on the given `queue`.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block that returns a promise used to resolve the new promise.
convenience init<T>(
on queue: DispatchQueue = .promises,
_ work: @escaping Do<Promise<T>>
) {
let objCPromise = ObjCPromise<AnyObject>.__onQueue(queue) {
do {
return try work().objCPromise
} catch let error {
return error as NSError
}
}
self.init(objCPromise)
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(self)
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Dispatch
/// Wait until any of the given promises are fulfilled.
/// If one of the given promises is rejected, then the returned promise is rejected with same
/// error. If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: First promise, among the given ones, which was fulfilled.
public func race<Value>(
on queue: DispatchQueue = .promises,
_ promises: Promise<Value>...
) -> Promise<Value> {
return race(on: queue, promises)
}
/// Wait until any of the given promises are fulfilled.
/// If one of the given promises is rejected, then the returned promise is rejected with same
/// error. If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promises: Promises to wait for.
/// - returns: First promise, among the given ones, which was fulfilled.
public func race<Value>(
on queue: DispatchQueue = .promises,
_ promises: [Promise<Value>]
) -> Promise<Value> {
let promises = promises.map { $0.objCPromise }
let promise = Promise<Value>(
Promise<Value>.ObjCPromise<AnyObject>.__onQueue(queue, race: promises)
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__addPendingObject(promise)
}
return promise
}

View File

@ -0,0 +1,68 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
/// Provides a new promise to recover in case `self` gets rejected.
/// - parameters:
/// - queue: A queue to execute `recovery` block on.
/// - recovery: A block to handle the error that `self` was rejected with.
/// - returns: A new pending promise to use instead of the rejected one that gets resolved with
/// the same resolution as the promise returned from `recovery` block.
@discardableResult
func recover(
on queue: DispatchQueue = .promises,
_ recovery: @escaping (Error) throws -> Promise
) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, recover: {
do {
// Convert `NSError` to `PromiseError`, if applicable.
let error = PromiseError($0) ?? $0
return try recovery(error).objCPromise
} catch let error {
return error as NSError
}
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
/// Provides a new promise to recover in case `self` gets rejected.
/// - parameters:
/// - queue: A queue to execute `recovery` block on.
/// - recovery: A block to handle the error that `self` was rejected with.
/// - returns: A new pending promise to use instead of the rejected one that gets resolved with
/// the value returned from `recovery` block.
@discardableResult
func recover(
on queue: DispatchQueue = .promises,
_ recovery: @escaping (Error) throws -> Value
) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, recover: {
do {
// Convert `NSError` to `PromiseError`, if applicable.
let error = PromiseError($0) ?? $0
return Promise<Value>.asAnyObject(try recovery(error)) as Any
} catch let error {
return error as NSError
}
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,61 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Dispatch
public extension Promise {
typealias Reducer<Element> = (Value, Element) throws -> Promise<Value>
/// Sequentially reduces a collection of values to a single promise using a given combining block
/// and the value `self` resolves with as initial value.
/// - parameters:
/// - queue: A queue to execute `reducer` block on.
/// - items: A sequence of values to process in order.
/// - reducer: A block to combine an accumulating value and an element of the sequence into
/// a promise resolved with the new accumulating value, to be used in the next call
/// of the `reducer` or returned to the caller.
/// - returns: A new pending promise returned from the last `reducer` invocation.
/// Or `self` if `items` is empty.
@discardableResult
func reduce<Element>(
on queue: DispatchQueue = .promises,
_ items: Element...,
combine reducer: @escaping Reducer<Element>
) -> Promise<Value> {
return reduce(on: queue, items, reducer)
}
/// Sequentially reduces a collection of values to a single promise using a given combining block
/// and the value `self` resolves with as initial value.
/// - parameters:
/// - queue: A queue to execute `reducer` block on.
/// - items: A sequence of values to process in order.
/// - reducer: A block to combine an accumulating value and an element of the sequence into
/// a promise resolved with the new accumulating value, to be used in the next call
/// of the `reducer` or returned to the caller.
/// - returns: A new pending promise returned from the last `reducer` invocation.
/// Or `self` if `items` is empty.
@discardableResult
func reduce<Container: Sequence>(
on queue: DispatchQueue = .promises,
_ items: Container,
_ reducer: @escaping Reducer<Container.Element>
) -> Promise<Value> {
return items.reduce(self) { promise, item in
promise.then { value in
try reducer(value, item)
}
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import FBLPromises
/// Creates a pending promise that fulfills with the same value as the promise returned from `work`
/// block, which executes asynchronously on the given `queue`, or rejects with the same error after
/// all retry attempts have been exhausted. On rejection, the `work` block is retried after the
/// given delay `interval` and will continue to retry until the number of specified attempts have
/// been exhausted or will bail early if the given condition is not met.
///
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - count: Max number of retry attempts. The `work` block will be executed once if the specified
/// count is less than or equal to zero. The default is
/// `__FBLPromiseRetryDefaultAttemptsCount`.
/// - interval: Time to wait before the next retry attempt. The default is
/// `__FBLPromiseRetryDefaultDelayInterval`.
/// - predicate: Condition to check before the next retry attempt. The block takes the following
/// parameters:
/// - count: Number of remaining retry attempts.
/// - error: The error the promise was rejected with.
/// - work: A block that executes asynchronously on the given `queue` and returns a value or an
/// error used to resolve the promise.
/// - returns: A new pending promise that fulfills with the same value as the promise returned from
/// `work` block, or rejects with the same error after all retry attempts have been
/// exhausted or if the given condition is not met.
public func retry<Value>(
on queue: DispatchQueue = .promises,
attempts count: Int = __FBLPromiseRetryDefaultAttemptsCount,
delay interval: TimeInterval = __FBLPromiseRetryDefaultDelayInterval,
condition predicate: ((_ count: Int, _ error: Error) -> Bool)? = nil,
_ work: @escaping () throws -> Promise<Value>
) -> Promise<Value> {
#if (swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3)))
let predicateBlock = predicate
#else
var predicateBlock: ((_ count: Int, _ error: Error) -> ObjCBool)?
if predicate != nil {
predicateBlock = { count, error -> ObjCBool in
guard let predicate = predicate else { return true }
return ObjCBool(predicate(count, error))
}
}
#endif // (swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3)))
let objCPromise = Promise<Value>.ObjCPromise<AnyObject>.__onQueue(
queue,
attempts: count,
delay: interval,
condition: predicateBlock
) {
do {
return try work().objCPromise
} catch let error {
return error as NSError
}
}
let promise = Promise<Value>(objCPromise)
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}

View File

@ -0,0 +1,27 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import FBLPromises
extension DispatchGroup {
/// Dispatch group for promises that is typically used to wait for all scheduled blocks.
static var promises: DispatchGroup { return Promise<Any>.ObjCPromise<AnyObject>.__dispatchGroup }
}
/// Waits for all scheduled promise blocks.
/// - parameter timeout: Maximum time to wait.
/// - returns: `true` if all promise blocks have completed before `timeout` and `false` otherwise.
func waitForPromises(timeout: TimeInterval) -> Bool {
return __FBLWaitForPromisesWithTimeout(timeout)
}

View File

@ -0,0 +1,105 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
typealias Then<Result> = (Value) throws -> Result
/// Creates a pending promise which eventually gets resolved with the same resolution as the
/// promise returned from `work` block. The `work` block is executed asynchronously on the given
/// `queue` only when `self` is fulfilled. If `self` is rejected, the returned promise is also
/// rejected with the same error.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block to handle the value that `self` was fulfilled with.
/// - returns: A new pending promise to be resolved with the same resolution as the promise
/// returned from the `work` block.
@discardableResult
func then<Result>(
on queue: DispatchQueue = .promises,
_ work: @escaping Then<Promise<Result>>
) -> Promise<Result> {
let promise = Promise<Result>(objCPromise.__onQueue(queue, then: { objCValue in
guard let value = Promise<Value>.asValue(objCValue) else {
preconditionFailure("Cannot cast \(type(of: objCValue)) to \(Value.self)")
}
do {
return try work(value).objCPromise
} catch let error {
return error as NSError
}
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
/// Creates a pending promise which eventually gets resolved with the value returned from `work`
/// block. The `work` block is executed asynchronously on the given `queue` only when `self` is
/// fulfilled. If `self` is rejected, the returned promise is also rejected with the same error.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block to handle the value that `self` was fulfilled with.
/// - returns: A new pending promise to be resolved with the value returned from the `work` block.
@discardableResult
func then<Result>(
on queue: DispatchQueue = .promises,
_ work: @escaping Then<Result>
) -> Promise<Result> {
let promise = Promise<Result>(objCPromise.__onQueue(queue, then: { objCValue in
guard let value = Promise<Value>.asValue(objCValue) else {
preconditionFailure("Cannot cast \(type(of: objCValue)) to \(Value.self)")
}
do {
let value = try work(value)
return type(of: value) is NSError.Type
? value as! NSError : Promise<Result>.asAnyObject(value)
} catch let error {
return error as NSError
}
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
/// Creates a pending promise which eventually gets resolved with the same resolution as `self`.
/// `work` block is executed asynchronously on the given `queue` only when `self` is fulfilled.
/// If `self` is rejected, the returned promise is also rejected with the same error.
/// - parameters:
/// - queue: A queue to invoke the `work` block on.
/// - work: A block to handle the value that `self` was fulfilled with.
/// - returns: A new pending promise to be resolved with the value passed into the `work` block.
@discardableResult
func then(
on queue: DispatchQueue = .promises,
_ work: @escaping Then<Void>
) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, then: { objCValue in
guard let value = Promise<Value>.asValue(objCValue) else {
preconditionFailure("Cannot cast \(type(of: objCValue)) to \(Value.self)")
}
do {
try work(value)
return Promise<Value>.asAnyObject(value)
} catch let error {
return error as NSError
}
}))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,32 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
/// Waits on a promise for a given interval or rejects the promise if it exceeds the time limit.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - interval: Time to wait in seconds.
/// - returns: A new pending promise that gets either resolved with same resolution as `self` or
/// rejected with `PromiseError.timedOut` error.
@discardableResult
func timeout(on queue: DispatchQueue = .promises, _ interval: TimeInterval) -> Promise {
let promise = Promise(objCPromise.__onQueue(queue, timeout: interval))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,47 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public extension Promise {
/// Validates a fulfilled value or rejects the value if it can not be validated.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - predicate: An expression to validate.
/// - returns: A new pending promise that gets either resolved with same resolution as `self` or
/// rejected with `PromiseError.validationFailure` error.
@discardableResult
func validate(
on queue: DispatchQueue = .promises,
_ predicate: @escaping (Value) -> Bool
) -> Promise {
let promise = Promise(objCPromise.__onQueue(
queue,
validate: { objCValue in
guard let value = Promise<Value>.asValue(objCValue) else {
preconditionFailure("Cannot cast \(type(of: objCValue)) to \(Value.self)")
}
#if (swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3)))
return predicate(value)
#else
return ObjCBool(predicate(value))
#endif // (swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3)))
}
))
// Keep Swift wrapper alive for chained promise until `ObjCPromise` counterpart is resolved.
objCPromise.__addPendingObject(promise)
return promise
}
}

View File

@ -0,0 +1,163 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Dispatch
/// Provides a convenient way to convert methods that use common callback patterns into `Promise`s.
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with no arguments.
/// - returns: A new pending promise to be resolved with `nil` when completion handler finishes.
public func wrap(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping () -> Void) throws -> Void
) -> Promise<Any?> {
return Promise<Any?>(on: queue) { fulfill, _ in
try work { fulfill(nil) }
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with one argument of generic `Value` type.
/// - returns: A new pending promise to be resolved with the value provided by completion handler
/// when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Value) -> Void) throws -> Void
) -> Promise<Value> {
return Promise<Value>(on: queue) { fulfill, _ in
try work { fulfill($0) }
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with one argument of optional generic `Value` type.
/// - returns: A new pending promise to be resolved with the value or error provided by completion
/// handler when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Value?) -> Void) throws -> Void
) -> Promise<Value?> {
return Promise<Value?>(on: queue) { fulfill, _ in
try work { fulfill($0) }
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with two arguments: a generic of `Value` type and
/// an optional `Error`.
/// - returns: A new pending promise to be resolved with the value or error provided by completion
/// handler when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Value, Error?) -> Void) throws -> Void
) -> Promise<Value> {
return Promise<Value>(on: queue) { fulfill, reject in
try work { value, error in
if let error = error {
reject(error)
} else {
fulfill(value)
}
}
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with two arguments: an optional `Error` and a generic of
/// `Value` type.
/// - returns: A new pending promise to be resolved with the error or value provided by completion
/// handler when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Error?, Value) -> Void) throws -> Void
) -> Promise<Value> {
return Promise<Value>(on: queue) { fulfill, reject in
try work { error, value in
if let error = error {
reject(error)
} else {
fulfill(value)
}
}
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with two arguments: an optional generic of `Value` type
/// and an optional `Error`.
/// - returns: A new pending promise to be resolved with the value or error provided by completion
/// handler when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Value?, Error?) -> Void) throws -> Void
) -> Promise<Value?> {
return Promise<Value?>(on: queue) { fulfill, reject in
try work { value, error in
if let error = error {
reject(error)
} else {
fulfill(value)
}
}
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with two arguments: an optional `Error` and an optional
/// generic of `Value` type.
/// - returns: A new pending promise to be resolved with the error or value provided by completion
/// handler when it finishes.
public func wrap<Value>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Error?, Value?) -> Void) throws -> Void
) -> Promise<Value?> {
return Promise<Value?>(on: queue) { fulfill, reject in
try work { error, value in
if let error = error {
reject(error)
} else {
fulfill(value)
}
}
}
}
/// Creates a new promise to be resolved when completion handler gets invoked.
/// - parameter work: A block to execute asynchronously to invoke some API that requires
/// a completion handler with three arguments: two optionals of `Any` type
/// and an optional `Error`.
/// - returns: A new pending promise to be resolved with a tuple of optional values or an error
/// provided by completion handler when it finishes.
public func wrap<Value1, Value2>(
on queue: DispatchQueue = .promises,
_ work: @escaping (@escaping (Value1?, Value2?, Error?) -> Void) throws -> Void
) -> Promise<(Value1?, Value2?)> {
return Promise<(Value1?, Value2?)>(on: queue) { fulfill, reject in
try work { value1, value2, error in
if let error = error {
reject(error)
} else {
fulfill((value1, value2))
}
}
}
}

View File

@ -0,0 +1,146 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import FBLPromises
/// Promises synchronization construct in Swift. Leverages ObjC implementation internally.
public final class Promise<Value> {
public typealias ObjCPromise<T: AnyObject> = FBLPromise<T>
/// Creates a new promise with an existing ObjC promise.
public init<T>(_ objCPromise: ObjCPromise<T>) {
guard let objCPromise = objCPromise as? ObjCPromise<AnyObject> else {
preconditionFailure("Cannot cast \(T.self) to \(AnyObject.self)")
}
self.objCPromise = objCPromise
}
/// Creates a new pending promise.
public static func pending() -> Promise<Value> {
return Promise<Value>.init(ObjCPromise<AnyObject>.__pending())
}
/// Creates a new pending promise.
public convenience init() {
self.init(ObjCPromise<AnyObject>.__pending())
}
/// Creates a new promise rejected with the given `error`.
public convenience init(_ error: Error) {
self.init(ObjCPromise<AnyObject>.__resolved(with: error as NSError))
}
/// Creates a new promise resolved with the result of `work` block.
public convenience init(_ work: @autoclosure () throws -> Value) {
do {
let resolution = try work()
if type(of: resolution) is NSError.Type {
let error = resolution as! NSError
self.init(error)
} else if let objCPromise = resolution as? ObjCPromise<AnyObject> {
self.init(objCPromise)
} else {
self.init(ObjCPromise<AnyObject>.__resolved(with: Promise<Value>.asAnyObject(resolution)))
}
} catch let error {
self.init(error as NSError)
}
}
/// Resolves `self` with the given `resolution`.
public func fulfill(_ resolution: Value) {
objCPromise.__fulfill(Promise<Value>.asAnyObject(resolution))
}
/// Rejects `self` with the given `error`.
public func reject(_ error: Error) {
objCPromise.__fulfill(error as NSError)
}
/// Converts `self` into ObjC promise.
public func asObjCPromise<T>() -> ObjCPromise<T> {
guard let objCPromise = objCPromise as? ObjCPromise<T> else {
preconditionFailure("Cannot cast \(AnyObject.self) to \(T.self)")
}
return objCPromise
}
// MARK: Internal
/// Underlying ObjC counterpart.
let objCPromise: ObjCPromise<AnyObject>
var isPending: Bool { return objCPromise.__isPending }
var isFulfilled: Bool { return objCPromise.__isFulfilled }
var isRejected: Bool { return objCPromise.__isRejected }
var value: Value? {
let objCValue = objCPromise.__value
if Promise<AnyObject>.isBridgedNil(objCValue) { return nil }
guard let value = objCValue as? Value else {
preconditionFailure("Cannot cast \(type(of: objCValue)) to \(Value.self)")
}
return value
}
var error: Error? {
guard let objCPromiseError = objCPromise.__error else { return nil }
// Convert `NSError` to `PromiseError`, if applicable.
return PromiseError(objCPromiseError) ?? objCPromiseError
}
/// Converts generic `Value` to `AnyObject`.
static func asAnyObject(_ value: Value) -> AnyObject? {
return Promise<Value>.isBridgedNil(value) ? nil : value as AnyObject
}
/// Converts `AnyObject` to generic `Value`, or `nil` if the conversion is not possible.
static func asValue(_ value: AnyObject?) -> Value? {
// Swift nil becomes NSNull during bridging.
return (value as? Value) ?? NSNull() as AnyObject as? Value
}
// MARK: Private
/// Checks if generic `Value` is bridged ObjC `nil`.
private static func isBridgedNil(_ value: Value?) -> Bool {
// Swift nil becomes NSNull during bridging.
return !(value is NSNull) && (value as AnyObject is NSNull)
}
}
extension Promise: CustomStringConvertible {
public var description: String {
var description = "nil"
if isFulfilled {
if let value = value { description = String(describing: value) }
return "Fulfilled: \(description)"
}
if isRejected {
if let error = error { description = String(describing: error) }
return "Rejected: \(description)"
}
return "Pending: \(Value.self)"
}
}
public extension DispatchQueue {
/// Default dispatch queue used for `Promise`, which is `main` if a queue is not specified.
static var promises: DispatchQueue {
get { return Promise<Any>.ObjCPromise<AnyObject>.__defaultDispatchQueue }
set { Promise<Any>.ObjCPromise<AnyObject>.__defaultDispatchQueue = newValue }
}
}

View File

@ -0,0 +1,58 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import FBLPromises
/// Internal errors that `Promise` can throw.
/// Indirectly conforms to `Swift.Error` through conformance to `Swift.CustomNSError` below.
/// Not placing it under extension `Promise` for convenience to avoid collisions with `Swift.Error`.
public enum PromiseError {
case timedOut
case validationFailure
}
/// Downcasting from `Swift.Error`.
extension PromiseError {
public init?(_ error: Error) {
let error = error as NSError
if error.domain != __FBLPromiseErrorDomain { return nil }
switch error.code {
case __FBLPromiseErrorCode.timedOut.rawValue:
self = .timedOut
case __FBLPromiseErrorCode.validationFailure.rawValue:
self = .validationFailure
default:
return nil
}
}
}
extension PromiseError: CustomNSError {
public static var errorDomain: String {
return __FBLPromiseErrorDomain
}
public var errorCode: Int {
switch self {
case .timedOut:
return __FBLPromiseErrorCode.timedOut.rawValue
case .validationFailure:
return __FBLPromiseErrorCode.validationFailure.rawValue
}
}
public var errorUserInfo: [String: Any] {
return [String: Any]()
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>