diff --git a/.gitmodules b/.gitmodules index 651b467..9733255 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Xcode/dependencies/Atomics"] path = Xcode/dependencies/Atomics url = https://github.com/glessard/swift-atomics.git +[submodule "Xcode/dependencies/Outcome"] + path = Xcode/dependencies/Outcome + url = https://github.com/glessard/outcome.git diff --git a/Package.swift b/Package.swift index bbea4a4..987158b 100644 --- a/Package.swift +++ b/Package.swift @@ -15,9 +15,10 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/glessard/swift-atomics.git", from: "4.1.0"), + .package(url: "https://github.com/glessard/outcome.git", from: "4.1.5") ], targets: [ - .target(name: "deferred", dependencies: ["CAtomics"]), + .target(name: "deferred", dependencies: ["CAtomics", "Outcome"]), .testTarget(name: "deferredTests", dependencies: ["deferred"]), ], swiftLanguageVersions: versions diff --git a/Source/deferred/deferred-extras.swift b/Source/deferred/deferred-extras.swift index adb2965..ffa4756 100644 --- a/Source/deferred/deferred-extras.swift +++ b/Source/deferred/deferred-extras.swift @@ -51,7 +51,7 @@ extension Deferred public func enqueuing(on queue: DispatchQueue) -> Deferred { - return Transfer(queue: queue, source: self) + return Transferred(from: self, on: queue) } /// Get a `Deferred` that will have the same `result` as `self` once determined, diff --git a/Source/deferred/deferred.swift b/Source/deferred/deferred.swift index e8c7c1a..80f662c 100644 --- a/Source/deferred/deferred.swift +++ b/Source/deferred/deferred.swift @@ -7,7 +7,7 @@ // import Dispatch - +import Outcome import CAtomics /// The possible states of a `Deferred`. @@ -35,20 +35,20 @@ extension UnsafeMutableRawPointer /// /// A `Deferred` that becomes determined, will henceforth always be determined: it can no longer mutate. /// -/// The `get()` function will return the computation result (or throw an `Error`), +/// The `get()` function will return the value of the computation's result (or throw an `Error`), /// blocking until it becomes available. If the result is ready when `get()` is called, /// it will return immediately. The properties `value` and `error` are convenient non-throwing wrappers /// for the `get()` method -- although they do block. /// -/// Closures supplied to the `enqueuing` function will be called after the `Deferred` has become determined. -/// The functions `map`, `flatMap`, `notify` and others are wrappers that add functionality to the `enqueuing` function. +/// Closures supplied to the `enqueue` function will be called after the `Deferred` has become determined. +/// The functions `map`, `flatMap`, `notify` and others are wrappers that add functionality to the `enqueue` function. open class Deferred { let queue: DispatchQueue private var source: AnyObject? - private var determination: Determined? + private var determined: Outcome? private var waiters = AtomicMutableRawPointer() private var stateid = AtomicInt32() @@ -66,7 +66,7 @@ open class Deferred { self.queue = queue ?? source.queue self.source = source - determination = nil + determined = nil waiters.initialize(nil) stateid.initialize(beginExecution && (source.stateid.load(.relaxed) != 0) ? 1:0) } @@ -75,25 +75,31 @@ open class Deferred { self.queue = queue source = nil - determination = nil + determined = nil waiters.initialize(nil) stateid.initialize(0) } - /// Initialize to an already determined state + /// Initialize with a pre-determined `Outcome` /// - /// - parameter queue: the dispatch queue upon which to execute future notifications for this `Deferred` - /// - parameter result: the result of this `Deferred` + /// - parameter queue: the dispatch queue upon which to execute future notifications for this `Deferred` + /// - parameter outcome: the `Outcome` of this `Deferred` - public init(queue: DispatchQueue, result: Determined) + public init(queue: DispatchQueue, outcome: Outcome) { self.queue = queue source = nil - determination = result + determined = outcome waiters.initialize(.determined) stateid.initialize(2) } + @available(*, deprecated, renamed: "init(queue:outcome:)") + public convenience init(queue: DispatchQueue, result: Outcome) + { + self.init(queue: queue, outcome: result) + } + /// Initialize with a computation task to be performed on the specified queue /// /// - parameter queue: the `DispatchQueue` on which the computation (and notifications) will be executed @@ -103,7 +109,7 @@ open class Deferred { self.queue = queue source = nil - determination = nil + determined = nil waiters.initialize(nil) stateid.initialize(1) @@ -149,7 +155,7 @@ open class Deferred public convenience init(queue: DispatchQueue, value: Value) { - self.init(queue: queue, result: Determined(value: value)) + self.init(queue: queue, outcome: Outcome(value: value)) } /// Initialize with an Error @@ -170,7 +176,7 @@ open class Deferred public convenience init(queue: DispatchQueue, error: Error) { - self.init(queue: queue, result: Determined(error: error)) + self.init(queue: queue, outcome: Outcome(error: error)) } // MARK: state changes / determine @@ -188,16 +194,16 @@ open class Deferred } while !stateid.loadCAS(¤t, 1, .weak, .relaxed, .relaxed) } - /// Set the `Result` of this `Deferred` and dispatch all notifications for execution. + /// Set the `Outcome` of this `Deferred` and dispatch all notifications for execution. /// Note that a `Deferred` can only be determined once. /// On subsequent calls, `determine` will fail and return `false`. /// This operation is lock-free and thread-safe. /// - /// - parameter result: the intended `Result` to determine this `Deferred` + /// - parameter outcome: the intended `Outcome` to determine this `Deferred` /// - returns: whether the call succesfully changed the state of this `Deferred`. @discardableResult - fileprivate func determine(_ result: Determined) -> Bool + fileprivate func determine(_ outcome: Outcome) -> Bool { var current = stateid.load(.relaxed) repeat { // keep trying if another thread hasn't succeeded yet @@ -207,20 +213,20 @@ open class Deferred } } while !stateid.loadCAS(¤t, 2, .weak, .relaxed, .relaxed) - determination = result + determined = outcome source = nil let waitQueue = waiters.swap(.determined, .acqrel)?.assumingMemoryBound(to: Waiter.self) // precondition(waitQueue != .determined) - notifyWaiters(queue, waitQueue, result) + notifyWaiters(queue, waitQueue, outcome) // precondition(waiters.load() == .determined, "waiters.pointer has incorrect value \(String(describing: waiters.load()))") - // The result is now available for the world + // The outcome has been determined return true } - /// Set the `Result` of this `Deferred` and dispatch all notifications for execution. + /// Set the `Outcome` of this `Deferred` and dispatch all notifications for execution. /// Note that a `Deferred` can only be determined once. /// On subsequent calls, `determine` will fail and return `false`. /// This operation is lock-free and thread-safe. @@ -240,7 +246,7 @@ open class Deferred @discardableResult fileprivate func determine(_ value: Value) -> Bool { - return determine(Determined(value: value)) + return determine(Outcome(value: value)) } /// Set this `Deferred` to an error and dispatch all notifications for execution. @@ -254,7 +260,7 @@ open class Deferred @discardableResult fileprivate func determine(_ error: Error) -> Bool { - return determine(Determined(error: error)) + return determine(Outcome(error: error)) } // MARK: public interface @@ -271,9 +277,9 @@ open class Deferred /// - parameter queue: the `DispatchQueue` on which to dispatch this notification when ready; defaults to `self`'s queue. /// - parameter boostQoS: whether `enqueue` should attempt to boost the QoS if `queue.qos` is higher than `self.qos`; defaults to `true` /// - parameter task: a closure to be executed after `self` becomes determined. - /// - parameter result: the determined result of `self` + /// - parameter outcome: the determined `Outcome` of `self` - open func enqueue(queue: DispatchQueue? = nil, boostQoS: Bool = true, task: @escaping (_ result: Determined) -> Void) + open func enqueue(queue: DispatchQueue? = nil, boostQoS: Bool = true, task: @escaping (_ outcome: Outcome) -> Void) { var waitQueue = waiters.load(.acquire) if waitQueue != .determined @@ -305,7 +311,7 @@ open class Deferred // this Deferred is determined let q = queue ?? self.queue - q.async(execute: { [value = determination!] in task(value) }) + q.async(execute: { [outcome = determined!] in task(outcome) }) } /// Enqueue a notification to be performed asynchronously after this `Deferred` becomes determined. @@ -313,11 +319,11 @@ open class Deferred /// /// - parameter queue: the `DispatchQueue` on which to dispatch this notification when ready; defaults to `self`'s queue. /// - parameter task: a closure to be executed as a notification - /// - parameter result: the determined result of `self` + /// - parameter outcome: the determined `Outcome` of `self` - public func notify(queue: DispatchQueue? = nil, task: @escaping (_ result: Determined) -> Void) + public func notify(queue: DispatchQueue? = nil, task: @escaping (_ outcome: Outcome) -> Void) { - enqueue(queue: queue, task: { value in withExtendedLifetime(self) { task(value) } }) + enqueue(queue: queue, task: { outcome in withExtendedLifetime(self) { task(outcome) } }) } /// Query the current state of this `Deferred` @@ -326,7 +332,7 @@ open class Deferred public var state: DeferredState { return (waiters.load(.acquire) != .determined) ? (stateid.load(.relaxed) == 0 ? .waiting : .executing ) : - (determination!.isValue ? .succeeded : .errored) + (determined!.isValue ? .succeeded : .errored) } /// Query whether this `Deferred` has become determined. @@ -357,13 +363,13 @@ open class Deferred return determine(error) } - /// Get this `Deferred`'s `Determined` result, blocking if necessary until it exists. + /// Get this `Deferred`'s `Outcome` result, blocking if necessary until it exists. /// When called on a `Deferred` that is already determined, this call is non-blocking. /// When called on a `Deferred` that is not determined, this call blocks the executing thread. /// - /// - returns: this `Deferred`'s determined result + /// - returns: this `Deferred`'s determined `Outcome` - public var result: Determined { + public var outcome: Outcome { if waiters.load(.acquire) != .determined { if let current = DispatchQoS.QoSClass.current, current > queue.qos.qosClass @@ -378,9 +384,12 @@ open class Deferred } // this Deferred is determined - return determination! + return determined! } + @available(*, deprecated, renamed: "outcome") + public var result: Outcome { return self.outcome } + /// Get this `Deferred`'s value, blocking if necessary until it becomes determined. /// If the `Deferred` is determined with an `Error`, throw it. /// When called on a `Deferred` that is already determined, this call is non-blocking. @@ -390,19 +399,19 @@ open class Deferred public func get() throws -> Value { - return try result.get() + return try outcome.get() } - /// Get this `Deferred`'s `Determined` result if exists, `nil` otherwise. + /// Get this `Deferred`'s `Outcome` result if exists, `nil` otherwise. /// This call is non-blocking. /// /// - returns: this `Deferred`'s determined result, or `nil` - public func peek() -> Determined? + public func peek() -> Outcome? { if waiters.load(.acquire) == .determined { - return determination + return determined } return nil } @@ -415,7 +424,7 @@ open class Deferred /// - returns: this `Deferred`'s determined value, or `nil` public var value: Value? { - return result.value + return outcome.value } /// Get this `Deferred`'s error state, blocking if necessary until it becomes determined. @@ -426,7 +435,7 @@ open class Deferred /// - returns: this `Deferred`'s determined error state, or `nil` public var error: Error? { - return result.error + return outcome.error } /// Get the QoS of this `Deferred`'s queue @@ -453,12 +462,12 @@ class Map: Deferred super.init(queue: queue, source: source) source.enqueue(queue: queue) { - [weak self] value in + [weak self] outcome in guard let this = self else { return } if this.isDetermined { return } this.beginExecution() do { - let value = try value.get() + let value = try outcome.get() let transformed = try transform(value) this.determine(transformed) } @@ -469,7 +478,7 @@ class Map: Deferred } } -class Transfer: Deferred +open class Transferred: Deferred { /// Transfer a `Deferred` result to a new `Deferred` that notifies on a new queue. /// (Acts like a fast path for a Map with no transform.) @@ -478,23 +487,24 @@ class Transfer: Deferred /// - parameter queue: the `DispatchQueue` onto which the new `Deferred` should dispatch notifications; use `source.queue` if `nil` /// - parameter source: the `Deferred` whose value will be transferred into a new instance. - init(queue: DispatchQueue, source: Deferred) + public init(from source: Deferred, on queue: DispatchQueue? = nil) { - if let result = source.peek() + if let outcome = source.peek() { - super.init(queue: queue, result: result) + super.init(queue: queue ?? source.queue, outcome: outcome) } else { - super.init(queue: queue, source: source, beginExecution: true) - source.enqueue(queue: queue, boostQoS: false, task: { [weak self] in self?.determine($0) }) + super.init(queue: queue ?? source.queue, source: source, beginExecution: true) + source.enqueue(queue: queue, boostQoS: false, + task: { [weak self] outcome in self?.determine(outcome) }) } } } class Flatten: Deferred { - /// Flatten a Deferred-of-a-Deferred to a Deferred. + /// Flatten a Deferred> to a Deferred. /// (In the right conditions, acts like a fast path for a flatMap with no transform.) /// This constructor is used by `flatten()` /// @@ -503,14 +513,14 @@ class Flatten: Deferred init(queue: DispatchQueue? = nil, source: Deferred>) { - if let determined = source.peek() + if let outcome = source.peek() { let mine = queue ?? source.queue do { - let deferred = try determined.get() - if let result = deferred.peek() + let deferred = try outcome.get() + if let outcome = deferred.peek() { - super.init(queue: mine, result: result) + super.init(queue: mine, outcome: outcome) } else { @@ -519,19 +529,19 @@ class Flatten: Deferred } } catch { - super.init(queue: mine, result: Determined(error: error)) + super.init(queue: mine, outcome: Outcome(error: error)) } return } super.init(queue: queue, source: source) source.enqueue(queue: queue) { - [weak self] determined in + [weak self] outcome in do { - let deferred = try determined.get() - if let result = deferred.peek() + let deferred = try outcome.get() + if let outcome = deferred.peek() { - self?.determine(result) + self?.determine(outcome) } else { @@ -565,12 +575,12 @@ class Bind: Deferred super.init(queue: queue, source: source) source.enqueue(queue: queue) { - [weak self] value in + [weak self] outcome in guard let this = self else { return } if this.isDetermined { return } this.beginExecution() do { - let value = try value.get() + let value = try outcome.get() let transformed = try transform(value) transformed.notify(queue: queue) { [weak this] transformed in @@ -599,11 +609,11 @@ class Recover: Deferred super.init(queue: queue, source: source) source.enqueue(queue: queue) { - [weak self] determined in + [weak self] outcome in guard let this = self else { return } if this.isDetermined { return } this.beginExecution() - if let error = determined.error + if let error = outcome.error { do { let transformed = try transform(error) @@ -618,7 +628,7 @@ class Recover: Deferred } else { - this.determine(determined) + this.determine(outcome) } } } @@ -641,11 +651,11 @@ class Apply: Deferred super.init(queue: queue, source: source) source.enqueue(queue: queue) { - [weak self] value in + [weak self] outcome in guard let this = self else { return } if this.isDetermined { return } do { - let value = try value.get() + let value = try outcome.get() transform.notify(queue: queue) { [weak this] transform in guard let this = this else { return } @@ -686,13 +696,13 @@ class Delay: Deferred super.init(queue: queue, source: source) source.enqueue(queue: queue, boostQoS: false) { - [weak self] value in + [weak self] outcome in guard let this = self else { return } if this.isDetermined { return } - if value.isError + if outcome.isError { - this.determine(value) + this.determine(outcome) return } @@ -703,12 +713,12 @@ class Delay: Deferred { this.queue.asyncAfter(deadline: time) { [weak this] in - this?.determine(value) + this?.determine(outcome) } } else { - this.determine(value) + this.determine(outcome) } } } @@ -737,18 +747,18 @@ open class TBD: Deferred self.init(queue: queue) } - /// Set the `Result` of this `Deferred` and dispatch all notifications for execution. + /// Set the `Outcome` of this `Deferred` and dispatch all notifications for execution. /// Note that a `Deferred` can only be determined once. /// On subsequent calls, `determine` will fail and return `false`. /// This operation is lock-free and thread-safe. /// - /// - parameter result: the determined value for this `Deferred` + /// - parameter outcome: the intended `Outcome` for this `Deferred` /// - returns: whether the call succesfully changed the state of this `Deferred`. @discardableResult - open override func determine(_ result: Determined) -> Bool + open override func determine(_ outcome: Outcome) -> Bool { - return super.determine(result) + return super.determine(outcome) } /// Set the value of this `Deferred` and dispatch all notifications for execution. @@ -762,7 +772,7 @@ open class TBD: Deferred @discardableResult open override func determine(_ value: Value) -> Bool { - return determine(Determined(value: value)) + return determine(Outcome(value: value)) } /// Set this `Deferred` to an error and dispatch all notifications for execution. @@ -776,7 +786,7 @@ open class TBD: Deferred @discardableResult open override func determine(_ error: Error) -> Bool { - return determine(Determined(error: error)) + return determine(Outcome(error: error)) } /// Change the state of this `TBD` from `.waiting` to `.executing` diff --git a/Source/deferred/determined.swift b/Source/deferred/determined.swift index 38940b8..b05a088 100644 --- a/Source/deferred/determined.swift +++ b/Source/deferred/determined.swift @@ -2,112 +2,10 @@ // determined.swift // deferred // -// Created by Guillaume Lessard on 9/26/17. -// Copyright © 2017 Guillaume Lessard. All rights reserved. +// Created by Guillaume Lessard on 9/19/18. // -public struct Determined -{ - fileprivate let state: State +import Outcome - public init(value: Value) - { - state = .value(value) - } - - public init(error: Error) - { - state = .error(error) - } - - public func get() throws -> Value - { - switch state - { - case .value(let value): return value - case .error(let error): throw error - } - } - - public var value: Value? { - if case .value(let value) = state { return value } - return nil - } - - public var error: Error? { - if case .error(let error) = state { return error } - return nil - } - - public var isValue: Bool { - if case .value = state { return true } - return false - } - - public var isError: Bool { - if case .error = state { return true } - return false - } -} - -extension Determined: CustomStringConvertible -{ - public var description: String { - switch state - { - case .value(let value): return String(describing: value) - case .error(let error): return "Error: \(error)" - } - } -} - -#if swift (>=4.1) -extension Determined: Equatable where Value: Equatable -{ - public static func ==(lhs: Determined, rhs: Determined) -> Bool - { - switch (lhs.state, rhs.state) - { - case (.value(let lhv), .value(let rhv)): - return lhv == rhv - case (.error(let lhe), .error(let rhe)): - return String(describing: lhe) == String(describing: rhe) - default: - return false - } - } -} -#endif - -#if swift (>=4.2) -extension Determined: Hashable where Value: Hashable -{ - public func hash(into hasher: inout Hasher) - { - switch self.state - { - case .value(let value): - Int(0).hash(into: &hasher) - value.hash(into: &hasher) - case .error(let error): - Int(1).hash(into: &hasher) - String(describing: error).hash(into: &hasher) - } - } -} -#endif - -#if swift (>=4.2) -@usableFromInline -enum State -{ - case value(Value) - case error(Error) -} -#else -enum State -{ -case value(Value) -case error(Error) -} -#endif +@available(*, deprecated, renamed: "Outcome") +public typealias Determined = Outcome diff --git a/Source/deferred/nsurlsession.swift b/Source/deferred/nsurlsession.swift index bf2212e..6682fda 100644 --- a/Source/deferred/nsurlsession.swift +++ b/Source/deferred/nsurlsession.swift @@ -7,6 +7,7 @@ // import Dispatch +import Outcome import Foundation //import struct Foundation.Data @@ -29,14 +30,18 @@ public enum URLSessionError: Error case InvalidState } -class DeferredURLSessionTask: Transfer +public class DeferredURLSessionTask: Transferred { public let urlSessionTask: URLSessionTask init(source: TBD, task: URLSessionTask) { urlSessionTask = task - super.init(queue: source.queue, source: source) + super.init(from: source, on: source.queue) + } + + deinit { + urlSessionTask.cancel() } @discardableResult @@ -49,7 +54,8 @@ class DeferredURLSessionTask: Transfer return true } - public override func enqueue(queue: DispatchQueue? = nil, boostQoS: Bool = true, task: @escaping (Determined) -> Void) + public override func enqueue(queue: DispatchQueue? = nil, boostQoS: Bool = true, + task: @escaping (Outcome) -> Void) { if state == .waiting { @@ -90,55 +96,55 @@ public extension URLSession } public func deferredDataTask(qos: DispatchQoS = .current, - with request: URLRequest) -> Deferred<(Data, HTTPURLResponse)> + with request: URLRequest) -> DeferredURLSessionTask<(Data, HTTPURLResponse)> { - guard let scheme = request.url?.scheme, scheme == "http" || scheme == "https" - else { - let e = DeferredError.invalid( - "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" - ) - return Deferred(error: e) + let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) + + if let scheme = request.url?.scheme, + scheme != "http" && scheme != "https" + { + let message = "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" + tbd.determine(DeferredError.invalid(message)) } - let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) let task = dataTask(with: request, completionHandler: dataCompletion(tbd)) return DeferredURLSessionTask(source: tbd, task: task) } public func deferredDataTask(qos: DispatchQoS = .current, - with url: URL) -> Deferred<(Data, HTTPURLResponse)> + with url: URL) -> DeferredURLSessionTask<(Data, HTTPURLResponse)> { return deferredDataTask(qos: qos, with: URLRequest(url: url)) } public func deferredUploadTask(qos: DispatchQoS = .current, - with request: URLRequest, fromData bodyData: Data) -> Deferred<(Data, HTTPURLResponse)> + with request: URLRequest, fromData bodyData: Data) -> DeferredURLSessionTask<(Data, HTTPURLResponse)> { - guard let scheme = request.url?.scheme, scheme == "http" || scheme == "https" - else { - let e = DeferredError.invalid( - "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" - ) - return Deferred(error: e) + let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) + + if let scheme = request.url?.scheme, + scheme != "http" && scheme != "https" + { + let message = "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" + tbd.determine(DeferredError.invalid(message)) } - let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) let task = uploadTask(with: request, from: bodyData, completionHandler: dataCompletion(tbd)) return DeferredURLSessionTask(source: tbd, task: task) } public func deferredUploadTask(qos: DispatchQoS = .current, - with request: URLRequest, fromFile fileURL: URL) -> Deferred<(Data, HTTPURLResponse)> + with request: URLRequest, fromFile fileURL: URL) -> DeferredURLSessionTask<(Data, HTTPURLResponse)> { - guard let scheme = request.url?.scheme, scheme == "http" || scheme == "https" - else { - let e = DeferredError.invalid( - "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" - ) - return Deferred(error: e) + let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) + + if let scheme = request.url?.scheme, + scheme != "http" && scheme != "https" + { + let message = "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" + tbd.determine(DeferredError.invalid(message)) } - let tbd = TBD<(Data, HTTPURLResponse)>(qos: qos) let task = uploadTask(with: request, fromFile: fileURL, completionHandler: dataCompletion(tbd)) return DeferredURLSessionTask(source: tbd, task: task) } @@ -209,29 +215,29 @@ extension URLSession } public func deferredDownloadTask(qos: DispatchQoS = .current, - with request: URLRequest) -> Deferred<(URL, FileHandle, HTTPURLResponse)> + with request: URLRequest) -> DeferredURLSessionTask<(URL, FileHandle, HTTPURLResponse)> { - guard let scheme = request.url?.scheme, scheme == "http" || scheme == "https" - else { - let e = DeferredError.invalid( - "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" - ) - return Deferred(error: e) + let tbd = TBD<(URL, FileHandle, HTTPURLResponse)>(qos: qos) + + if let scheme = request.url?.scheme, + scheme != "http" && scheme != "https" + { + let message = "deferred does not support url scheme \"\(request.url?.scheme! ?? "unknown")\"" + tbd.determine(DeferredError.invalid(message)) } - let tbd = TBD<(URL, FileHandle, HTTPURLResponse)>(qos: qos) let task = downloadTask(with: request, completionHandler: downloadCompletion(tbd)) return DeferredDownloadTask(source: tbd, task: task) } public func deferredDownloadTask(qos: DispatchQoS = .current, - with url: URL) -> Deferred<(URL, FileHandle, HTTPURLResponse)> + with url: URL) -> DeferredURLSessionTask<(URL, FileHandle, HTTPURLResponse)> { return deferredDownloadTask(qos: qos, with: URLRequest(url: url)) } public func deferredDownloadTask(qos: DispatchQoS = .current, - withResumeData data: Data) -> Deferred<(URL, FileHandle, HTTPURLResponse)> + withResumeData data: Data) -> DeferredURLSessionTask<(URL, FileHandle, HTTPURLResponse)> { let tbd = TBD<(URL, FileHandle, HTTPURLResponse)>(qos: qos) diff --git a/Source/deferred/waiter.swift b/Source/deferred/waiter.swift index 27d325d..681ed7b 100644 --- a/Source/deferred/waiter.swift +++ b/Source/deferred/waiter.swift @@ -7,27 +7,28 @@ // import Dispatch +import Outcome struct Waiter { private let queue: DispatchQueue? - private let handler: (Determined) -> Void + private let handler: (Outcome) -> Void var next: UnsafeMutablePointer>? = nil - init(_ queue: DispatchQueue?, _ handler: @escaping (Determined) -> Void) + init(_ queue: DispatchQueue?, _ handler: @escaping (Outcome) -> Void) { self.queue = queue self.handler = handler } - fileprivate func notify(_ queue: DispatchQueue, _ value: Determined) + fileprivate func notify(_ queue: DispatchQueue, _ value: Outcome) { let q = self.queue ?? queue q.async { [handler = self.handler] in handler(value) } } } -func notifyWaiters(_ queue: DispatchQueue, _ tail: UnsafeMutablePointer>?, _ value: Determined) +func notifyWaiters(_ queue: DispatchQueue, _ tail: UnsafeMutablePointer>?, _ value: Outcome) { var head = reverseList(tail) while let current = head diff --git a/Tests/deferredTests/DeferredTests.swift b/Tests/deferredTests/DeferredTests.swift index 65643ce..f87b7d2 100644 --- a/Tests/deferredTests/DeferredTests.swift +++ b/Tests/deferredTests/DeferredTests.swift @@ -278,8 +278,8 @@ class DeferredTests: XCTestCase return 42 } d3.notify { - determined in - guard let e = determined.error, + outcome in + guard let e = outcome.error, let deferredErr = e as? DeferredError, case .canceled = deferredErr else @@ -549,8 +549,8 @@ class DeferredTests: XCTestCase var v1 = 0 var v2 = 0 result.notify { - determined in - XCTAssert(determined.value == (Double(v1*v2))) + outcome in + XCTAssert(outcome.value == (Double(v1*v2))) expect.fulfill() } diff --git a/Tests/deferredTests/DeterminedTests.swift b/Tests/deferredTests/DeterminedTests.swift deleted file mode 100644 index ed7874c..0000000 --- a/Tests/deferredTests/DeterminedTests.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// DeterminedTests.swift -// deferred -// -// Created by Guillaume Lessard on 6/5/18. -// Copyright © 2018 Guillaume Lessard. All rights reserved. -// - -import XCTest - -import deferred - -class DeterminedTests: XCTestCase -{ - func testEquals() - { -#if swift(>=4.1) - let i1 = nzRandom() - let i2 = nzRandom() - let i3 = i1*i2 - - let o3 = Determined(value: i1*i2) - XCTAssert(o3 == Determined(value: i3)) - XCTAssert(o3 != Determined(value: i2)) - - var o4 = o3 - o4 = Determined(error: TestError(i1)) - XCTAssert(o3 != o4) - XCTAssert(o4 != Determined(error: TestError(i2))) -#endif - } - - func testHashable() - { -#if swift(>=4.2) - let d1 = Determined(value: nzRandom()) - let d2 = Determined(error: TestError(nzRandom())) - - let set = Set([d1, d2]) - - XCTAssert(set.contains(d1)) -#endif - } - - func testGetters() throws - { - let v1 = nzRandom() - let v2 = nzRandom() - - let detValue = Determined(value: v1) - let detError = Determined(error: TestError(v2)) - - do { - let v = try detValue.get() - XCTAssert(v1 == v) - - let _ = try detError.get() - } - catch let error as TestError { - XCTAssert(v2 == error.error) - } - - XCTAssertNotNil(detValue.value) - XCTAssertNotNil(detError.error) - - XCTAssertNil(detValue.error) - XCTAssertNil(detError.value) - - XCTAssertTrue(detValue.isValue) - XCTAssertTrue(detError.isError) - - XCTAssertFalse(detValue.isError) - XCTAssertFalse(detError.isValue) - } - - func testCustomStringConvertible() throws - { - let value = Determined(value: 1) - let error = value.isError ? value : Determined(error: TestError(1)) - - let v = String(describing: value) - let e = String(describing: error) - - XCTAssert(v != e) - // print(v, "\n", e, separator: "") - } -} diff --git a/Tests/deferredTests/TBDTests.swift b/Tests/deferredTests/TBDTests.swift index 3a3af11..35df289 100644 --- a/Tests/deferredTests/TBDTests.swift +++ b/Tests/deferredTests/TBDTests.swift @@ -120,8 +120,8 @@ class TBDTests: XCTestCase let e3 = expectation(description: "TBD never determined") let d3 = TBD() d3.notify { - determined in - XCTAssert(determined.error as? DeferredError == DeferredError.canceled("")) + outcome in + XCTAssert(outcome.error as? DeferredError == DeferredError.canceled("")) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.2) { // This will trigger the `XCWaitCompletionHandler` in the `waitForExpectationsWithTimeout` call below. diff --git a/Tests/deferredTests/URLSessionTests.swift b/Tests/deferredTests/URLSessionTests.swift index 62f96c7..7d59a55 100644 --- a/Tests/deferredTests/URLSessionTests.swift +++ b/Tests/deferredTests/URLSessionTests.swift @@ -51,11 +51,12 @@ class URLSessionTests: XCTestCase return false } - switch success.result - { - case .value(let success) where success == true: break // savor success - case .value: XCTFail("Failed without error") - case .error(let error): XCTFail(String(describing: error)) + do { + let success = try success.get() + XCTAssert(success, "Failed with error") + } + catch { + XCTFail(String(describing: error)) } session.invalidateAndCancel() @@ -66,23 +67,22 @@ class URLSessionTests: XCTestCase let url = URL(string: basePath)! var request = URLRequest(url: url) request.httpMethod = "POST" - request.httpBody = Data(fromString: "name=John Tester&age=97&data=****") + request.httpBody = String("name=John Tester&age=97&data=****").data(using: .utf8) let session = URLSession(configuration: URLSessionConfiguration.ephemeral) let dataTask = session.deferredDataTask(with: request) - switch dataTask.result - { - case .value(let data, let response): + do { + let (data, response) = try dataTask.get() XCTAssert(response.statusCode == 200) XCTAssert(data.count > 0) - if let i = String(fromData: data).components(separatedBy: " ").last + if let i = String(data: data, encoding: .utf8)?.components(separatedBy: " ").last { XCTAssert(Int(i) == 4) } else { XCTFail("unexpected data in response") } - - case .error(let error): + } + catch { XCTFail(String(describing: error)) } @@ -112,7 +112,7 @@ class URLSessionTests: XCTestCase func testData_DoubleCancellation() { - let deferred: DeferredURLSessionTask<(Data, HTTPURLResponse)> = { + let deferred: Deferred<(Data, HTTPURLResponse)> = { let url = URL(string: imagePath)! let session = URLSession(configuration: URLSessionConfiguration.ephemeral) defer { session.invalidateAndCancel() } @@ -142,7 +142,7 @@ class URLSessionTests: XCTestCase let session = URLSession(configuration: URLSessionConfiguration.ephemeral) let deferred = session.deferredDataTask(with: url) - deferred.task?.suspend() + deferred.urlSessionTask.suspend() let canceled = deferred.cancel() XCTAssert(canceled) @@ -166,12 +166,12 @@ class URLSessionTests: XCTestCase let deferred = session.deferredDataTask(with: request) - switch deferred.result - { - case .value(let data, let response): + do { + let (data, response) = try deferred.get() XCTAssert(data.count > 0) XCTAssert(response.statusCode == 404) - case .error(let error): + } + catch { XCTFail(String(describing: error)) } @@ -209,10 +209,12 @@ class URLSessionTests: XCTestCase return false } - switch success.result - { - case .value(let success): XCTAssert(success, "Failed without error") - case .error(let error): XCTFail(String(describing: error)) + do { + let success = try success.get() + XCTAssert(success, "Failed without error") + } + catch { + XCTFail(String(describing: error)) } session.invalidateAndCancel() @@ -271,7 +273,7 @@ class URLSessionTests: XCTestCase let session = URLSession(configuration: URLSessionConfiguration.ephemeral) let deferred = session.deferredDownloadTask(with: url) - deferred.task?.suspend() + deferred.urlSessionTask.suspend() let canceled = deferred.cancel() XCTAssert(canceled) @@ -360,14 +362,14 @@ class URLSessionTests: XCTestCase let deferred = session.deferredDownloadTask(with: request) - switch deferred.result - { - case .value(_, let handle, let response): + do { + let (_, handle, response) = try deferred.get() let data = handle.readDataToEndOfFile() handle.closeFile() XCTAssert(data.count > 0) XCTAssert(response.statusCode == 404) - case .error(let error): + } + catch { XCTFail(String(describing: error)) } @@ -382,7 +384,7 @@ class URLSessionTests: XCTestCase var request = URLRequest(url: url) request.httpMethod = "POST" - let data = Data(fromString: "name=John Tester&age=97") + let data = String("name=John Tester&age=97").data(using: .utf8)! let deferred = session.deferredUploadTask(with: request, fromData: data) let canceled = deferred.cancel() @@ -409,19 +411,18 @@ class URLSessionTests: XCTestCase request.httpMethod = method let payload = "data=" + String(repeatElement("A", count: 1995)) - let length = payload.characters.count - let message = Data(fromString: payload) + let length = payload.count + let message = payload.data(using: .utf8)! XCTAssert(message.count == length) let task = session.deferredUploadTask(with: request, fromData: message) - switch task.result - { - case let .value(data, response): + do { + let (data, response) = try task.get() XCTAssert(response.statusCode == 200) - XCTAssert(task.task?.countOfBytesSent == Int64(length)) + XCTAssert(task.urlSessionTask.countOfBytesSent == Int64(length)) - if case let reply = String(fromData: data), + if let reply = String(data: data, encoding: .utf8), let text = reply.components(separatedBy: " ").last, let tlen = Int(text) { @@ -431,8 +432,8 @@ class URLSessionTests: XCTestCase { XCTFail("Unexpected data in response") } - - case .error(let error): + } + catch { XCTFail(String(describing: error)) } @@ -458,8 +459,8 @@ class URLSessionTests: XCTestCase request.httpMethod = method let payload = "data=" + String(repeatElement("A", count: 1995)) - let length = payload.characters.count - let message = Data(fromString: payload) + let length = payload.count + let message = payload.data(using: .utf8)! XCTAssert(message.count == length) let path = NSTemporaryDirectory() + "temporary.tmp" @@ -481,13 +482,12 @@ class URLSessionTests: XCTestCase let task = session.deferredUploadTask(with: request, fromFile: fileurl) - switch task.result - { - case let .value(data, response): + do { + let (data, response) = try task.get() XCTAssert(response.statusCode == 200) - XCTAssert(task.task?.countOfBytesSent == Int64(length)) + XCTAssert(task.urlSessionTask.countOfBytesSent == Int64(length)) - if case let reply = String(fromData: data), + if let reply = String(data: data, encoding: .utf8), let text = reply.components(separatedBy: " ").last, let tlen = Int(text) { @@ -497,8 +497,8 @@ class URLSessionTests: XCTestCase { XCTFail("Unexpected data in response") } - - case .error(let error): + } + catch { XCTFail(String(describing: error)) } diff --git a/Tests/deferredTests/XCTestManifests.swift b/Tests/deferredTests/XCTestManifests.swift index e584ebe..9f583f7 100644 --- a/Tests/deferredTests/XCTestManifests.swift +++ b/Tests/deferredTests/XCTestManifests.swift @@ -77,15 +77,6 @@ extension DeletionTests { ] } -extension DeterminedTests { - static let __allTests = [ - ("testCustomStringConvertible", testCustomStringConvertible), - ("testEquals", testEquals), - ("testGetters", testGetters), - ("testHashable", testHashable), - ] -} - extension DispatchUtilitiesTests { static let __allTests = [ ("testCurrent", testCurrent), @@ -125,7 +116,6 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(DeferredCombinationTimedTests.__allTests), testCase(DeferredTests.__allTests), testCase(DeletionTests.__allTests), - testCase(DeterminedTests.__allTests), testCase(DispatchUtilitiesTests.__allTests), testCase(TBDTests.__allTests), testCase(TBDTimingTests.__allTests), diff --git a/Xcode/deferred.xcodeproj/project.pbxproj b/Xcode/deferred.xcodeproj/project.pbxproj index a7142c6..5c9b067 100644 --- a/Xcode/deferred.xcodeproj/project.pbxproj +++ b/Xcode/deferred.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 03110CEE20D2069F00F4B44B /* deferred.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 03B4E06D1C14003B00F4B44B /* deferred.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 032F0AA91E4454D600F4B44B /* DeletionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 038147591E4138BA00F4B44B /* DeletionTests.swift */; }; 033928191F8C099700F4B44B /* deferred-first.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033928181F8C099700F4B44B /* deferred-first.swift */; }; + 0358CBDD2153109600F4B44B /* Outcome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0358CBDA2153107C00F4B44B /* Outcome.framework */; }; + 0358CBDE2153109F00F4B44B /* Outcome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0358CBDA2153107C00F4B44B /* Outcome.framework */; }; + 0358CBDF2153109F00F4B44B /* Outcome.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0358CBDA2153107C00F4B44B /* Outcome.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 035D0C151D84A8DB00F4B44B /* deferred-combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D0C0D1D84A8DB00F4B44B /* deferred-combine.swift */; }; 035D0C171D84A8DB00F4B44B /* deferred-error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D0C0E1D84A8DB00F4B44B /* deferred-error.swift */; }; 035D0C191D84A8DB00F4B44B /* deferred-extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D0C0F1D84A8DB00F4B44B /* deferred-extras.swift */; }; @@ -20,7 +23,6 @@ 0366AE5A1DF0FEC400F4B44B /* TBDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366AE521DF0FEC400F4B44B /* TBDTests.swift */; }; 0366AE5C1DF0FEC400F4B44B /* TBDTimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366AE531DF0FEC400F4B44B /* TBDTimingTests.swift */; }; 0366AE5E1DF0FEC400F4B44B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366AE541DF0FEC400F4B44B /* TestError.swift */; }; - 0370F2D620C8E25400F4B44B /* DeterminedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0370F2D520C8E25400F4B44B /* DeterminedTests.swift */; }; 03857DB81F55C3DE00F4B44B /* deferred-timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03857DB71F55C3DE00F4B44B /* deferred-timing.swift */; }; 038CB65620DC637600F4B44B /* CAtomics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 038CB64F20DC636D00F4B44B /* CAtomics.framework */; }; 038CB65920DC639400F4B44B /* CAtomics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 038CB64F20DC636D00F4B44B /* CAtomics.framework */; }; @@ -39,7 +41,6 @@ 03B7769C20CAED1C00F4B44B /* DeferredCombinationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03AA21731E3FF9D600F4B44B /* DeferredCombinationTests.swift */; }; 03B7769D20CAED1C00F4B44B /* DeletionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 038147591E4138BA00F4B44B /* DeletionTests.swift */; }; 03B7769E20CAED1C00F4B44B /* TBDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366AE521DF0FEC400F4B44B /* TBDTests.swift */; }; - 03B776A020CAED1C00F4B44B /* DeterminedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0370F2D520C8E25400F4B44B /* DeterminedTests.swift */; }; 03B776A120CAED1C00F4B44B /* DispatchUtilitiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E8795A1F6C98ED00F4B44B /* DispatchUtilitiesTests.swift */; }; 03B776A220CAED1C00F4B44B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0366AE541DF0FEC400F4B44B /* TestError.swift */; }; 03B776A320CAED1C00F4B44B /* nzRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0374DFB01DFF5BD800F4B44B /* nzRandom.swift */; }; @@ -72,6 +73,27 @@ remoteGlobalIDString = 03B4E06C1C14003B00F4B44B; remoteInfo = deferred; }; + 0358CBD92153107C00F4B44B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 0312008C2152C95100F4B44B; + remoteInfo = Outcome; + }; + 0358CBDB2153107C00F4B44B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 031200952152C95100F4B44B; + remoteInfo = OutcomeTests; + }; + 0358CBE02153109F00F4B44B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 0312008B2152C95100F4B44B; + remoteInfo = Outcome; + }; 038CB64720DC636D00F4B44B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 030A0C7820CB48DF00F4B44B /* Atomics.xcodeproj */; @@ -153,6 +175,7 @@ files = ( 038CB65A20DC63A500F4B44B /* CAtomics.framework in Embed Frameworks */, 03110CEE20D2069F00F4B44B /* deferred.framework in Embed Frameworks */, + 0358CBDF2153109F00F4B44B /* Outcome.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -163,6 +186,7 @@ 030A0C7820CB48DF00F4B44B /* Atomics.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Atomics.xcodeproj; path = Atomics/Xcode/Atomics.xcodeproj; sourceTree = ""; }; 033928181F8C099700F4B44B /* deferred-first.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "deferred-first.swift"; path = "deferred/deferred-first.swift"; sourceTree = ""; }; 034857621C11BD5E00F4B44B /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Package.swift; sourceTree = ""; }; + 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Outcome.xcodeproj; path = Outcome/Xcode/Outcome.xcodeproj; sourceTree = ""; }; 035D0C0D1D84A8DB00F4B44B /* deferred-combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "deferred-combine.swift"; path = "deferred/deferred-combine.swift"; sourceTree = ""; }; 035D0C0E1D84A8DB00F4B44B /* deferred-error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "deferred-error.swift"; path = "deferred/deferred-error.swift"; sourceTree = ""; }; 035D0C0F1D84A8DB00F4B44B /* deferred-extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "deferred-extras.swift"; path = "deferred/deferred-extras.swift"; sourceTree = ""; }; @@ -175,7 +199,6 @@ 0366AE531DF0FEC400F4B44B /* TBDTimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBDTimingTests.swift; path = DeferredTests/TBDTimingTests.swift; sourceTree = ""; }; 0366AE541DF0FEC400F4B44B /* TestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestError.swift; path = DeferredTests/TestError.swift; sourceTree = ""; }; 0366AE551DF0FEC400F4B44B /* URLSessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = URLSessionTests.swift; path = DeferredTests/URLSessionTests.swift; sourceTree = ""; }; - 0370F2D520C8E25400F4B44B /* DeterminedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeterminedTests.swift; path = deferredTests/DeterminedTests.swift; sourceTree = ""; }; 0372B2DC1F8DBB2900F4B44B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 0374DFB01DFF5BD800F4B44B /* nzRandom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = nzRandom.swift; path = DeferredTests/nzRandom.swift; sourceTree = ""; }; 038147591E4138BA00F4B44B /* DeletionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeletionTests.swift; path = deferredTests/DeletionTests.swift; sourceTree = ""; }; @@ -208,6 +231,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0358CBDD2153109600F4B44B /* Outcome.framework in Frameworks */, 038CB65620DC637600F4B44B /* CAtomics.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -226,6 +250,7 @@ files = ( 038CB65920DC639400F4B44B /* CAtomics.framework in Frameworks */, 03B776A420CAED2F00F4B44B /* deferred.framework in Frameworks */, + 0358CBDE2153109F00F4B44B /* Outcome.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -244,6 +269,7 @@ 030A0C3220CB48CA00F4B44B /* dependencies */ = { isa = PBXGroup; children = ( + 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */, 030A0C7820CB48DF00F4B44B /* Atomics.xcodeproj */, ); path = dependencies; @@ -269,6 +295,15 @@ name = Frameworks; sourceTree = ""; }; + 0358CBD52153107C00F4B44B /* Products */ = { + isa = PBXGroup; + children = ( + 0358CBDA2153107C00F4B44B /* Outcome.framework */, + 0358CBDC2153107C00F4B44B /* OutcomeTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; 03850ABD1C76AB2200F4B44B /* Source */ = { isa = PBXGroup; children = ( @@ -298,7 +333,6 @@ 0366AE521DF0FEC400F4B44B /* TBDTests.swift */, 0366AE531DF0FEC400F4B44B /* TBDTimingTests.swift */, 0366AE551DF0FEC400F4B44B /* URLSessionTests.swift */, - 0370F2D520C8E25400F4B44B /* DeterminedTests.swift */, 03E8795A1F6C98ED00F4B44B /* DispatchUtilitiesTests.swift */, 0366AE541DF0FEC400F4B44B /* TestError.swift */, 0374DFB01DFF5BD800F4B44B /* nzRandom.swift */, @@ -440,6 +474,7 @@ dependencies = ( 038CB65820DC638E00F4B44B /* PBXTargetDependency */, 03B776A720CAED2F00F4B44B /* PBXTargetDependency */, + 0358CBE12153109F00F4B44B /* PBXTargetDependency */, ); name = deferredTest; productName = deferredTest; @@ -515,6 +550,10 @@ ProductGroup = 030A0C7920CB48DF00F4B44B /* Products */; ProjectRef = 030A0C7820CB48DF00F4B44B /* Atomics.xcodeproj */; }, + { + ProductGroup = 0358CBD52153107C00F4B44B /* Products */; + ProjectRef = 0358CBD42153107C00F4B44B /* Outcome.xcodeproj */; + }, ); projectRoot = ""; targets = ( @@ -541,6 +580,20 @@ remoteRef = 030A0C8120CB48DF00F4B44B /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 0358CBDA2153107C00F4B44B /* Outcome.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Outcome.framework; + remoteRef = 0358CBD92153107C00F4B44B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 0358CBDC2153107C00F4B44B /* OutcomeTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = OutcomeTests.xctest; + remoteRef = 0358CBDB2153107C00F4B44B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 038CB64F20DC636D00F4B44B /* CAtomics.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -646,7 +699,6 @@ files = ( 0366AE5E1DF0FEC400F4B44B /* TestError.swift in Sources */, 03AA21741E3FF9D600F4B44B /* DeferredCombinationTests.swift in Sources */, - 0370F2D620C8E25400F4B44B /* DeterminedTests.swift in Sources */, 0366AE5C1DF0FEC400F4B44B /* TBDTimingTests.swift in Sources */, 039E18411DFF621300F4B44B /* nzRandom.swift in Sources */, 03E8795B1F6C98ED00F4B44B /* DispatchUtilitiesTests.swift in Sources */, @@ -677,7 +729,6 @@ 03B776A220CAED1C00F4B44B /* TestError.swift in Sources */, 03B7769C20CAED1C00F4B44B /* DeferredCombinationTests.swift in Sources */, 03B776A120CAED1C00F4B44B /* DispatchUtilitiesTests.swift in Sources */, - 03B776A020CAED1C00F4B44B /* DeterminedTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -689,6 +740,11 @@ target = 03B4E06C1C14003B00F4B44B /* deferred */; targetProxy = 03110CEA20D2065900F4B44B /* PBXContainerItemProxy */; }; + 0358CBE12153109F00F4B44B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Outcome; + targetProxy = 0358CBE02153109F00F4B44B /* PBXContainerItemProxy */; + }; 038CB64820DC636D00F4B44B /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = CAtomics; diff --git a/Xcode/dependencies/Outcome b/Xcode/dependencies/Outcome new file mode 160000 index 0000000..4a435ba --- /dev/null +++ b/Xcode/dependencies/Outcome @@ -0,0 +1 @@ +Subproject commit 4a435ba0f030e58a3c505a14275a1f4b0521f222