From 5f07f701c867ae9e24ffe03bd153f781eea11ff8 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:10:37 +0200 Subject: [PATCH 01/61] Added Cartfile --- Cartfile | 1 + 1 file changed, 1 insertion(+) create mode 100644 Cartfile diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..9e4df54 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "antitypical/Result" ~> 0.4.1 From 223fab5640d39f27157d3a8eb23fbc5e4e0f8c69 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:10:47 +0200 Subject: [PATCH 02/61] carthage bootstrap --- Cartfile.resolved | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Cartfile.resolved diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..1586bce --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,2 @@ +github "robrix/Box" "1.2.2" +github "antitypical/Result" "0.4.1" From 55861e5b193b7e902f1ac08b50bf1c90fb115068 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:13:05 +0200 Subject: [PATCH 03/61] Added frameworks to project --- BrightFutures.xcodeproj/project.pbxproj | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index a3863d5..7f60ade 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -7,6 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + B31A24241B0268F80016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; + B31A24251B0268F80016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; + B31A24291B02691A0016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; + B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; + B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; + B31A24341B026D500016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; + B31A24351B026D500016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; + B31A243A1B0279120016AE7A /* Result.framework in Resources */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; + B31A243B1B0279150016AE7A /* Box.framework in Resources */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */; }; E907D1DE1A6AE4A000AB8075 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */; }; E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9319EA31A397D2C00A0604A /* QueueTests.swift */; }; @@ -53,6 +62,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + B31A24221B0268F80016AE7A /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; + B31A24231B0268F80016AE7A /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Build/iOS/Box.framework; sourceTree = ""; }; + B31A24271B02691A0016AE7A /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; }; + B31A24281B02691A0016AE7A /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Build/Mac/Box.framework; sourceTree = ""; }; E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = ""; }; E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Semaphore.swift; sourceTree = ""; }; E9319EA31A397D2C00A0604A /* QueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueTests.swift; sourceTree = ""; }; @@ -78,6 +91,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B31A24291B02691A0016AE7A /* Result.framework in Frameworks */, + B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,6 +101,8 @@ buildActionMask = 2147483647; files = ( E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */, + B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */, + B31A24321B026D4B0016AE7A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -93,6 +110,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + B31A24241B0268F80016AE7A /* Result.framework in Frameworks */, + B31A24251B0268F80016AE7A /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -101,15 +120,45 @@ buildActionMask = 2147483647; files = ( E9DF0821194470060083F7F2 /* BrightFutures.framework in Frameworks */, + B31A24351B026D500016AE7A /* Box.framework in Frameworks */, + B31A24341B026D500016AE7A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + B31A24261B0269060016AE7A /* Frameworks */ = { + isa = PBXGroup; + children = ( + B31A242B1B0269280016AE7A /* Mac */, + B31A242C1B02692E0016AE7A /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + B31A242B1B0269280016AE7A /* Mac */ = { + isa = PBXGroup; + children = ( + B31A24271B02691A0016AE7A /* Result.framework */, + B31A24281B02691A0016AE7A /* Box.framework */, + ); + name = Mac; + sourceTree = ""; + }; + B31A242C1B02692E0016AE7A /* iOS */ = { + isa = PBXGroup; + children = ( + B31A24221B0268F80016AE7A /* Result.framework */, + B31A24231B0268F80016AE7A /* Box.framework */, + ); + name = iOS; + sourceTree = ""; + }; E9DF080B194470060083F7F2 = { isa = PBXGroup; children = ( + B31A24261B0269060016AE7A /* Frameworks */, E9DF0817194470060083F7F2 /* BrightFutures */, E9DF0824194470060083F7F2 /* BrightFuturesTests */, E9DF0816194470060083F7F2 /* Products */, @@ -235,6 +284,7 @@ E9DF0811194470060083F7F2 /* Frameworks */, E9DF0812194470060083F7F2 /* Headers */, E9DF0813194470060083F7F2 /* Resources */, + B31A242E1B0269790016AE7A /* Carthage */, ); buildRules = ( ); @@ -252,6 +302,7 @@ E9DF081C194470060083F7F2 /* Sources */, E9DF081D194470060083F7F2 /* Frameworks */, E9DF081E194470060083F7F2 /* Resources */, + B31A24381B02785F0016AE7A /* Carthage */, ); buildRules = ( ); @@ -319,6 +370,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + B31A243B1B0279150016AE7A /* Box.framework in Resources */, + B31A243A1B0279120016AE7A /* Result.framework in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -338,6 +391,41 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + B31A242E1B0269790016AE7A /* Carthage */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/Box.framework", + "$(SRCROOT)/Carthage/Build/iOS/Result.framework", + ); + name = Carthage; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks"; + }; + B31A24381B02785F0016AE7A /* Carthage */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/Box.framework", + "$(SRCROOT)/Carthage/Build/iOS/Result.framework", + ); + name = Carthage; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ E9D45AFB1AF00659000F6CA7 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -417,6 +505,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); FRAMEWORK_VERSION = A; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -445,6 +537,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); FRAMEWORK_VERSION = A; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = BrightFutures/Info.plist; @@ -466,6 +562,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", ); GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -490,6 +587,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", ); GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = BrightFuturesTests/Info.plist; @@ -595,6 +693,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = BrightFutures/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -613,6 +715,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = BrightFutures/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -627,6 +733,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -645,6 +752,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = BrightFuturesTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; From 36c398bb8f15828c7959f5ade6f7593d4d3453c5 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:47:11 +0200 Subject: [PATCH 04/61] Added NSError to all type signatures of Result --- BrightFutures/Future.swift | 37 ++++---- BrightFutures/FutureUtils.swift | 3 +- BrightFutures/Promise.swift | 9 +- BrightFutures/Result.swift | 99 +-------------------- BrightFuturesTests/BrightFuturesTests.swift | 13 +-- BrightFuturesTests/ResultTests.swift | 11 +-- 6 files changed, 40 insertions(+), 132 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index a5f530b..7f4b70a 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -21,6 +21,7 @@ // SOFTWARE. import Foundation +import Result /// Executes the given task on `Queue.global` and wraps the result of the task in a Future public func future(@autoclosure(escaping) task: () -> T) -> Future { @@ -34,23 +35,23 @@ public func future(task: () -> T) -> Future { /// Executes the given task on the given context and wraps the result of the task in a Future public func future(context c: ExecutionContext, task: () -> T) -> Future { - return future(context: c, { () -> Result in return Result(task()) + return future(context: c, { () -> Result in }) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(@autoclosure(escaping) task: () -> Result) -> Future { +public func future(@autoclosure(escaping) task: () -> Result) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(task: () -> Result) -> Future { +public func future(task: () -> Result) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on the given context and wraps the result of the task in a Future -public func future(context c: ExecutionContext, task: () -> Result) -> Future { +public func future(context c: ExecutionContext, task: () -> Result) -> Future { let promise = Promise(); c { @@ -115,12 +116,12 @@ internal func errorFromCode(code: ErrorCode, failureReason: String? = nil) -> NS public class Future { typealias CallbackInternal = (future: Future) -> () - typealias CompletionCallback = (result: Result) -> () + typealias CompletionCallback = (result: Result) -> () typealias SuccessCallback = (T) -> () public typealias FailureCallback = (NSError) -> () /// The result of the operation this Future represents or `nil` if it is not yet completed - public internal(set) var result: Result? = nil + public internal(set) var result: Result? = nil /// This queue is used for all callback related administrative tasks /// to prevent that a callback is added to a completed future and never @@ -153,7 +154,7 @@ internal extension Future { /// Completes the future with the given result /// If the future is already completed, this function does nothing /// and an assert will be raised (if enabled) - func complete(result: Result) { + func complete(result: Result) { let succeeded = tryComplete(result) assert(succeeded) } @@ -161,7 +162,7 @@ internal extension Future { /// Tries to complete the future with the given result /// If the future is already completed, nothing happens and `false` is returned /// otherwise the future is completed and `true` is returned - func tryComplete(result: Result) -> Bool { + func tryComplete(result: Result) -> Bool { switch result { case .Success(let val): return self.trySuccess(val.value) @@ -276,7 +277,7 @@ public extension Future { } /// Returns a new future that completed with the given result - public class func completed(result: Result) -> Future { + public class func completed(result: Result) -> Future { let res = Future() res.result = result @@ -317,24 +318,24 @@ public extension Future { public extension Future { /// Blocks the current thread until the future is completed and then returns the result - public func forced() -> Result? { + public func forced() -> Result? { return self.forced(TimeInterval.Forever) } - /// See `forced(timeout: TimeInterval) -> Result?` - public func forced(timeout: NSTimeInterval) -> Result? { + /// See `forced(timeout: TimeInterval) -> Result?` + public func forced(timeout: NSTimeInterval) -> Result? { return self.forced(.In(timeout)) } /// Blocks the current thread until the future is completed, but no longer than the given timeout /// If the future did not complete before the timeout, `nil` is returned, otherwise the result of the future is returned - public func forced(timeout: TimeInterval) -> Result? { + public func forced(timeout: TimeInterval) -> Result? { if let certainResult = self.result { return certainResult } else { let sema = Semaphore(value: 0) - var res: Result? = nil + var res: Result? = nil self.onComplete(context: Queue.global.context) { res = $0 sema.signal() @@ -430,7 +431,7 @@ public extension Future { /// Transforms the given closure returning `Result` to a closure returning `Future` and then calls /// `flatMap(context c: ExecutionContext, f: T -> Future) -> Future` - public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Result) -> Future { + public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Result) -> Future { return self.flatMap(context: c) { value in Future.completed(f(value)) } @@ -465,7 +466,7 @@ public extension Future { /// Adds the given closure as a callback for when this future completes. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) /// Returns a future that completes with the result from this future but only after executing the given closure - public func andThen(context c: ExecutionContext = executionContextForCurrentContext(), callback: Result -> ()) -> Future { + public func andThen(context c: ExecutionContext = executionContextForCurrentContext(), callback: Result -> ()) -> Future { let p = Promise() self.onComplete(context: c) { result in @@ -520,7 +521,7 @@ public extension Future { /// `ErrorCode.NoSuchElement` if the test failed. /// If this future fails, the returned future fails with the same error. public func filter(p: T -> Bool) -> Future { - return self.flatMap { value -> Result in + return self.flatMap { value -> Result in if p(value) { return .Success(Box(value)) } else { @@ -541,7 +542,7 @@ public extension Future { /// See `onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future` /// If the given invalidation token is invalidated when the future is completed, the given callback is not invoked - public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: Result -> ()) -> Future { + public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: Result -> ()) -> Future { firstCompletedOfSelfAndToken(token).onComplete(context: c) { res in token.context { if !token.isInvalid { diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index 4620354..dfa6c43 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -21,6 +21,7 @@ // SOFTWARE. import Foundation +import Result //// The free functions in this file operate on sequences of Futures @@ -80,7 +81,7 @@ public func find>(seq: /// error of the first failed future in the sequence. /// If no futures in the sequence pass the test, a future with an error with NoSuchElement is returned. public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future { - return sequence(seq).flatMap(context: c) { val -> Result in + return sequence(seq).flatMap(context: c) { val -> Result in for elem in val { if (p(elem)) { return .Success(Box(elem)) diff --git a/BrightFutures/Promise.swift b/BrightFutures/Promise.swift index 0247315..8a246a5 100644 --- a/BrightFutures/Promise.swift +++ b/BrightFutures/Promise.swift @@ -21,6 +21,7 @@ // SOFTWARE. import Foundation +import Result /// The source of a future. Create a `Promise` when you are /// performing an asynchronous task and want to return a future. @@ -75,14 +76,14 @@ public class Promise { } /// Completes the promise's future with the given result - /// See `future.complete(result: Result)` - public func complete(result: Result) { + /// See `future.complete(result: Result)` + public func complete(result: Result) { return self.future.complete(result) } /// Attempts to complete the promise's future with the given result - /// See `future.tryComplete(result: Result)` - public func tryComplete(result: Result) -> Bool { + /// See `future.tryComplete(result: Result)` + public func tryComplete(result: Result) -> Bool { return self.future.tryComplete(result) } diff --git a/BrightFutures/Result.swift b/BrightFutures/Result.swift index 4cc63e9..809d2fe 100644 --- a/BrightFutures/Result.swift +++ b/BrightFutures/Result.swift @@ -21,109 +21,12 @@ // SOFTWARE. import Foundation - -/// Boxes a value of type `T` -/// We have to box the Result value until Swift supports variable-layout enums -public final class Box { - - /// The boxed value - public let value: T - - /// Creates a new box with the given value - public init(_ value: T) { - self.value = value - } -} - -/// Represents the result of a failable operation, -/// which is either a succes with a value of type `T` -/// or a failure with an NSError -public enum Result { - case Success(Box) - case Failure(NSError) - - /// Creates a new .Success that wraps the given value - public init(_ value: T) { - self = .Success(Box(value)) - } - - /// `true` iff this result is a .Success - public var isSuccess: Bool { - get { - switch self { - case .Success(_): - return true - case .Failure(_): - return false - } - } - } - - /// `true` iff this result is a .Failure - public var isFailure: Bool { - get { - return !self.isSuccess - } - } - - /// Returns the value associated with this result if it is a .Success, `nil` otherwise - public var value: T? { - get { - switch self { - case .Success(let boxedValue): - return boxedValue.value - default: - return nil - } - } - } - - /// Returns the error associated with this result if it is a .Failure, `nil` otherwise - public var error: NSError? { - get { - switch self { - case .Failure(let error): - return error - default: - return nil - } - } - } -} +import Result extension Result { - /// Returns a .Success with the value returned from the given closure when invoked with the - /// value associated with this result if it is a .Success. If this result is a .Failure, a - /// .Failure with the same error is returned. - public func map(@noescape f:T -> U) -> Result { - switch self { - case .Success(let boxedValue): - return Result.Success(Box(f(boxedValue.value))) - case .Failure(let err): - return Result.Failure(err) - } - } - - /// Enables the chaining of two failable operations where the second operation - /// depends on the success value of the first. - /// Like map, the given closure (that performs the second operation) is only executed - /// if the first operation result (this result) is a .Success - /// If a regular `map` was used, the result would be `Result>`. - /// The implementation of this function uses `map`, but then flattens the result before returning it. - public func flatMap(@noescape f: T -> Result) -> Result { - return flatten(self.map(f)) - } - /// Enables the chaining of two failable operations where the second operation is asynchronous and /// represented by a future. See `flatMap(@noescape f: T -> Result) -> Result` - public func flatMap(@noescape f: T -> Future) -> Future { - return flatten(self.map(f)) - } -} - -extension Result { - /// Returns `self.value` if this result is a .Success, or the given value otherwise public func recover(value: T) -> T { return self.value ?? value diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index 3a48df4..1aa5e9d 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -21,6 +21,7 @@ // SOFTWARE. import XCTest +import Result import BrightFutures class BrightFuturesTests: XCTestCase { @@ -42,7 +43,7 @@ extension BrightFuturesTests { let completeExpectation = self.expectationWithDescription("immediate complete") - f.onComplete { (result: Result) in + f.onComplete { (result: Result) in XCTAssert(result.isSuccess) completeExpectation.fulfill() } @@ -339,8 +340,8 @@ extension BrightFuturesTests { let e = self.expectation() - future { () -> Result in .Failure(NSError(domain: "Tests", code: 123, userInfo: nil)) + future { () -> Result in }.map { number in XCTAssert(false, "map should not be evaluated because of failure above") }.map { number in @@ -417,7 +418,7 @@ extension BrightFuturesTests { } func testZipThisFails() { - let f = future { () -> Result in + let f = future { () -> Result in sleep(1) return .Failure(NSError(domain: "test", code: 2, userInfo: nil)) } @@ -436,7 +437,7 @@ extension BrightFuturesTests { } func testZipThatFails() { - let f = future { () -> Result in + let f = future { () -> Result in sleep(1) return .Failure(NSError(domain: "tester", code: 3, userInfo: nil)) } @@ -455,12 +456,12 @@ extension BrightFuturesTests { } func testZipBothFail() { - let f = future { () -> Result in + let f = future { () -> Result in sleep(1) return .Failure(NSError(domain: "f-error", code: 3, userInfo: nil)) } - let f1 = future { () -> Result in + let f1 = future { () -> Result in sleep(1) return .Failure(NSError(domain: "f1-error", code: 4, userInfo: nil)) } diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index f6f9fa0..bddbc3e 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -21,6 +21,7 @@ // SOFTWARE. import XCTest +import Result import BrightFutures class ResultTests: XCTestCase { @@ -87,7 +88,7 @@ class ResultTests: XCTestCase { } func testFlatMapResultFailure() { - let r = divide(20, 0).flatMap { i -> Result in + let r = divide(20, 0).flatMap { i -> Result in XCTAssert(false, "flatMap should not get called if the result failed") return divide(i, 2) } @@ -132,18 +133,18 @@ class ResultTests: XCTestCase { } func testSequenceSuccess() { - let results: [Result] = (1...10).map { i in + let results: [Result] = (1...10).map { i in return divide(123, i) } - let result: Result<[Int]> = sequence(results) + let result: Result<[Int],NSError> = sequence(results) let outcome = [123, 61, 41, 30, 24, 20, 17, 15, 13, 12] XCTAssertEqual(result.value!, outcome) } func testSequenceFailure() { - let results: [Result] = (-10...10).map { i in + let results: [Result] = (-10...10).map { i in return divide(123, i) } @@ -167,7 +168,7 @@ class ResultTests: XCTestCase { } } -func divide(a: Int, b: Int) -> Result { +func divide(a: Int, b: Int) -> Result { if (b == 0) { return .Failure(NSError(domain: "DivisionByZeroError", code: 0, userInfo: nil)) } From f36b849327751cc3883c636536ce375f4530d054 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:50:56 +0200 Subject: [PATCH 05/61] Replaced all initializations of Result with the constructor in Result.Result --- BrightFutures/Future.swift | 14 +++++------ BrightFutures/FutureUtils.swift | 4 ++-- BrightFuturesTests/BrightFuturesTests.swift | 26 ++++++++++----------- BrightFuturesTests/ResultTests.swift | 14 +++++------ 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 7f4b70a..c180756 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -35,8 +35,8 @@ public func future(task: () -> T) -> Future { /// Executes the given task on the given context and wraps the result of the task in a Future public func future(context c: ExecutionContext, task: () -> T) -> Future { - return Result(task()) return future(context: c, { () -> Result in + return Result(value: task()) }) } @@ -188,7 +188,7 @@ internal extension Future { return false; } - self.result = Result(value) + self.result = Result(value: value) self.runCallbacks() return true; }; @@ -211,7 +211,7 @@ internal extension Future { return false; } - self.result = .Failure(error) + self.result = Result(error: error) self.runCallbacks() return true; }; @@ -263,7 +263,7 @@ public extension Future { /// Returns a new future that succeeded with the given value public class func succeeded(value: T) -> Future { let res = Future(); - res.result = Result(value) + res.result = Result(value: value) return res } @@ -271,7 +271,7 @@ public extension Future { /// Returns a new future that failed with the given error public class func failed(error: NSError) -> Future { let res = Future(); - res.result = .Failure(error) + res.result = Result(error: error) return res } @@ -523,9 +523,9 @@ public extension Future { public func filter(p: T -> Bool) -> Future { return self.flatMap { value -> Result in if p(value) { - return .Success(Box(value)) + return Result(value: value) } else { - return .Failure(errorFromCode(.NoSuchElement)) + return Result(error: errorFromCode(.NoSuchElement)) } } } diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index dfa6c43..129ade2 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -84,10 +84,10 @@ public func find>(seq: return sequence(seq).flatMap(context: c) { val -> Result in for elem in val { if (p(elem)) { - return .Success(Box(elem)) + return Result(value: elem) } } - return .Failure(errorFromCode(.NoSuchElement)) + return Result(error: errorFromCode(.NoSuchElement)) } } diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index 1aa5e9d..72f870a 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -145,7 +145,7 @@ extension BrightFuturesTests { func testControlFlowSyntaxWithError() { let f : Future = future { - .Failure(NSError(domain: "NaN", code: 0, userInfo: nil)) + Result(error: NSError(domain: "NaN", code: 0, userInfo: nil)) } let failureExpectation = self.expectationWithDescription("failure expected") @@ -340,8 +340,8 @@ extension BrightFuturesTests { let e = self.expectation() - .Failure(NSError(domain: "Tests", code: 123, userInfo: nil)) future { () -> Result in + Result(error: NSError(domain: "Tests", code: 123, userInfo: nil)) }.map { number in XCTAssert(false, "map should not be evaluated because of failure above") }.map { number in @@ -380,7 +380,7 @@ extension BrightFuturesTests { let e = self.expectation() future { - .Failure(NSError(domain: "NaN", code: 0, userInfo: nil)) + Result(error: NSError(domain: "NaN", code: 0, userInfo: nil)) }.recoverWith { _ in return future { _ in fibonacci(5) @@ -420,7 +420,7 @@ extension BrightFuturesTests { func testZipThisFails() { let f = future { () -> Result in sleep(1) - return .Failure(NSError(domain: "test", code: 2, userInfo: nil)) + return Result(error: NSError(domain: "test", code: 2, userInfo: nil)) } let f1 = Future.succeeded(2) @@ -439,7 +439,7 @@ extension BrightFuturesTests { func testZipThatFails() { let f = future { () -> Result in sleep(1) - return .Failure(NSError(domain: "tester", code: 3, userInfo: nil)) + return Result(error: NSError(domain: "tester", code: 3, userInfo: nil)) } let f1 = Future.succeeded(2) @@ -458,12 +458,12 @@ extension BrightFuturesTests { func testZipBothFail() { let f = future { () -> Result in sleep(1) - return .Failure(NSError(domain: "f-error", code: 3, userInfo: nil)) + return Result(error: NSError(domain: "f-error", code: 3, userInfo: nil)) } let f1 = future { () -> Result in sleep(1) - return .Failure(NSError(domain: "f1-error", code: 4, userInfo: nil)) + return Result(error: NSError(domain: "f1-error", code: 4, userInfo: nil)) } let e = self.expectation() @@ -581,9 +581,9 @@ extension BrightFuturesTests { let evenFuture: Int -> Future = { i in return future { if i % 2 == 0 { - return .Success(Box(true)) + return Result(value: true) } else { - return .Failure(NSError(domain: "traverse-single-error", code: i, userInfo: nil)) + return Result(error: NSError(domain: "traverse-single-error", code: i, userInfo: nil)) } } } @@ -605,9 +605,9 @@ extension BrightFuturesTests { let evenFuture: Int -> Future = { i in return future { err in if i % 2 == 0 { - return .Success(Box(true)) + return Result(value: true) } else { - return .Failure(NSError(domain: "traverse-single-error", code: i, userInfo: nil)) + return Result(error: NSError(domain: "traverse-single-error", code: i, userInfo: nil)) } } } @@ -927,14 +927,14 @@ extension XCTestCase { func failingFuture() -> Future { return future { error in usleep(arc4random_uniform(100)) - return .Failure(NSError(domain: "failedFuture", code: 0, userInfo: nil)) + return Result(error: NSError(domain: "failedFuture", code: 0, userInfo: nil)) } } func succeedingFuture(val: U) -> Future { return future { _ in usleep(arc4random_uniform(100)) - return .Success(Box(val)) + return Result(value: val) } } } diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index bddbc3e..7040676 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -37,13 +37,13 @@ class ResultTests: XCTestCase { } func testSuccess() { - let result = Result.Success(Box(3)) + let result = Result.success(3) XCTAssert(result.isSuccess) XCTAssertFalse(result.isFailure) XCTAssertEqual(result.value!, 3) XCTAssertNil(result.error) - let result1 = Result(4) + let result1 = Result(value: 4) XCTAssert(result1.isSuccess) XCTAssertFalse(result1.isFailure) XCTAssertEqual(result1.value!, 4) @@ -52,7 +52,7 @@ class ResultTests: XCTestCase { func testFailure() { let error = NSError() - let result = Result.Failure(error) + let result = Result(error: error) XCTAssert(result.isFailure) XCTAssertFalse(result.isSuccess) XCTAssertEqual(result.error!, error) @@ -60,7 +60,7 @@ class ResultTests: XCTestCase { } func testMapSuccess() { - let r = Result.Success(Box(2)).map { i -> Bool in + let r = Result(value: 2).map { i -> Bool in XCTAssertEqual(i, 2) return i % 2 == 0 } @@ -70,7 +70,7 @@ class ResultTests: XCTestCase { } func testMapFailure() { - let r = Result.Failure(NSError(domain: "error", code: 1, userInfo: nil)).map { i -> Int in + let r = Result(error: NSError(domain: "error", code: 1, userInfo: nil)).map { i -> Int in XCTAssert(false, "map should not get called if the result failed") return i * 2 } @@ -170,8 +170,8 @@ class ResultTests: XCTestCase { func divide(a: Int, b: Int) -> Result { if (b == 0) { - return .Failure(NSError(domain: "DivisionByZeroError", code: 0, userInfo: nil)) + return Result(error: NSError(domain: "DivisionByZeroError", code: 0, userInfo: nil)) } - return Result(a / b) + return Result(value: a / b) } From 785b85e09504b04e3b1764d8ff8403ad51f0b830 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:55:35 +0200 Subject: [PATCH 06/61] Unboxing errors everywhere --- BrightFutures/Future.swift | 14 +++++++------- BrightFutures/Promise.swift | 2 +- BrightFuturesTests/BrightFuturesTests.swift | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index c180756..037edd7 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -59,8 +59,8 @@ public func future(context c: ExecutionContext, task: () -> Result switch result { case .Success(let boxedValue): promise.success(boxedValue.value) - case .Failure(let error): - promise.failure(error) + case .Failure(let boxedError): + promise.failure(boxedError.value) } } @@ -167,7 +167,7 @@ internal extension Future { case .Success(let val): return self.trySuccess(val.value) case .Failure(let err): - return self.tryFailure(err) + return self.tryFailure(err.value) } } @@ -402,7 +402,7 @@ public extension Future { self.onComplete(context: c) { result in switch result { case .Failure(let err): - callback(err) + callback(err.value) default: break } @@ -455,7 +455,7 @@ public extension Future { p.success(f(v.value)) break; case .Failure(let e): - p.failure(e) + p.failure(e.value) break; } }) @@ -497,7 +497,7 @@ public extension Future { self.onComplete(context: c) { result -> () in switch result { case .Failure(let err): - p.completeWith(task(err)) + p.completeWith(task(err.value)) case .Success(let val): p.completeWith(self) } @@ -591,7 +591,7 @@ public func flatten(future: Future>) -> Future { case .Success(let boxedFuture): p.completeWith(boxedFuture.value) case .Failure(let e): - p.failure(e) + p.failure(e.value) } } diff --git a/BrightFutures/Promise.swift b/BrightFutures/Promise.swift index 8a246a5..86d22b8 100644 --- a/BrightFutures/Promise.swift +++ b/BrightFutures/Promise.swift @@ -46,7 +46,7 @@ public class Promise { case .Success(let val): self.success(val.value) case .Failure(let err): - self.failure(err) + self.failure(err.value) } } } diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index 72f870a..873d28f 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -78,8 +78,8 @@ extension BrightFuturesTests { switch result { case .Success(let val): XCTAssert(false) - case .Failure(let err): - XCTAssertEqual(err, error) + case .Failure(let boxedErr): + XCTAssertEqual(boxedErr.value, error) } completeExpectation.fulfill() } From cd2c31fa2a928d69c4f0103b3463d687f96a8839 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:56:25 +0200 Subject: [PATCH 07/61] rewriting flatten and sequence --- BrightFutures/Result.swift | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/BrightFutures/Result.swift b/BrightFutures/Result.swift index 809d2fe..5605594 100644 --- a/BrightFutures/Result.swift +++ b/BrightFutures/Result.swift @@ -46,39 +46,29 @@ extension Result { /// Returns a .Failure with the error from the outer or inner result if either of the two failed /// or a .Success with the success value from the inner Result -public func flatten(result: Result>) -> Result { - switch result { - case .Success(let boxedValue): - return boxedValue.value - case .Failure(let err): - return Result.Failure(err) - } +public func flatten(result: Result,NSError>) -> Result { + return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) }) } /// Returns the inner future if the outer result succeeded or a failed future /// with the error from the outer result otherwise -public func flatten(result: Result>) -> Future { - switch result { - case .Success(let boxedFuture): - return boxedFuture.value - case .Failure(let err): - return Future.failed(err) - } +public func flatten(result: Result,NSError>) -> Future { + return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) }) } /// Turns a sequence of `Result`'s into a Result with an array of T's (`Result<[T]>`) /// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the /// error from the first failed result from the sequence. -public func sequence>(seq: S) -> Result<[T]> { - return reduce(seq, Result([])) { (res, elem) -> Result<[T]> in +public func sequence>(seq: S) -> Result<[T],NSError> { + return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T],NSError> in switch res { case .Success(let boxedResultSequence): switch elem { case .Success(let boxedElemValue): let newSeq = boxedResultSequence.value + [boxedElemValue.value] - return Result<[T]>.Success(Box(newSeq)) - case .Failure(let elemError): - return Result<[T]>.Failure(elemError) + return Result<[T],NSError>(value: newSeq) + case .Failure(let boxedElemError): + return Result<[T],NSError>(error: boxedElemError.value) } case .Failure(let err): return res From 41a84593b43ef0304033134ed7383c7175d33d49 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:56:54 +0200 Subject: [PATCH 08/61] Removing recover As it is covered by ?? --- BrightFutures/Result.swift | 27 +-------------------------- BrightFuturesTests/ResultTests.swift | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/BrightFutures/Result.swift b/BrightFutures/Result.swift index 5605594..17aae6b 100644 --- a/BrightFutures/Result.swift +++ b/BrightFutures/Result.swift @@ -27,21 +27,6 @@ extension Result { /// Enables the chaining of two failable operations where the second operation is asynchronous and /// represented by a future. See `flatMap(@noescape f: T -> Result) -> Result` - /// Returns `self.value` if this result is a .Success, or the given value otherwise - public func recover(value: T) -> T { - return self.value ?? value - } - - /// Returns this result if it is a .Success, or the given result otherwise - public func recoverWith(result: Result) -> Result { - switch self { - case .Success(_): - return self - case .Failure(_): - return result - } - } - } /// Returns a .Failure with the error from the outer or inner result if either of the two failed @@ -74,14 +59,4 @@ public func sequence(lhs: Result, @autoclosure rhs: () -> T) -> T { - return lhs.recover(rhs()) -} - -/// The `.Failure` coalescing operator (Short-hand for `lhs.recoverWith(rhs()`) -public func ?? (lhs: Result, @autoclosure rhs: () -> Result) -> Result { - return lhs.recoverWith(rhs()) -} +} \ No newline at end of file diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index 7040676..fc1998d 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -152,20 +152,20 @@ class ResultTests: XCTestCase { XCTAssert(r.isFailure) XCTAssertEqual(r.error!.domain, "DivisionByZeroError") } - - func testRecoverNeeded() { - let r = divide(10, 0).recover(2) - XCTAssertEqual(r, 2) - - XCTAssertEqual(divide(10, 0) ?? 2, 2) - } - - func testRecoverUnneeded() { - let r = divide(10, 3).recover(10) - XCTAssertEqual(r, 3) - - XCTAssertEqual(divide(10, 3) ?? 10, 3) - } +// +// func testRecoverNeeded() { +// let r = divide(10, 0).recover(2) +// XCTAssertEqual(r, 2) +// +// XCTAssertEqual(divide(10, 0) ?? 2, 2) +// } +// +// func testRecoverUnneeded() { +// let r = divide(10, 3).recover(10) +// XCTAssertEqual(r, 3) +// +// XCTAssertEqual(divide(10, 3) ?? 10, 3) +// } } func divide(a: Int, b: Int) -> Result { From 198995823365f4b748c5b4a74581da4b4079394b Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:57:48 +0200 Subject: [PATCH 09/61] Commenting out flatMap for futures --- BrightFutures/Result.swift | 3 ++ BrightFuturesTests/ResultTests.swift | 72 ++++++++++++++-------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/BrightFutures/Result.swift b/BrightFutures/Result.swift index 17aae6b..3f77557 100644 --- a/BrightFutures/Result.swift +++ b/BrightFutures/Result.swift @@ -27,6 +27,9 @@ extension Result { /// Enables the chaining of two failable operations where the second operation is asynchronous and /// represented by a future. See `flatMap(@noescape f: T -> Result) -> Result` +// public func flatMap(@noescape f: T -> Future) -> Future { +// return flatten(self.map(f)) +// } } /// Returns a .Failure with the error from the outer or inner result if either of the two failed diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index fc1998d..0fafffc 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -96,42 +96,42 @@ class ResultTests: XCTestCase { XCTAssert(r.isFailure) XCTAssertEqual(r.error!.domain, "DivisionByZeroError") } - - func testFlatMapFutureSuccess() { - let f = divide(100, 10).flatMap { i -> Future in - return future { - fibonacci(i) - } - } - - let e = self.expectation() - - f.onSuccess { i in - XCTAssertEqual(i, 55) - e.fulfill() - } - - self.waitForExpectationsWithTimeout(2, handler: nil) - } - - func testFlatMapFutureFailure() { - let f = divide(100, 0).flatMap { i -> Future in - XCTAssert(false, "flatMap should not get called if the result failed") - return future { - fibonacci(i) - } - } - - let e = self.expectation() - - f.onFailure { err in - XCTAssertEqual(err.domain, "DivisionByZeroError") - e.fulfill() - } - - self.waitForExpectationsWithTimeout(2, handler: nil) - } - +// +// func testFlatMapFutureSuccess() { +// let f = divide(100, 10).flatMap { i -> Future in +// return future { +// fibonacci(i) +// } +// } +// +// let e = self.expectation() +// +// f.onSuccess { i in +// XCTAssertEqual(i, 55) +// e.fulfill() +// } +// +// self.waitForExpectationsWithTimeout(2, handler: nil) +// } +// +// func testFlatMapFutureFailure() { +// let f = divide(100, 0).flatMap { i -> Future in +// XCTAssert(false, "flatMap should not get called if the result failed") +// return future { +// fibonacci(i) +// } +// } +// +// let e = self.expectation() +// +// f.onFailure { err in +// XCTAssertEqual(err.domain, "DivisionByZeroError") +// e.fulfill() +// } +// +// self.waitForExpectationsWithTimeout(2, handler: nil) +// } +// func testSequenceSuccess() { let results: [Result] = (1...10).map { i in return divide(123, i) From 252761d00f049601ab320e7251667ba993d757c2 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:58:06 +0200 Subject: [PATCH 10/61] Added helper extension for Result to ResultTest --- BrightFuturesTests/ResultTests.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index 0fafffc..b2be12b 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -24,6 +24,15 @@ import XCTest import Result import BrightFutures +extension Result { + var isSuccess: Bool { + return self.analysis(ifSuccess: { _ in return true }, ifFailure: { _ in return false }) + } + var isFailure: Bool { + return !isSuccess + } +} + class ResultTests: XCTestCase { override func setUp() { From e6faea9f704dc260ffa6e06bf15550822540be97 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 20:58:36 +0200 Subject: [PATCH 11/61] Rewriting isSuccess and isFailure with Result.analysis --- BrightFutures/Future.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 037edd7..9bbbb59 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -238,14 +238,14 @@ public extension Future { /// `true` if the future completed with success, or `false` otherwise public var isSuccess: Bool { get { - return self.result?.isSuccess ?? false + return result?.analysis(ifSuccess: { _ in return true }, ifFailure: { _ in return false }) ?? false } } /// `true` if the future failed, or `false` otherwise public var isFailure: Bool { get { - return self.result?.isFailure ?? false + return !isSuccess } } From ab81d1359143e051bdd776012a5bf6f5c7404b5c Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Tue, 12 May 2015 21:31:39 +0200 Subject: [PATCH 12/61] Replaced a lot of switch statements with Result.analysis --- BrightFutures/Future.swift | 52 ++++++++++--------------------------- BrightFutures/Promise.swift | 7 +---- 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 9bbbb59..ac413ad 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -56,12 +56,7 @@ public func future(context c: ExecutionContext, task: () -> Result c { let result = task() - switch result { - case .Success(let boxedValue): - promise.success(boxedValue.value) - case .Failure(let boxedError): - promise.failure(boxedError.value) - } + result.analysis(ifSuccess: promise.success, ifFailure: promise.failure) } return promise.future @@ -384,12 +379,7 @@ public extension Future { /// Returns self public func onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), callback: SuccessCallback) -> Future { self.onComplete(context: c) { result in - switch result { - case .Success(let val): - callback(val.value) - default: - break - } + result.analysis(ifSuccess: callback, ifFailure: { _ in }) } return self @@ -400,12 +390,7 @@ public extension Future { /// Returns self public func onFailure(context c: ExecutionContext = executionContextForCurrentContext(), callback: FailureCallback) -> Future { self.onComplete(context: c) { result in - switch result { - case .Failure(let err): - callback(err.value) - default: - break - } + result.analysis(ifSuccess: { _ in }, ifFailure: callback) } return self } @@ -450,14 +435,9 @@ public extension Future { let p = Promise() self.onComplete(context: c, callback: { result in - switch result { - case .Success(let v): - p.success(f(v.value)) - break; - case .Failure(let e): - p.failure(e.value) - break; - } + result.analysis( + ifSuccess: { p.success(f($0)) }, + ifFailure: p.failure ) }) return p.future @@ -494,13 +474,10 @@ public extension Future { public func recoverWith(context c: ExecutionContext = executionContextForCurrentContext(), task: (NSError) -> Future) -> Future { let p = Promise() - self.onComplete(context: c) { result -> () in - switch result { - case .Failure(let err): - p.completeWith(task(err.value)) - case .Success(let val): - p.completeWith(self) - } + self.onComplete(context: c) { result in + result.analysis( + ifSuccess: { value in p.completeWith(self) }, + ifFailure: { p.completeWith(task($0)) }) } return p.future; @@ -587,12 +564,9 @@ public func flatten(future: Future>) -> Future { let p = Promise() future.onComplete { result in - switch result { - case .Success(let boxedFuture): - p.completeWith(boxedFuture.value) - case .Failure(let e): - p.failure(e.value) - } + result.analysis( + ifSuccess: { p.completeWith($0) }, + ifFailure: { p.failure($0) }) } return p.future diff --git a/BrightFutures/Promise.swift b/BrightFutures/Promise.swift index 86d22b8..cfac237 100644 --- a/BrightFutures/Promise.swift +++ b/BrightFutures/Promise.swift @@ -42,12 +42,7 @@ public class Promise { /// Completes the promise's future with the given future public func completeWith(future: Future) { future.onComplete { result in - switch result { - case .Success(let val): - self.success(val.value) - case .Failure(let err): - self.failure(err.value) - } + result.analysis(ifSuccess: self.success, ifFailure: self.failure) } } From 4f2a7ffd59ee7aae14414bd9847802694592c681 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Wed, 13 May 2015 09:51:51 +0200 Subject: [PATCH 13/61] Moved free functions to FutureUtils made `flatMap() -> Future` a free function instead of a instance of `Result` --- BrightFutures.xcodeproj/project.pbxproj | 7 --- BrightFutures/FutureUtils.swift | 43 ++++++++++++++++ BrightFutures/Result.swift | 65 ------------------------- 3 files changed, 43 insertions(+), 72 deletions(-) delete mode 100644 BrightFutures/Result.swift diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 7f60ade..a2c649e 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -20,12 +20,10 @@ E907D1DE1A6AE4A000AB8075 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */; }; E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9319EA31A397D2C00A0604A /* QueueTests.swift */; }; E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E979E5F31975B1AA007FE914 /* FutureUtils.swift */; }; - E9BD4A4919F3E43100BBD966 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9BD4A4819F3E43100BBD966 /* Result.swift */; }; E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; }; E9D45B191AF00668000F6CA7 /* InvalidationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D923AB1A6CE29A00CADD9F /* InvalidationToken.swift */; }; E9D45B1A1AF00668000F6CA7 /* ExecutionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0831194470190083F7F2 /* ExecutionContext.swift */; }; E9D45B1B1AF00668000F6CA7 /* Future.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0832194470190083F7F2 /* Future.swift */; }; - E9D45B1C1AF00668000F6CA7 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9BD4A4819F3E43100BBD966 /* Result.swift */; }; E9D45B1D1AF00668000F6CA7 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0833194470190083F7F2 /* Promise.swift */; }; E9D45B1E1AF00668000F6CA7 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0834194470190083F7F2 /* Queue.swift */; }; E9D45B1F1AF00670000F6CA7 /* FutureUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E979E5F31975B1AA007FE914 /* FutureUtils.swift */; }; @@ -70,7 +68,6 @@ E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Semaphore.swift; sourceTree = ""; }; E9319EA31A397D2C00A0604A /* QueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueTests.swift; sourceTree = ""; }; E979E5F31975B1AA007FE914 /* FutureUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FutureUtils.swift; sourceTree = ""; }; - E9BD4A4819F3E43100BBD966 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; E9D45B001AF00659000F6CA7 /* BrightFutures.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BrightFutures.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E9D45B0A1AF00659000F6CA7 /* BrightFutures-MacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "BrightFutures-MacTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E9D923AB1A6CE29A00CADD9F /* InvalidationToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvalidationToken.swift; sourceTree = ""; }; @@ -102,7 +99,6 @@ files = ( E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */, B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */, - B31A24321B026D4B0016AE7A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -182,7 +178,6 @@ E9D923AB1A6CE29A00CADD9F /* InvalidationToken.swift */, E9DF0831194470190083F7F2 /* ExecutionContext.swift */, E9DF0832194470190083F7F2 /* Future.swift */, - E9BD4A4819F3E43100BBD966 /* Result.swift */, E9DF0833194470190083F7F2 /* Promise.swift */, E9DF0834194470190083F7F2 /* Queue.swift */, E9DF0818194470060083F7F2 /* Supporting Files */, @@ -436,7 +431,6 @@ E9D45B1A1AF00668000F6CA7 /* ExecutionContext.swift in Sources */, E9D45B1F1AF00670000F6CA7 /* FutureUtils.swift in Sources */, E9D45B1D1AF00668000F6CA7 /* Promise.swift in Sources */, - E9D45B1C1AF00668000F6CA7 /* Result.swift in Sources */, E9D45B201AF00670000F6CA7 /* Semaphore.swift in Sources */, E9D45B191AF00668000F6CA7 /* InvalidationToken.swift in Sources */, ); @@ -457,7 +451,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E9BD4A4919F3E43100BBD966 /* Result.swift in Sources */, E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */, E9DF0836194470190083F7F2 /* Future.swift in Sources */, E9DF0835194470190083F7F2 /* ExecutionContext.swift in Sources */, diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index 129ade2..9d039ac 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -105,3 +105,46 @@ public func firstCompletedOf>`. +/// The implementation of this function uses `map`, but then flattens the result before returning it. +public func flatMap(result: Result, @noescape f: T -> Future) -> Future { + return flatten(result.map(f)) +} + +/// Returns a .Failure with the error from the outer or inner result if either of the two failed +/// or a .Success with the success value from the inner Result +public func flatten(result: Result,NSError>) -> Result { + return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) }) +} + +/// Returns the inner future if the outer result succeeded or a failed future +/// with the error from the outer result otherwise +public func flatten(result: Result,NSError>) -> Future { + return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) }) +} + +/// Turns a sequence of `Result`'s into a Result with an array of T's (`Result<[T]>`) +/// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the +/// error from the first failed result from the sequence. +public func sequence>(seq: S) -> Result<[T],NSError> { + return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T],NSError> in + switch res { + case .Success(let boxedResultSequence): + switch elem { + case .Success(let boxedElemValue): + let newSeq = boxedResultSequence.value + [boxedElemValue.value] + return Result<[T],NSError>(value: newSeq) + case .Failure(let boxedElemError): + return Result<[T],NSError>(error: boxedElemError.value) + } + case .Failure(let err): + return res + } + } +} diff --git a/BrightFutures/Result.swift b/BrightFutures/Result.swift deleted file mode 100644 index 3f77557..0000000 --- a/BrightFutures/Result.swift +++ /dev/null @@ -1,65 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2014 Thomas Visser -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Result - -extension Result { - - /// Enables the chaining of two failable operations where the second operation is asynchronous and - /// represented by a future. See `flatMap(@noescape f: T -> Result) -> Result` -// public func flatMap(@noescape f: T -> Future) -> Future { -// return flatten(self.map(f)) -// } -} - -/// Returns a .Failure with the error from the outer or inner result if either of the two failed -/// or a .Success with the success value from the inner Result -public func flatten(result: Result,NSError>) -> Result { - return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) }) -} - -/// Returns the inner future if the outer result succeeded or a failed future -/// with the error from the outer result otherwise -public func flatten(result: Result,NSError>) -> Future { - return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) }) -} - -/// Turns a sequence of `Result`'s into a Result with an array of T's (`Result<[T]>`) -/// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the -/// error from the first failed result from the sequence. -public func sequence>(seq: S) -> Result<[T],NSError> { - return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T],NSError> in - switch res { - case .Success(let boxedResultSequence): - switch elem { - case .Success(let boxedElemValue): - let newSeq = boxedResultSequence.value + [boxedElemValue.value] - return Result<[T],NSError>(value: newSeq) - case .Failure(let boxedElemError): - return Result<[T],NSError>(error: boxedElemError.value) - } - case .Failure(let err): - return res - } - } -} \ No newline at end of file From 1f7c8eb0b417f801a39ede64e8045d54182164fe Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Wed, 13 May 2015 09:54:14 +0200 Subject: [PATCH 14/61] Updated flatMapFuture tests --- BrightFuturesTests/ResultTests.swift | 72 ++++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index b2be12b..5bf5553 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -105,42 +105,42 @@ class ResultTests: XCTestCase { XCTAssert(r.isFailure) XCTAssertEqual(r.error!.domain, "DivisionByZeroError") } -// -// func testFlatMapFutureSuccess() { -// let f = divide(100, 10).flatMap { i -> Future in -// return future { -// fibonacci(i) -// } -// } -// -// let e = self.expectation() -// -// f.onSuccess { i in -// XCTAssertEqual(i, 55) -// e.fulfill() -// } -// -// self.waitForExpectationsWithTimeout(2, handler: nil) -// } -// -// func testFlatMapFutureFailure() { -// let f = divide(100, 0).flatMap { i -> Future in -// XCTAssert(false, "flatMap should not get called if the result failed") -// return future { -// fibonacci(i) -// } -// } -// -// let e = self.expectation() -// -// f.onFailure { err in -// XCTAssertEqual(err.domain, "DivisionByZeroError") -// e.fulfill() -// } -// -// self.waitForExpectationsWithTimeout(2, handler: nil) -// } -// + + func testFlatMapFutureSuccess() { + let f = flatMap(divide(100, 10)) { i -> Future in + return future { + fibonacci(i) + } + } + + let e = self.expectation() + + f.onSuccess { i in + XCTAssertEqual(i, 55) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func testFlatMapFutureFailure() { + let f = flatMap(divide(100, 0)) { i -> Future in + XCTAssert(false, "flatMap should not get called if the result failed") + return future { + fibonacci(i) + } + } + + let e = self.expectation() + + f.onFailure { err in + XCTAssertEqual(err.domain, "DivisionByZeroError") + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + func testSequenceSuccess() { let results: [Result] = (1...10).map { i in return divide(123, i) From 901ce01567e9c16b338d9d0409ce74fe021b6987 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Wed, 13 May 2015 10:42:34 +0200 Subject: [PATCH 15/61] Re-enabled recover tests --- BrightFuturesTests/ResultTests.swift | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index 5bf5553..70efd5f 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -161,20 +161,20 @@ class ResultTests: XCTestCase { XCTAssert(r.isFailure) XCTAssertEqual(r.error!.domain, "DivisionByZeroError") } -// -// func testRecoverNeeded() { -// let r = divide(10, 0).recover(2) -// XCTAssertEqual(r, 2) -// -// XCTAssertEqual(divide(10, 0) ?? 2, 2) -// } -// -// func testRecoverUnneeded() { -// let r = divide(10, 3).recover(10) -// XCTAssertEqual(r, 3) -// -// XCTAssertEqual(divide(10, 3) ?? 10, 3) -// } + + func testRecoverNeeded() { + let r = divide(10, 0).recover(2) + XCTAssertEqual(r, 2) + + XCTAssertEqual(divide(10, 0) ?? 2, 2) + } + + func testRecoverUnneeded() { + let r = divide(10, 3).recover(10) + XCTAssertEqual(r, 3) + + XCTAssertEqual(divide(10, 3) ?? 10, 3) + } } func divide(a: Int, b: Int) -> Result { From 0ee93b974e6cb29174f060a3a1c573ff9cd7d5a6 Mon Sep 17 00:00:00 2001 From: Niels van Hoorn Date: Wed, 13 May 2015 17:13:18 +0200 Subject: [PATCH 16/61] Update Result to 0.4.2 --- Cartfile | 2 +- Cartfile.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index 9e4df54..58c59f8 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "antitypical/Result" ~> 0.4.1 +github "antitypical/Result" ~> 0.4.2 diff --git a/Cartfile.resolved b/Cartfile.resolved index 1586bce..cfc8cef 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "robrix/Box" "1.2.2" -github "antitypical/Result" "0.4.1" +github "antitypical/Result" "0.4.2" From eb50115134c51ef2e4f2922a9746b6446a5e6a1a Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 14 May 2015 18:36:21 +0200 Subject: [PATCH 17/61] added Carthage dependencies as submodules --- .gitmodules | 3 +++ Carthage/Checkouts/Result | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 Carthage/Checkouts/Result diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..00f2c4d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Carthage/Checkouts/Result"] + path = Carthage/Checkouts/Result + url = https://github.com/antitypical/Result.git diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result new file mode 160000 index 0000000..bd4a493 --- /dev/null +++ b/Carthage/Checkouts/Result @@ -0,0 +1 @@ +Subproject commit bd4a493b262b70bfcf140cb22661f048a8838b7b From 599011f4fbfd6f9f5b88cf7142d93856f1cb9d2c Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 14 May 2015 18:46:52 +0200 Subject: [PATCH 18/61] Added Result to dependencies. Spec currently does not lint because Result 0.4.2 has not yet been pushed to CocoaPods --- BrightFutures.podspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BrightFutures.podspec b/BrightFutures.podspec index c450018..ec37f6b 100644 --- a/BrightFutures.podspec +++ b/BrightFutures.podspec @@ -13,5 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'BrightFutures/*.swift' + s.dependency 'Result', '~> 0.4.2' + s.requires_arc = true end \ No newline at end of file From 85e0270c83cdf002bf6614c080b2027fc7530e50 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 14 May 2015 18:47:29 +0200 Subject: [PATCH 19/61] corrected build phases. Dependencies do not have to be copied into the framework (just linked), but do need to be copied for the test targets --- BrightFutures.xcodeproj/project.pbxproj | 73 ++++++------------------- 1 file changed, 16 insertions(+), 57 deletions(-) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 0a77db0..9834312 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -7,15 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - B31A24241B0268F80016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; - B31A24251B0268F80016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; B31A24291B02691A0016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; - B31A24341B026D500016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; - B31A24351B026D500016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; - B31A243A1B0279120016AE7A /* Result.framework in Resources */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; - B31A243B1B0279150016AE7A /* Box.framework in Resources */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */; }; E907D1DE1A6AE4A000AB8075 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */; }; E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9319EA31A397D2C00A0604A /* QueueTests.swift */; }; @@ -42,6 +36,14 @@ E9DF0838194470190083F7F2 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0834194470190083F7F2 /* Queue.swift */; }; E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9DF0815194470060083F7F2 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E9F496E61B05086100F82839 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; + E9F496E71B05086300F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; + E9F496E81B05088000F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E9F496E91B05088200F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E9F496EA1B05088F00F82839 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; + E9F496EB1B05089100F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; + E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -69,6 +71,8 @@ dstSubfolderSpec = 10; files = ( E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */, + E9F496E81B05088000F82839 /* Result.framework in CopyFiles */, + E9F496E91B05088200F82839 /* Box.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -79,6 +83,8 @@ dstSubfolderSpec = 10; files = ( E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */, + E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */, + E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -131,8 +137,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B31A24241B0268F80016AE7A /* Result.framework in Frameworks */, - B31A24251B0268F80016AE7A /* Box.framework in Frameworks */, + E9F496E61B05086100F82839 /* Result.framework in Frameworks */, + E9F496E71B05086300F82839 /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -141,8 +147,8 @@ buildActionMask = 2147483647; files = ( E9DF0821194470060083F7F2 /* BrightFutures.framework in Frameworks */, - B31A24351B026D500016AE7A /* Box.framework in Frameworks */, - B31A24341B026D500016AE7A /* Result.framework in Frameworks */, + E9F496EB1B05089100F82839 /* Box.framework in Frameworks */, + E9F496EA1B05088F00F82839 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -284,7 +290,6 @@ buildPhases = ( E9D45B061AF00659000F6CA7 /* Sources */, E9D45B071AF00659000F6CA7 /* Frameworks */, - E9D45B081AF00659000F6CA7 /* Resources */, E9F496E41B03980600F82839 /* CopyFiles */, ); buildRules = ( @@ -305,7 +310,6 @@ E9DF0811194470060083F7F2 /* Frameworks */, E9DF0812194470060083F7F2 /* Headers */, E9DF0813194470060083F7F2 /* Resources */, - B31A242E1B0269790016AE7A /* Carthage */, ); buildRules = ( ); @@ -323,7 +327,6 @@ E9DF081C194470060083F7F2 /* Sources */, E9DF081D194470060083F7F2 /* Frameworks */, E9DF081E194470060083F7F2 /* Resources */, - B31A24381B02785F0016AE7A /* Carthage */, E9F496E21B0397D200F82839 /* CopyFiles */, ); buildRules = ( @@ -388,15 +391,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E9D45B081AF00659000F6CA7 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B31A243B1B0279150016AE7A /* Box.framework in Resources */, - B31A243A1B0279120016AE7A /* Result.framework in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; E9DF0813194470060083F7F2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -413,41 +407,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - B31A242E1B0269790016AE7A /* Carthage */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/Box.framework", - "$(SRCROOT)/Carthage/Build/iOS/Result.framework", - ); - name = Carthage; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks"; - }; - B31A24381B02785F0016AE7A /* Carthage */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/Box.framework", - "$(SRCROOT)/Carthage/Build/iOS/Result.framework", - ); - name = Carthage; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ E9D45AFB1AF00659000F6CA7 /* Sources */ = { isa = PBXSourcesBuildPhase; From 094b779a54f6640d3ee0cf433c54d9fcecdd2631 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 14 May 2015 20:33:26 +0200 Subject: [PATCH 20/61] attempt for adding carthage bootstrap to CircleCI configuration --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/circle.yml b/circle.yml index 121373d..909d08f 100644 --- a/circle.yml +++ b/circle.yml @@ -1,8 +1,12 @@ machine: xcode: version: "6.3.1" +dependencies: + pre: + - brew install carthage test: override: + - carthage bootstrap - xctool -reporter pretty -reporter junit:$CIRCLE_TEST_REPORTS/xcode/results-ios.xml From ad6d2c081ce3a3c7c095eea4986d5ce0515a9480 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 14 May 2015 20:58:37 +0200 Subject: [PATCH 21/61] Trying to fix CircleCI build by just building for mac --- circle.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/circle.yml b/circle.yml index 909d08f..6e7c40c 100644 --- a/circle.yml +++ b/circle.yml @@ -6,19 +6,7 @@ dependencies: - brew install carthage test: override: - - carthage bootstrap - - xctool - -reporter pretty - -reporter junit:$CIRCLE_TEST_REPORTS/xcode/results-ios.xml - -reporter plain:$CIRCLE_ARTIFACTS/xctool-ios.log - CODE_SIGNING_REQUIRED=NO - CODE_SIGN_IDENTITY= - PROVISIONING_PROFILE= - -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' - -sdk iphonesimulator - -project BrightFutures.xcodeproj - -scheme BrightFutures-iOS - build build-tests run-tests + - carthage bootstrap --platform Mac - xctool -reporter pretty -reporter junit:$CIRCLE_TEST_REPORTS/xcode/results-mac.xml From a3b0c1baaa84834f13f8a79f81add4954251c618 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 15 May 2015 17:21:42 +0200 Subject: [PATCH 22/61] refactoring to remove dependency on NSError, tests do not compile yet --- BrightFutures.xcodeproj/project.pbxproj | 6 + BrightFutures/Error.swift | 70 +++++++++ BrightFutures/Future.swift | 193 +++++++++++------------- BrightFutures/FutureUtils.swift | 34 +++-- BrightFutures/InvalidationToken.swift | 8 +- BrightFutures/Promise.swift | 24 +-- BrightFutures/Queue.swift | 8 +- 7 files changed, 204 insertions(+), 139 deletions(-) create mode 100644 BrightFutures/Error.swift diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 9834312..349e020 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ E9F496EB1B05089100F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E9F496EF1B052F1400F82839 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Error.swift */; }; + E9F496F01B052F1400F82839 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Error.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -112,6 +114,7 @@ E9DF0832194470190083F7F2 /* Future.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Future.swift; sourceTree = ""; }; E9DF0833194470190083F7F2 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = ""; }; E9DF0834194470190083F7F2 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + E9F496EE1B052F1400F82839 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -214,6 +217,7 @@ E9DF0818194470060083F7F2 /* Supporting Files */, E979E5F31975B1AA007FE914 /* FutureUtils.swift */, E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */, + E9F496EE1B052F1400F82839 /* Error.swift */, ); path = BrightFutures; sourceTree = ""; @@ -412,6 +416,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E9F496F01B052F1400F82839 /* Error.swift in Sources */, E9D45B1B1AF00668000F6CA7 /* Future.swift in Sources */, E9D45B1E1AF00668000F6CA7 /* Queue.swift in Sources */, E9D45B1A1AF00668000F6CA7 /* ExecutionContext.swift in Sources */, @@ -437,6 +442,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E9F496EF1B052F1400F82839 /* Error.swift in Sources */, E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */, E9DF0836194470190083F7F2 /* Future.swift in Sources */, E9DF0835194470190083F7F2 /* ExecutionContext.swift in Sources */, diff --git a/BrightFutures/Error.swift b/BrightFutures/Error.swift new file mode 100644 index 0000000..99bf0f9 --- /dev/null +++ b/BrightFutures/Error.swift @@ -0,0 +1,70 @@ +// +// Error.swift +// BrightFutures +// +// Created by Thomas Visser on 14/05/15. +// Copyright (c) 2015 Thomas Visser. All rights reserved. +// + +import Foundation +import Box + +public protocol ErrorType { + /// An NSError describing this error + var nsError: NSError { get } +} + +public enum NoError {} + +extension NoError: ErrorType { + public var nsError: NSError { + fatalError("Impossible to construct NoError") + } +} + +extension NSError: ErrorType { + public var nsError: NSError { + return self + } +} + +public enum EitherError : ErrorType { + case Left(Box) + case Right(Box) + + public static func left(error: E1) -> EitherError { + return .Left(Box(error)) + } + + public static func right(error: E2) -> EitherError { + return .Right(Box(error)) + } + + public var nsError: NSError { + switch self { + case .Left(let boxedError): + return boxedError.value.nsError + case .Right(let boxedError): + return boxedError.value.nsError + } + } +} + +public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" + +/// An enum representing every possible error code for errors returned by BrightFutures +public enum BrightFuturesError: ErrorType { + + case NoSuchElement + case InvalidationTokenInvalidated + + public var nsError: NSError { + switch self { + case .NoSuchElement: + return NSError(domain: BrightFuturesErrorDomain, code: 0, userInfo: nil) + case .InvalidationTokenInvalidated: + return NSError(domain: BrightFuturesErrorDomain, code: 1, userInfo: nil) + } + } + +} \ No newline at end of file diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index ac413ad..ea3db69 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -24,35 +24,35 @@ import Foundation import Result /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(@autoclosure(escaping) task: () -> T) -> Future { +public func future(@autoclosure(escaping) task: () -> T) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(task: () -> T) -> Future { +public func future(task: () -> T) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on the given context and wraps the result of the task in a Future -public func future(context c: ExecutionContext, task: () -> T) -> Future { - return future(context: c, { () -> Result in +public func future(context c: ExecutionContext, task: () -> T) -> Future { + return future(context: c, { () -> Result in return Result(value: task()) }) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(@autoclosure(escaping) task: () -> Result) -> Future { +public func future(@autoclosure(escaping) task: () -> Result) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(task: () -> Result) -> Future { +public func future(task: () -> Result) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on the given context and wraps the result of the task in a Future -public func future(context c: ExecutionContext, task: () -> Result) -> Future { - let promise = Promise(); +public func future(context c: ExecutionContext, task: () -> Result) -> Future { + let promise = Promise(); c { let result = task() @@ -69,37 +69,6 @@ func executionContextForCurrentContext() -> ExecutionContext { return toContext(NSThread.isMainThread() ? Queue.main : Queue.global) } -/// The error domain used for all errors returned by BrightFutures -public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" - -/// An enum representing every possible error code for errors returned by BrightFutures -public enum ErrorCode: Int { - case NoSuchElement - case InvalidationTokenInvalidated - - var errorDescription: String { - switch self { - case .NoSuchElement: - return "No such element" - case .InvalidationTokenInvalidated: - return "Invalidation token invalidated" - } - } -} - -/// Creates an NSError instance with the given code, failure reason and the -internal func errorFromCode(code: ErrorCode, failureReason: String? = nil) -> NSError { - var userInfo = [ - NSLocalizedDescriptionKey : code.errorDescription - ] - - if let reason = failureReason { - userInfo[NSLocalizedFailureReasonErrorKey] = reason - } - - return NSError(domain: BrightFuturesErrorDomain, code: code.rawValue, userInfo: userInfo) -} - /// A Future represents the outcome of an asynchronous operation /// The outcome will be represented as an instance of the `Result` enum and will be stored /// in the `result` property. As long as the operation is not yet completed, `result` will be nil. @@ -108,15 +77,15 @@ internal func errorFromCode(code: ErrorCode, failureReason: String? = nil) -> NS /// subsequent actions (e.g. map, flatMap, recover, andThen, etc.). /// /// For more info, see the project README.md -public class Future { +public class Future { - typealias CallbackInternal = (future: Future) -> () - typealias CompletionCallback = (result: Result) -> () - typealias SuccessCallback = (T) -> () - public typealias FailureCallback = (NSError) -> () + typealias CallbackInternal = (future: Future) -> () + typealias CompletionCallback = (result: Result) -> () + typealias SuccessCallback = T -> () + public typealias FailureCallback = E -> () /// The result of the operation this Future represents or `nil` if it is not yet completed - public internal(set) var result: Result? = nil + public internal(set) var result: Result? = nil /// This queue is used for all callback related administrative tasks /// to prevent that a callback is added to a completed future and never @@ -149,7 +118,7 @@ internal extension Future { /// Completes the future with the given result /// If the future is already completed, this function does nothing /// and an assert will be raised (if enabled) - func complete(result: Result) { + func complete(result: Result) { let succeeded = tryComplete(result) assert(succeeded) } @@ -157,7 +126,7 @@ internal extension Future { /// Tries to complete the future with the given result /// If the future is already completed, nothing happens and `false` is returned /// otherwise the future is completed and `true` is returned - func tryComplete(result: Result) -> Bool { + func tryComplete(result: Result) -> Bool { switch result { case .Success(let val): return self.trySuccess(val.value) @@ -192,7 +161,7 @@ internal extension Future { /// Completes the future with the given error /// If the future is already completed, this function does nothing /// and an assert will be raised (if enabled) - func failure(error: NSError) { + func failure(error: E) { let succeeded = self.tryFailure(error) assert(succeeded) } @@ -200,7 +169,7 @@ internal extension Future { /// Tries to complete the future with the given error /// If the future is already completed, nothing happens and `false` is returned /// otherwise the future is completed and `true` is returned - func tryFailure(error: NSError) -> Bool { + func tryFailure(error: E) -> Bool { return self.callbackAdministrationQueue.sync { if self.result != nil { return false; @@ -224,7 +193,7 @@ public extension Future { } /// Returns the error that the future failed with, or `nil` if the future succeeded or is still in progress - public var error: NSError? { + public var error: E? { get { return self.result?.error } @@ -256,24 +225,24 @@ public extension Future { public extension Future { /// Returns a new future that succeeded with the given value - public class func succeeded(value: T) -> Future { - let res = Future(); + public class func succeeded(value: T) -> Future { + let res = Future(); res.result = Result(value: value) return res } /// Returns a new future that failed with the given error - public class func failed(error: NSError) -> Future { - let res = Future(); - res.result = Result(error: error) + public class func failed(error: E) -> Future { + let res = Future(); + res.result = Result(error: error) return res } /// Returns a new future that completed with the given result - public class func completed(result: Result) -> Future { - let res = Future() + public class func completed(result: Result) -> Future { + let res = Future() res.result = result return res @@ -281,8 +250,8 @@ public extension Future { /// Returns a new future that will succeed with the given value after the given time interval /// The implementation of this function uses dispatch_after - public class func completeAfter(delay: NSTimeInterval, withValue value: T) -> Future { - let res = Future() + public class func completeAfter(delay: NSTimeInterval, withValue value: T) -> Future { + let res = Future() dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * NSTimeInterval(NSEC_PER_SEC))), Queue.global.underlyingQueue) { res.success(value) @@ -292,18 +261,18 @@ public extension Future { } /// Returns a new future that will never complete - public class func never() -> Future { - return Future() + public class func never() -> Future { + return Future() } /// Returns a new future with the new type. /// The value that this future succeeds with will be downcasted to the new type using `as!` and may fail - public func asType() -> Future { - return self.map { $0 as! U } + public func asType() -> Future { + return self.map { $0 as! U }.mapError { $0 as! E1} } /// Returns a new future that completes with this future, but returns Void on success - public func asVoid() -> Future { + public func asVoid() -> Future { return self.map { _ in return () } } } @@ -313,24 +282,24 @@ public extension Future { public extension Future { /// Blocks the current thread until the future is completed and then returns the result - public func forced() -> Result? { + public func forced() -> Result? { return self.forced(TimeInterval.Forever) } - /// See `forced(timeout: TimeInterval) -> Result?` - public func forced(timeout: NSTimeInterval) -> Result? { + /// See `forced(timeout: TimeInterval) -> Result?` + public func forced(timeout: NSTimeInterval) -> Result? { return self.forced(.In(timeout)) } /// Blocks the current thread until the future is completed, but no longer than the given timeout /// If the future did not complete before the timeout, `nil` is returned, otherwise the result of the future is returned - public func forced(timeout: TimeInterval) -> Result? { + public func forced(timeout: TimeInterval) -> Result? { if let certainResult = self.result { return certainResult } else { let sema = Semaphore(value: 0) - var res: Result? = nil + var res: Result? = nil self.onComplete(context: Queue.global.context) { res = $0 sema.signal() @@ -350,8 +319,8 @@ public extension Future { /// Adds the given closure as a callback for when the future completes. The closure is executed on the given context. /// If no context is given, the behavior is defined by the default threading model (see README.md) /// Returns self - public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future { - let wrappedCallback : Future -> () = { future in + public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future { + let wrappedCallback : Future -> () = { future in if let realRes = self.result { c { self.callbackExecutionSemaphore.execute { @@ -377,7 +346,7 @@ public extension Future { /// Adds the given closure as a callback for when the future succeeds. The closure is executed on the given context. /// If no context is given, the behavior is defined by the default threading model (see README.md) /// Returns self - public func onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), callback: SuccessCallback) -> Future { + public func onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), callback: SuccessCallback) -> Future { self.onComplete(context: c) { result in result.analysis(ifSuccess: callback, ifFailure: { _ in }) } @@ -388,7 +357,7 @@ public extension Future { /// Adds the given closure as a callback for when the future fails. The closure is executed on the given context. /// If no context is given, the behavior is defined by the default threading model (see README.md) /// Returns self - public func onFailure(context c: ExecutionContext = executionContextForCurrentContext(), callback: FailureCallback) -> Future { + public func onFailure(context c: ExecutionContext = executionContextForCurrentContext(), callback: FailureCallback) -> Future { self.onComplete(context: c) { result in result.analysis(ifSuccess: { _ in }, ifFailure: callback) } @@ -400,7 +369,7 @@ public extension Future { * This extension contains all methods related to functional composition */ public extension Future { - + /// Enables the the chaining of two future-wrapped asynchronous operations where the second operation depends on the success value of the first. /// Like map, the given closure (that returns the second operation) is only executed if the first operation (this future) is successful. /// If a regular `map` was used, the result would be a `Future>`. The implementation of this function uses `map`, but then flattens the result @@ -410,13 +379,13 @@ public extension Future { /// If this future succeeds, the returned future will complete with the future returned from the given closure. /// /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Future) -> Future { + public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Future) -> Future { return flatten(map(context: c, f: f)) } /// Transforms the given closure returning `Result` to a closure returning `Future` and then calls /// `flatMap(context c: ExecutionContext, f: T -> Future) -> Future` - public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Result) -> Future { + public func flatMap(context c: ExecutionContext = executionContextForCurrentContext(), f: T -> Result) -> Future { return self.flatMap(context: c) { value in Future.completed(f(value)) } @@ -424,17 +393,17 @@ public extension Future { /// See `map(context c: ExecutionContext, f: (T) -> U) -> Future` /// The given closure is executed according to the default threading model (see README.md) - public func map(f: (T) -> U) -> Future { + public func map(f: (T) -> U) -> Future { return self.map(context: executionContextForCurrentContext(), f: f) } /// Returns a future that succeeds with the value returned from the given closure when it is invoked with the success value /// from this future. If this future fails, the returned future fails with the same error. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func map(context c: ExecutionContext, f: (T) -> U) -> Future { - let p = Promise() + public func map(context c: ExecutionContext, f: (T) -> U) -> Future { + let p = Promise() - self.onComplete(context: c, callback: { result in + self.onComplete(context: c, callback: { (result: Result) in result.analysis( ifSuccess: { p.success(f($0)) }, ifFailure: p.failure ) @@ -442,12 +411,28 @@ public extension Future { return p.future } + + public func mapError(f: E -> E1) -> Future { + return mapError(context: executionContextForCurrentContext(), f: f) + } + + public func mapError(context c: ExecutionContext, f: E -> E1) -> Future { + let p = Promise() + + self.onComplete { result in + result.analysis( + ifSuccess: p.success , + ifFailure: { p.failure(f($0)) }) + } + + return p.future + } /// Adds the given closure as a callback for when this future completes. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) /// Returns a future that completes with the result from this future but only after executing the given closure - public func andThen(context c: ExecutionContext = executionContextForCurrentContext(), callback: Result -> ()) -> Future { - let p = Promise() + public func andThen(context c: ExecutionContext = executionContextForCurrentContext(), callback: Result -> ()) -> Future { + let p = Promise() self.onComplete(context: c) { result in callback(result) @@ -460,8 +445,8 @@ public extension Future { /// Returns a future that completes with this future if this future succeeds or with the value returned from the given closure /// when it is invoked with the error that this future failed with. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func recover(context c: ExecutionContext = executionContextForCurrentContext(), task: (NSError) -> T) -> Future { - return self.recoverWith(context: c) { error -> Future in + public func recover(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> T) -> Future { + return self.recoverWith(context: c) { error -> Future in return Future.succeeded(task(error)) } } @@ -471,8 +456,8 @@ public extension Future { /// This function should be used in cases where there are two asynchronous operations where the second operation (returned from the given closure) /// should only be executed if the first (this future) fails. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func recoverWith(context c: ExecutionContext = executionContextForCurrentContext(), task: (NSError) -> Future) -> Future { - let p = Promise() + public func recoverWith(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> Future) -> Future { + let p = Promise() self.onComplete(context: c) { result in result.analysis( @@ -485,8 +470,8 @@ public extension Future { /// Returns a future that succeeds with a tuple consisting of the success value of this future and the success value of the given future /// If either of the two futures fail, the returned future fails with the failure of this future or that future (in this order) - public func zip(that: Future) -> Future<(T,U)> { - return self.flatMap { thisVal -> Future<(T,U)> in + public func zip(that: Future) -> Future<(T,U), E> { + return self.flatMap { thisVal -> Future<(T,U), E> in return that.map { thatVal in return (thisVal, thatVal) } @@ -497,12 +482,14 @@ public extension Future { /// (i.e. the given closure returns `true` when invoked with the success value) or an error with code /// `ErrorCode.NoSuchElement` if the test failed. /// If this future fails, the returned future fails with the same error. - public func filter(p: T -> Bool) -> Future { - return self.flatMap { value -> Result in + public func filter(p: (T -> Bool)) -> Future> { + return self.mapError { error -> EitherError in + return EitherError.left(error) + }.flatMap { value -> Result> in if p(value) { - return Result(value: value) + return Result.success(value) } else { - return Result(error: errorFromCode(.NoSuchElement)) + return Result.failure(EitherError.right(.NoSuchElement)) } } } @@ -513,13 +500,13 @@ public extension Future { */ public extension Future { - func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future { + func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future { return firstCompletedOf([self, token.future.asType()]) } - /// See `onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future` + /// See `onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future` /// If the given invalidation token is invalidated when the future is completed, the given callback is not invoked - public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: Result -> ()) -> Future { + public func onComplete(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: Result -> ()) -> Future { firstCompletedOfSelfAndToken(token).onComplete(context: c) { res in token.context { if !token.isInvalid { @@ -530,9 +517,9 @@ public extension Future { return self; } - /// See `onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), callback: SuccessCallback) -> Future` + /// See `onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), callback: SuccessCallback) -> Future` /// If the given invalidation token is invalidated when the future is completed, the given callback is not invoked - public func onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: SuccessCallback) -> Future { + public func onSuccess(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: SuccessCallback) -> Future { firstCompletedOfSelfAndToken(token).onSuccess(context: c) { value in token.context { if !token.isInvalid { @@ -544,9 +531,9 @@ public extension Future { return self } - /// See `onFailure(context c: ExecutionContext = executionContextForCurrentContext(), callback: FailureCallback) -> Future` + /// See `onFailure(context c: ExecutionContext = executionContextForCurrentContext(), callback: FailureCallback) -> Future` /// If the given invalidation token is invalidated when the future is completed, the given callback is not invoked - public func onFailure(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: FailureCallback) -> Future { + public func onFailure(context c: ExecutionContext = executionContextForCurrentContext(), token: InvalidationTokenType, callback: FailureCallback) -> Future { firstCompletedOfSelfAndToken(token).onFailure(context: c) { error in token.context { if !token.isInvalid { @@ -560,8 +547,8 @@ public extension Future { /// Returns a future that fails with the error from the outer or inner future or succeeds with the value from the inner future /// if both futures succeed. -public func flatten(future: Future>) -> Future { - let p = Promise() +public func flatten(future: Future, E>) -> Future { + let p = Promise() future.onComplete { result in result.analysis( @@ -574,7 +561,7 @@ public func flatten(future: Future>) -> Future { /// Short-hand for `lhs.recover(rhs())` /// `rhs` is executed according to the default threading model (see README.md) -public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> Future { +public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> Future { return lhs.recover(context: executionContextForCurrentContext(), task: { _ in return rhs() }) @@ -582,7 +569,7 @@ public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> Futur /// Short-hand for `lhs.recoverWith(rhs())` /// `rhs` is executed according to the default threading model (see README.md) -public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> Future) -> Future { +public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> Future) -> Future { return lhs.recoverWith(context: executionContextForCurrentContext(), task: { _ in return rhs() }) diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index 9d039ac..d80a7ce 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -29,16 +29,16 @@ import Result /// on `Queue.global`. /// (The Swift compiler does not allow a context parameter with a default value /// so we define some functions twice) -public func fold>(seq: S, zero: R, f: (R, T) -> R) -> Future { +public func fold>(seq: S, zero: R, f: (R, T) -> R) -> Future { return fold(seq, context: Queue.global.context, zero, f) } /// Performs the fold operation over a sequence of futures. The folding is performed /// in the given context. -public func fold>(seq: S, context c: ExecutionContext, zero: R, f: (R, T) -> R) -> Future { +public func fold>(seq: S, context c: ExecutionContext, zero: R, f: (R, T) -> R) -> Future { - return reduce(seq, Future.succeeded(zero)) { zero, elem in + return reduce(seq, Future.succeeded(zero)) { zero, elem in return zero.flatMap { zeroVal in elem.map(context: c) { elemVal in return f(zeroVal, elemVal) @@ -48,13 +48,13 @@ public func fold>(s } /// See `traverse(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future) -> Future<[U]>` -public func traverse(seq: S, f: T -> Future) -> Future<[U]> { +public func traverse(seq: S, f: T -> Future) -> Future<[U], E> { return traverse(seq, context: Queue.global.context, f) } /// Turns a sequence of T's into an array of `Future`'s by calling the given closure for each element in the sequence. /// If no context is provided, the given closure is executed on `Queue.global` -public func traverse(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future) -> Future<[U]> { +public func traverse(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future) -> Future<[U], E> { return fold(map(seq, f), context: c, [U]()) { (list: [U], elem: U) -> [U] in return list + [elem] @@ -64,14 +64,14 @@ public func traverse(seq: /// Turns a sequence of `Future`'s into a future with an array of T's (Future<[T]>) /// If one of the futures in the given sequence fails, the returned future will fail /// with the error of the first future that comes first in the list. -public func sequence>(seq: S) -> Future<[T]> { - return traverse(seq) { (fut: Future) -> Future in +public func sequence>(seq: S) -> Future<[T], E> { + return traverse(seq) { (fut: Future) -> Future in return fut } } /// See `find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future` -public func find>(seq: S, p: T -> Bool) -> Future { +public func find>(seq: S, p: T -> Bool) -> Future> { return find(seq, context: Queue.global.context, p) } @@ -80,21 +80,23 @@ public func find>(seq: /// If any of the futures in the given sequence fail, the returned future fails with the /// error of the first failed future in the sequence. /// If no futures in the sequence pass the test, a future with an error with NoSuchElement is returned. -public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future { - return sequence(seq).flatMap(context: c) { val -> Result in +public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future> { + return sequence(seq).mapError { error in + return EitherError.left(error) + }.flatMap(context: c) { val -> Result> in for elem in val { if (p(elem)) { return Result(value: elem) } } - return Result(error: errorFromCode(.NoSuchElement)) + return Result(error: EitherError.right(.NoSuchElement)) } } /// Returns a future that returns with the first future from the given sequence that completes /// (regardless of whether that future succeeds or fails) -public func firstCompletedOf>(seq: S) -> Future { - let p = Promise() +public func firstCompletedOf>(seq: S) -> Future { + let p = Promise() for fut in seq { fut.onComplete(context: Queue.global.context) { res in @@ -113,19 +115,19 @@ public func firstCompletedOf>`. /// The implementation of this function uses `map`, but then flattens the result before returning it. -public func flatMap(result: Result, @noescape f: T -> Future) -> Future { +public func flatMap(result: Result, @noescape f: T -> Future) -> Future { return flatten(result.map(f)) } /// Returns a .Failure with the error from the outer or inner result if either of the two failed /// or a .Success with the success value from the inner Result -public func flatten(result: Result,NSError>) -> Result { +public func flatten(result: Result,E>) -> Result { return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) }) } /// Returns the inner future if the outer result succeeded or a failed future /// with the error from the outer result otherwise -public func flatten(result: Result,NSError>) -> Future { +public func flatten(result: Result,E>) -> Future { return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) }) } diff --git a/BrightFutures/InvalidationToken.swift b/BrightFutures/InvalidationToken.swift index 450a5dd..615b77f 100644 --- a/BrightFutures/InvalidationToken.swift +++ b/BrightFutures/InvalidationToken.swift @@ -16,7 +16,7 @@ public protocol InvalidationTokenType { var isInvalid : Bool { get } - var future: Future { get } + var future: Future { get } var context: ExecutionContext { get } } @@ -29,7 +29,7 @@ public protocol ManualInvalidationTokenType : InvalidationTokenType { /// A default invalidation token implementation public class InvalidationToken : ManualInvalidationTokenType { - let promise = Promise() + let promise = Promise() /// The synchronous context on which the invalidation and callbacks are executed public let context = toContext(Semaphore(value: 1)) @@ -43,12 +43,12 @@ public class InvalidationToken : ManualInvalidationTokenType { } /// The future will fail with an error with code `InvalidationTokenInvalid` when the token invalidates - public var future: Future { + public var future: Future { return self.promise.future } /// Invalidates the token public func invalidate() { - self.promise.failure(errorFromCode(.InvalidationTokenInvalidated)) + self.promise.failure(.InvalidationTokenInvalidated) } } diff --git a/BrightFutures/Promise.swift b/BrightFutures/Promise.swift index cfac237..78647c7 100644 --- a/BrightFutures/Promise.swift +++ b/BrightFutures/Promise.swift @@ -29,18 +29,18 @@ import Result /// when the asynchronous operation is completion. Completing a /// promise is thread safe and is typically performed from the /// (background) thread where the operation itself is also performed. -public class Promise { +public class Promise { /// The future that will complete through this promise - public let future: Future + public let future: Future /// Creates a new promise with a pending future public init() { - self.future = Future() + self.future = Future() } /// Completes the promise's future with the given future - public func completeWith(future: Future) { + public func completeWith(future: Future) { future.onComplete { result in result.analysis(ifSuccess: self.success, ifFailure: self.failure) } @@ -59,26 +59,26 @@ public class Promise { } /// Completes the promise's future with the given error - /// See `future.failure(error: NSError)` - public func failure(error: NSError) { + /// See `future.failure(error: E)` + public func failure(error: E) { self.future.failure(error) } /// Attempts to complete the promise's future with the given error - /// See `future.tryFailure(error: NSError)` - public func tryFailure(error: NSError) -> Bool { + /// See `future.tryFailure(error: E)` + public func tryFailure(error: E) -> Bool { return self.future.tryFailure(error) } /// Completes the promise's future with the given result - /// See `future.complete(result: Result)` - public func complete(result: Result) { + /// See `future.complete(result: Result)` + public func complete(result: Result) { return self.future.complete(result) } /// Attempts to complete the promise's future with the given result - /// See `future.tryComplete(result: Result)` - public func tryComplete(result: Result) -> Bool { + /// See `future.tryComplete(result: Result)` + public func tryComplete(result: Result) -> Bool { return self.future.tryComplete(result) } diff --git a/BrightFutures/Queue.swift b/BrightFutures/Queue.swift index 6d8c99c..ef10c9e 100644 --- a/BrightFutures/Queue.swift +++ b/BrightFutures/Queue.swift @@ -87,8 +87,8 @@ public struct Queue { /// Asynchronously executes the given closure on this queue and /// returns a future that will succeed with the result of the closure. - public func async(block: () -> T) -> Future { - let p = Promise() + public func async(block: () -> T) -> Future { + let p = Promise() async { p.success(block()) @@ -106,8 +106,8 @@ public struct Queue { /// Asynchronously executes the given closure on the queue after a delay /// and returns a future that will succeed with the result of the closure. /// Identical to dispatch_after(dispatch_time, self.underlyingQueue, block) - public func after(delay: TimeInterval, block: () -> T) -> Future { - let p = Promise() + public func after(delay: TimeInterval, block: () -> T) -> Future { + let p = Promise() after(delay) { p.success(block()) From 9335a5c169f7938bf160ffccc3e62fde8aeb0cec Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 17 May 2015 10:47:44 +0200 Subject: [PATCH 23/61] tests compile & succeed --- BrightFutures.xcodeproj/project.pbxproj | 22 +--- .../xcshareddata/BrightFutures.xccheckout | 4 +- BrightFutures/Error.swift | 2 +- BrightFutures/Future.swift | 57 ++++++---- BrightFutures/FutureUtils.swift | 11 +- BrightFutures/InvalidationToken.swift | 6 +- BrightFuturesTests/BrightFuturesTests.swift | 107 +++++++++--------- .../InvalidationTokenTests.swift | 4 +- BrightFuturesTests/QueueTests.swift | 4 +- BrightFuturesTests/ResultTests.swift | 41 ++++--- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/Result | 2 +- 13 files changed, 139 insertions(+), 125 deletions(-) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 349e020..86324b2 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -9,9 +9,10 @@ /* Begin PBXBuildFile section */ B31A24291B02691A0016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; - B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */; }; E907D1DE1A6AE4A000AB8075 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */; }; + E92B90781B088CBF005EDB6C /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E92B90791B088CC2005EDB6C /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9319EA31A397D2C00A0604A /* QueueTests.swift */; }; E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E979E5F31975B1AA007FE914 /* FutureUtils.swift */; }; E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; }; @@ -38,10 +39,6 @@ E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496E61B05086100F82839 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; E9F496E71B05086300F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; - E9F496E81B05088000F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E9F496E91B05088200F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E9F496EA1B05088F00F82839 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; - E9F496EB1B05089100F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496EF1B052F1400F82839 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Error.swift */; }; @@ -73,8 +70,8 @@ dstSubfolderSpec = 10; files = ( E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */, - E9F496E81B05088000F82839 /* Result.framework in CopyFiles */, - E9F496E91B05088200F82839 /* Box.framework in CopyFiles */, + E92B90781B088CBF005EDB6C /* Result.framework in CopyFiles */, + E92B90791B088CC2005EDB6C /* Box.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -132,7 +129,6 @@ buildActionMask = 2147483647; files = ( E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */, - B31A24331B026D4B0016AE7A /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -150,8 +146,6 @@ buildActionMask = 2147483647; files = ( E9DF0821194470060083F7F2 /* BrightFutures.framework in Frameworks */, - E9F496EB1B05089100F82839 /* Box.framework in Frameworks */, - E9F496EA1B05088F00F82839 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -330,7 +324,6 @@ buildPhases = ( E9DF081C194470060083F7F2 /* Sources */, E9DF081D194470060083F7F2 /* Frameworks */, - E9DF081E194470060083F7F2 /* Resources */, E9F496E21B0397D200F82839 /* CopyFiles */, ); buildRules = ( @@ -402,13 +395,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - E9DF081E194470060083F7F2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout b/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout index 43cd5e9..cc47d9a 100644 --- a/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout +++ b/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout @@ -7,14 +7,14 @@ IDESourceControlProjectIdentifier 3A31C8B6-B0CD-4D79-9DFB-AE57C9B04C9F IDESourceControlProjectName - BrightFutures + project IDESourceControlProjectOriginsDictionary 93608FFF008CD82ADDFA850BB9C1630FA754586F github.com:Thomvis/BrightFutures.git IDESourceControlProjectPath - BrightFutures.xcodeproj + BrightFutures.xcodeproj/project.xcworkspace IDESourceControlProjectRelativeInstallPathDictionary 93608FFF008CD82ADDFA850BB9C1630FA754586F diff --git a/BrightFutures/Error.swift b/BrightFutures/Error.swift index 99bf0f9..ae02b8b 100644 --- a/BrightFutures/Error.swift +++ b/BrightFutures/Error.swift @@ -53,7 +53,7 @@ public enum EitherError : ErrorType { public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" /// An enum representing every possible error code for errors returned by BrightFutures -public enum BrightFuturesError: ErrorType { +public enum BrightFuturesError: ErrorType, Equatable { case NoSuchElement case InvalidationTokenInvalidated diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index ea3db69..281bee1 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -24,18 +24,18 @@ import Foundation import Result /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(@autoclosure(escaping) task: () -> T) -> Future { +public func future(@autoclosure(escaping) task: () -> T) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on `Queue.global` and wraps the result of the task in a Future -public func future(task: () -> T) -> Future { +public func future(task: () -> T) -> Future { return future(context: Queue.global.context, task) } /// Executes the given task on the given context and wraps the result of the task in a Future -public func future(context c: ExecutionContext, task: () -> T) -> Future { - return future(context: c, { () -> Result in +public func future(context c: ExecutionContext, task: () -> T) -> Future { + return future(context: c, { () -> Result in return Result(value: task()) }) } @@ -51,7 +51,7 @@ public func future(task: () -> Result) -> Future { } /// Executes the given task on the given context and wraps the result of the task in a Future -public func future(context c: ExecutionContext, task: () -> Result) -> Future { +public func future(context c: ExecutionContext, task: () -> Result) -> Future { let promise = Promise(); c { @@ -266,8 +266,8 @@ public extension Future { } /// Returns a new future with the new type. - /// The value that this future succeeds with will be downcasted to the new type using `as!` and may fail - public func asType() -> Future { + /// The value or error will be casted using `as!` and may cause a runtime error + public func forceType() -> Future { return self.map { $0 as! U }.mapError { $0 as! E1} } @@ -445,9 +445,9 @@ public extension Future { /// Returns a future that completes with this future if this future succeeds or with the value returned from the given closure /// when it is invoked with the error that this future failed with. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func recover(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> T) -> Future { - return self.recoverWith(context: c) { error -> Future in - return Future.succeeded(task(error)) + public func recover(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> T) -> Future { + return self.recoverWith(context: c) { error -> Future in + return Future.succeeded(task(error)) } } @@ -456,12 +456,12 @@ public extension Future { /// This function should be used in cases where there are two asynchronous operations where the second operation (returned from the given closure) /// should only be executed if the first (this future) fails. /// The closure is executed on the given context. If no context is given, the behavior is defined by the default threading model (see README.md) - public func recoverWith(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> Future) -> Future { - let p = Promise() + public func recoverWith(context c: ExecutionContext = executionContextForCurrentContext(), task: (E) -> Future) -> Future { + let p = Promise() self.onComplete(context: c) { result in result.analysis( - ifSuccess: { value in p.completeWith(self) }, + ifSuccess: { value in p.success(value) }, ifFailure: { p.completeWith(task($0)) }) } @@ -500,8 +500,16 @@ public extension Future { */ public extension Future { - func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future { - return firstCompletedOf([self, token.future.asType()]) + func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { + return firstCompletedOf([ + self.mapError { + EitherError.left($0) + }, + promoteValue(token.future.mapError { + EitherError.right($0) + }) + ] + ) } /// See `onComplete(context c: ExecutionContext = executionContextForCurrentContext(), callback: CompletionCallback) -> Future` @@ -510,7 +518,7 @@ public extension Future { firstCompletedOfSelfAndToken(token).onComplete(context: c) { res in token.context { if !token.isInvalid { - callback(res) + callback(self.result!) } } } @@ -537,7 +545,7 @@ public extension Future { firstCompletedOfSelfAndToken(token).onFailure(context: c) { error in token.context { if !token.isInvalid { - callback(error) + callback(self.result!.error!) } } } @@ -561,7 +569,7 @@ public func flatten(future: Future, E>) -> Future { /// Short-hand for `lhs.recover(rhs())` /// `rhs` is executed according to the default threading model (see README.md) -public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> Future { +public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> Future { return lhs.recover(context: executionContextForCurrentContext(), task: { _ in return rhs() }) @@ -569,8 +577,19 @@ public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> T) -> /// Short-hand for `lhs.recoverWith(rhs())` /// `rhs` is executed according to the default threading model (see README.md) -public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> Future) -> Future { +public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> Future) -> Future { return lhs.recoverWith(context: executionContextForCurrentContext(), task: { _ in return rhs() }) } + +public func promoteError(future: Future) -> Future { + return future.mapError { $0 as! E } // future will never fail, so this map block will never get called +} + +/// If a future has this as its value type, it will never complete with success +public enum NoValue { } + +public func promoteValue(future: Future) -> Future { + return future.map { $0 as! T } // future will never succeed, so this map block will never get called +} diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index d80a7ce..327cb07 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -30,14 +30,12 @@ import Result /// (The Swift compiler does not allow a context parameter with a default value /// so we define some functions twice) public func fold>(seq: S, zero: R, f: (R, T) -> R) -> Future { - return fold(seq, context: Queue.global.context, zero, f) } /// Performs the fold operation over a sequence of futures. The folding is performed /// in the given context. public func fold>(seq: S, context c: ExecutionContext, zero: R, f: (R, T) -> R) -> Future { - return reduce(seq, Future.succeeded(zero)) { zero, elem in return zero.flatMap { zeroVal in elem.map(context: c) { elemVal in @@ -55,7 +53,6 @@ public func traverse(se /// Turns a sequence of T's into an array of `Future`'s by calling the given closure for each element in the sequence. /// If no context is provided, the given closure is executed on `Queue.global` public func traverse(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future) -> Future<[U], E> { - return fold(map(seq, f), context: c, [U]()) { (list: [U], elem: U) -> [U] in return list + [elem] } @@ -134,16 +131,16 @@ public func flatten(result: Result,E>) -> Future { /// Turns a sequence of `Result`'s into a Result with an array of T's (`Result<[T]>`) /// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the /// error from the first failed result from the sequence. -public func sequence>(seq: S) -> Result<[T],NSError> { - return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T],NSError> in +public func sequence>(seq: S) -> Result<[T], E> { + return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T], E> in switch res { case .Success(let boxedResultSequence): switch elem { case .Success(let boxedElemValue): let newSeq = boxedResultSequence.value + [boxedElemValue.value] - return Result<[T],NSError>(value: newSeq) + return Result<[T], E>(value: newSeq) case .Failure(let boxedElemError): - return Result<[T],NSError>(error: boxedElemError.value) + return Result<[T], E>(error: boxedElemError.value) } case .Failure(let err): return res diff --git a/BrightFutures/InvalidationToken.swift b/BrightFutures/InvalidationToken.swift index 615b77f..a262532 100644 --- a/BrightFutures/InvalidationToken.swift +++ b/BrightFutures/InvalidationToken.swift @@ -16,7 +16,7 @@ public protocol InvalidationTokenType { var isInvalid : Bool { get } - var future: Future { get } + var future: Future { get } var context: ExecutionContext { get } } @@ -29,7 +29,7 @@ public protocol ManualInvalidationTokenType : InvalidationTokenType { /// A default invalidation token implementation public class InvalidationToken : ManualInvalidationTokenType { - let promise = Promise() + let promise = Promise() /// The synchronous context on which the invalidation and callbacks are executed public let context = toContext(Semaphore(value: 1)) @@ -43,7 +43,7 @@ public class InvalidationToken : ManualInvalidationTokenType { } /// The future will fail with an error with code `InvalidationTokenInvalid` when the token invalidates - public var future: Future { + public var future: Future { return self.promise.future } diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index 873d28f..d99516d 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -39,11 +39,11 @@ class BrightFuturesTests: XCTestCase { extension BrightFuturesTests { func testCompletedFuture() { - let f = Future.succeeded(2) + let f = Future.succeeded(2) let completeExpectation = self.expectationWithDescription("immediate complete") - f.onComplete { (result: Result) in + f.onComplete { result in XCTAssert(result.isSuccess) completeExpectation.fulfill() } @@ -63,14 +63,14 @@ extension BrightFuturesTests { } func testCompletedVoidFuture() { - let f = Future.succeeded() + let f = Future.succeeded() XCTAssert(f.isCompleted, "void future should be completed") XCTAssert(f.isSuccess, "void future should be success") } func testFailedFuture() { let error = NSError(domain: "test", code: 0, userInfo: nil) - let f = Future.failed(error) + let f = Future.failed(error) let completeExpectation = self.expectationWithDescription("immediate complete") @@ -99,7 +99,7 @@ extension BrightFuturesTests { } func testCompleteAfterFuture() { - let f = Future.completeAfter(1, withValue: 3) + let f = Future.completeAfter(1, withValue: 3) XCTAssertFalse(f.isCompleted) @@ -118,7 +118,7 @@ extension BrightFuturesTests { // this is inherently impossible to test, but we'll give it a try func testNeverCompletingFuture() { - let f = Future.never() + let f = Future.never() XCTAssert(!f.isCompleted) sleep(UInt32(Double(arc4random_uniform(100))/100.0)) @@ -144,7 +144,7 @@ extension BrightFuturesTests { func testControlFlowSyntaxWithError() { - let f : Future = future { + let f : Future = future { Result(error: NSError(domain: "NaN", code: 0, userInfo: nil)) } @@ -161,7 +161,7 @@ extension BrightFuturesTests { func testAutoClosure() { let names = ["Steve", "Tim"] - let f = Future.succeeded(names.count) + let f = Future.succeeded(names.count) let e = self.expectation() f.onSuccess { value in @@ -171,8 +171,8 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) - let e1 = self.expectationWithDescription("-") - Future.succeeded(fibonacci(10)).onSuccess { value in + let e1 = self.expectation() + Future.succeeded(fibonacci(10)).onSuccess { value in XCTAssert(value == 55); e1.fulfill() } @@ -181,7 +181,7 @@ extension BrightFuturesTests { } func testPromise() { - let p = Promise() + let p = Promise() Queue.global.async { p.success(fibonacci(10)) @@ -232,7 +232,7 @@ extension BrightFuturesTests { } func testDefaultCallbackExecutionContextFromMain() { - let f = Future.succeeded(1) + let f = Future.succeeded(1) let e = self.expectation() f.onSuccess { _ in XCTAssert(NSThread.isMainThread(), "the callback should run on main") @@ -243,7 +243,7 @@ extension BrightFuturesTests { } func testDefaultCallbackExecutionContextFromBackground() { - let f = Future.succeeded(1) + let f = Future.succeeded(1) let e = self.expectation() dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { f.onSuccess { _ in @@ -269,7 +269,7 @@ extension BrightFuturesTests { let e = self.expectation() - let f = Future.succeeded(4) + let f = Future.succeeded(4) let f1 = f.andThen { result in if let val = result.value { answer *= val @@ -307,7 +307,7 @@ extension BrightFuturesTests { return i / 5 } - Future.succeeded(fibonacci(10)).map(divideByFive).onSuccess { val in + Future.succeeded(fibonacci(10)).map(divideByFive).onSuccess { val in XCTAssertEqual(val, 11, "The 10th fibonacci number (55) divided by 5 is 11") e.fulfill() return @@ -392,7 +392,7 @@ extension BrightFuturesTests { let e1 = self.expectation() - let f: Future = Future.failed(NSError(domain: "NaN", code: 0, userInfo: nil)) ?? future(fibonacci(5)) + let f: Future = Future.failed(NSError(domain: "NaN", code: 0, userInfo: nil)) ?? future(fibonacci(5)) f.onSuccess { XCTAssertEqual($0, 5) @@ -403,8 +403,8 @@ extension BrightFuturesTests { } func testZip() { - let f = Future.succeeded(1) - let f1 = Future.succeeded(2) + let f = Future.succeeded(1) + let f1 = Future.succeeded(2) let e = self.expectation() @@ -418,12 +418,12 @@ extension BrightFuturesTests { } func testZipThisFails() { - let f = future { () -> Result in + let f: Future = future { () -> Result in sleep(1) return Result(error: NSError(domain: "test", code: 2, userInfo: nil)) } - let f1 = Future.succeeded(2) + let f1 = Future.succeeded(2) let e = self.expectation() @@ -442,7 +442,7 @@ extension BrightFuturesTests { return Result(error: NSError(domain: "tester", code: 3, userInfo: nil)) } - let f1 = Future.succeeded(2) + let f1 = Future.succeeded(2) let e = self.expectation() @@ -479,9 +479,9 @@ extension BrightFuturesTests { func testFilterNoSuchElement() { let e = self.expectation() - Future.succeeded(3).filter { $0 > 5}.onComplete { result in + Future.succeeded(3).filter { $0 > 5}.onComplete { result in if let err = result.error { - XCTAssert(err.code == ErrorCode.NoSuchElement.rawValue, "filter should yield no result") + XCTAssert(err.nsError.code == 0, "filter should yield no result") } e.fulfill() @@ -491,7 +491,7 @@ extension BrightFuturesTests { func testFilterPasses() { let e = self.expectation() - Future.succeeded("Thomas").filter { $0.hasPrefix("Th") }.onComplete { result in + Future.succeeded("Thomas").filter { $0.hasPrefix("Th") }.onComplete { result in if let val = result.value { XCTAssert(val == "Thomas", "Filter should pass") } @@ -504,7 +504,7 @@ extension BrightFuturesTests { func testForcedFuture() { var x = 10 - let f: Future = future { () -> () in + let f: Future = future { () -> () in NSThread.sleepForTimeInterval(0.5) x = 3 } @@ -513,7 +513,7 @@ extension BrightFuturesTests { } func testForcedFutureWithTimeout() { - let f: Future = future { + let f: Future = future { NSThread.sleepForTimeInterval(0.5) } @@ -526,8 +526,9 @@ extension BrightFuturesTests { let e = self.expectation() let finalString = "Greg" - let flatMapped: Future = Future.succeeded("Thomas").flatMap { _ -> Future in - return Future.succeeded(finalString) + + let flatMapped = Future.succeeded("Thomas").flatMap { _ in + return Future.succeeded(finalString) } flatMapped.onSuccess { s in @@ -548,7 +549,7 @@ extension BrightFuturesTests { let n = 10 let f = traverse(Array(1...n)) { i in - Future.succeeded(fibonacci(i)) + Future.succeeded(fibonacci(i)) } let e = self.expectation() @@ -567,7 +568,7 @@ extension BrightFuturesTests { func testUtilsTraverseEmpty() { let e = self.expectation() - traverse([Int]()) {Future.succeeded($0)}.onSuccess { res in + traverse([Int]()) {Future.succeeded($0)}.onSuccess { res in XCTAssertEqual(res.count, 0); e.fulfill() } @@ -578,7 +579,7 @@ extension BrightFuturesTests { func testUtilsTraverseSingleError() { let e = self.expectation() - let evenFuture: Int -> Future = { i in + let evenFuture: Int -> Future = { i in return future { if i % 2 == 0 { return Result(value: true) @@ -602,7 +603,7 @@ extension BrightFuturesTests { func testUtilsTraverseMultipleErrors() { let e = self.expectation() - let evenFuture: Int -> Future = { i in + let evenFuture: Int -> Future = { i in return future { err in if i % 2 == 0 { return Result(value: true) @@ -623,7 +624,7 @@ extension BrightFuturesTests { func testUtilsTraverseWithExecutionContext() { let e = self.expectation() - traverse(Array(1...10), context: Queue.main.context) { _ -> Future in + traverse(Array(1...10), context: Queue.main.context) { _ -> Future in XCTAssert(NSThread.isMainThread()) return Future.succeeded(1) }.onComplete { _ in @@ -653,11 +654,11 @@ extension BrightFuturesTests { let error = NSError(domain: "fold-with-error", code: 0, userInfo: nil) // create a list of Futures containing the Fibonacci sequence and one error - let fibonacciList = (1...10).map { val -> Future in + let fibonacciList = (1...10).map { val -> Future in if val == 3 { - return Future.failed(error) + return Future.failed(error) } else { - return fibonacciFuture(val) + return promoteError(fibonacciFuture(val)) } } @@ -674,7 +675,7 @@ extension BrightFuturesTests { func testUtilsFoldWithExecutionContext() { let e = self.expectation() - fold([Future.succeeded(1)], context: Queue.main.context, 10) { remainder, elem -> Int in + fold([Future.succeeded(1)], context: Queue.main.context, 10) { remainder, elem -> Int in XCTAssert(NSThread.isMainThread()) return remainder + elem }.onSuccess { val in @@ -690,7 +691,7 @@ extension BrightFuturesTests { let e = self.expectation() - fold([Future](), z, { $0 + $1 }).onSuccess { val in + fold([Future](), z, { $0 + $1 }).onSuccess { val in XCTAssertEqual(val, z) e.fulfill() } @@ -699,7 +700,7 @@ extension BrightFuturesTests { } func testUtilsFirstCompleted() { - let futures = [ + let futures: [Future] = [ Future.completeAfter(0.2, withValue: 3), Future.completeAfter(0.3, withValue: 13), Future.completeAfter(0.4, withValue: 23), @@ -737,7 +738,7 @@ extension BrightFuturesTests { func testUtilsSequenceEmpty() { let e = self.expectation() - sequence([Future]()).onSuccess { val in + sequence([Future]()).onSuccess { val in XCTAssertEqual(val.count, 0) e.fulfill() @@ -747,7 +748,7 @@ extension BrightFuturesTests { } func testUtilsFindSuccess() { - let futures: [Future] = [ + let futures: [Future] = [ Future.succeeded(1), Future.completeAfter(0.2, withValue: 3), Future.succeeded(5), @@ -771,7 +772,7 @@ extension BrightFuturesTests { } func testUtilsFindNoSuchElement() { - let futures: [Future] = [ + let futures: [Future] = [ Future.succeeded(1), Future.completeAfter(0.2, withValue: 3), Future.succeeded(5), @@ -786,7 +787,7 @@ extension BrightFuturesTests { let e = self.expectation() f.onFailure { err in - XCTAssertEqual(err.code, ErrorCode.NoSuchElement.rawValue, "No matching elements") + XCTAssertEqual(err.nsError.code, 0, "No matching elements") e.fulfill() } @@ -802,12 +803,12 @@ extension BrightFuturesTests { // Creates a lot of futures and adds completion blocks concurrently, which should all fire func testStress() { let instances = 100; - var successfulFutures = [Future]() - var failingFutures = [Future]() + var successfulFutures = [Future]() + var failingFutures = [Future]() let contexts: [ExecutionContext] = [ImmediateExecutionContext, Queue.main.context, Queue.global.context] let randomContext: () -> ExecutionContext = { contexts[Int(arc4random_uniform(UInt32(contexts.count)))] } - let randomFuture: () -> Future = { + let randomFuture: () -> Future = { if arc4random() % 2 == 0 { return successfulFutures[Int(arc4random_uniform(UInt32(successfulFutures.count)))] } else { @@ -818,7 +819,7 @@ extension BrightFuturesTests { var finalSum = 0; for _ in 1...instances { - var future: Future + var future: Future if arc4random() % 2 == 0 { let futureResult: Int = Int(arc4random_uniform(10)) finalSum += futureResult @@ -858,7 +859,7 @@ extension BrightFuturesTests { } func testSerialCallbacks() { - let p = Promise() + let p = Promise() var executingCallbacks = 0 for _ in 0..<10 { @@ -907,7 +908,7 @@ extension BrightFuturesTests { XCTAssertEqual(dispatch_get_specific(&key), valuePointer, "value should have been set on the main (i.e. current) queue") let e = self.expectation() - Future.succeeded(1).onSuccess(context: Queue.main.context) { val in + Future.succeeded(1).onSuccess(context: Queue.main.context) { val in XCTAssertEqual(dispatch_get_specific(&key), valuePointer, "we should now too be on the main queue") e.fulfill() } @@ -924,14 +925,14 @@ extension XCTestCase { return self.expectationWithDescription("no description") } - func failingFuture() -> Future { + func failingFuture() -> Future { return future { error in usleep(arc4random_uniform(100)) return Result(error: NSError(domain: "failedFuture", code: 0, userInfo: nil)) } } - func succeedingFuture(val: U) -> Future { + func succeedingFuture(val: U) -> Future { return future { _ in usleep(arc4random_uniform(100)) return Result(value: val) @@ -948,8 +949,8 @@ func fibonacci(n: Int) -> Int { } } -func fibonacciFuture(n: Int) -> Future { - return Future.succeeded(fibonacci(n)) +func fibonacciFuture(n: Int) -> Future { + return Future.succeeded(fibonacci(n)) } func getMutablePointer (object: AnyObject) -> UnsafeMutablePointer { diff --git a/BrightFuturesTests/InvalidationTokenTests.swift b/BrightFuturesTests/InvalidationTokenTests.swift index 3fdd0cc..16406e6 100644 --- a/BrightFuturesTests/InvalidationTokenTests.swift +++ b/BrightFuturesTests/InvalidationTokenTests.swift @@ -28,7 +28,7 @@ class InvalidationTokenTests: XCTestCase { XCTAssert(!token.future.isCompleted, "token should have a future and not be complete") token.invalidate() XCTAssert(token.future.error != nil, "future should have an error") - if let error = token.future.error { + if let error = token.future.error?.nsError { XCTAssertEqual(error.domain, BrightFuturesErrorDomain) XCTAssertEqual(error.code, InvalidationTokenInvalid) } @@ -37,7 +37,7 @@ class InvalidationTokenTests: XCTestCase { func testCompletionAfterInvalidation() { let token = InvalidationToken() - let p = Promise() + let p = Promise() p.future.onSuccess(token: token) { val in XCTAssert(false, "onSuccess should not get called") diff --git a/BrightFuturesTests/QueueTests.swift b/BrightFuturesTests/QueueTests.swift index 9d8d573..645c0b1 100755 --- a/BrightFuturesTests/QueueTests.swift +++ b/BrightFuturesTests/QueueTests.swift @@ -70,7 +70,7 @@ class QueueTests: XCTestCase { func testAsyncFuture() { // unfortunately, the compiler is not able to figure out that we want the // future-returning async method - let f: Future = Queue.global.async({ + let f: Future = Queue.global.async({ NSThread.sleepForTimeInterval(1.0) return "fibonacci" }) @@ -99,7 +99,7 @@ class QueueTests: XCTestCase { func testAfterFuture() { // unfortunately, the compiler is not able to figure out that we want the // future-returning async method - let f: Future = Queue.global.after(.In(1.0)) { + let f: Future = Queue.global.after(.In(1.0)) { return "fibonacci" } diff --git a/BrightFuturesTests/ResultTests.swift b/BrightFuturesTests/ResultTests.swift index 70efd5f..bc996ad 100644 --- a/BrightFuturesTests/ResultTests.swift +++ b/BrightFuturesTests/ResultTests.swift @@ -97,20 +97,20 @@ class ResultTests: XCTestCase { } func testFlatMapResultFailure() { - let r = divide(20, 0).flatMap { i -> Result in + let r = divide(20, 0).flatMap { i -> Result in XCTAssert(false, "flatMap should not get called if the result failed") return divide(i, 2) } XCTAssert(r.isFailure) - XCTAssertEqual(r.error!.domain, "DivisionByZeroError") + XCTAssertEqual(r.error!.nsError.code, 123) } func testFlatMapFutureSuccess() { - let f = flatMap(divide(100, 10)) { i -> Future in - return future { + let f = flatMap(divide(100, 10)) { i -> Future in + return promoteError(future { fibonacci(i) - } + }) } let e = self.expectation() @@ -124,17 +124,17 @@ class ResultTests: XCTestCase { } func testFlatMapFutureFailure() { - let f = flatMap(divide(100, 0)) { i -> Future in + let f = flatMap(divide(100, 0)) { i -> Future in XCTAssert(false, "flatMap should not get called if the result failed") - return future { + return promoteError(future { fibonacci(i) - } + }) } let e = self.expectation() f.onFailure { err in - XCTAssertEqual(err.domain, "DivisionByZeroError") + XCTAssertEqual(err.nsError.code, 123) e.fulfill() } @@ -142,24 +142,24 @@ class ResultTests: XCTestCase { } func testSequenceSuccess() { - let results: [Result] = (1...10).map { i in + let results: [Result] = (1...10).map { i in return divide(123, i) } - let result: Result<[Int],NSError> = sequence(results) + let result: Result<[Int], MathError> = sequence(results) let outcome = [123, 61, 41, 30, 24, 20, 17, 15, 13, 12] XCTAssertEqual(result.value!, outcome) } func testSequenceFailure() { - let results: [Result] = (-10...10).map { i in + let results: [Result] = (-10...10).map { i in return divide(123, i) } let r = sequence(results) XCTAssert(r.isFailure) - XCTAssertEqual(r.error!.domain, "DivisionByZeroError") + XCTAssertEqual(r.error!.nsError.code, 123) } func testRecoverNeeded() { @@ -177,9 +177,20 @@ class ResultTests: XCTestCase { } } -func divide(a: Int, b: Int) -> Result { +enum MathError: ErrorType { + case DivisionByZero + + var nsError: NSError { + switch self { + case .DivisionByZero: + return NSError(domain: "nl.thomvis.brightfuturestests", code: 123, userInfo: nil); + } + } +} + +func divide(a: Int, b: Int) -> Result { if (b == 0) { - return Result(error: NSError(domain: "DivisionByZeroError", code: 0, userInfo: nil)) + return Result(error: .DivisionByZero) } return Result(value: a / b) diff --git a/Cartfile b/Cartfile index 58c59f8..33bdff4 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "antitypical/Result" ~> 0.4.2 +github "antitypical/Result" ~> 0.4.2 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index cfc8cef..0d99185 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "robrix/Box" "1.2.2" -github "antitypical/Result" "0.4.2" +github "antitypical/Result" "0.4.3" diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index bd4a493..f9045c2 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit bd4a493b262b70bfcf140cb22661f048a8838b7b +Subproject commit f9045c2a1fee1af321e29eea7c633be5a6a42532 From c2c7a449d243003bee7d87e080dc55b9a3bf8c8a Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 17 May 2015 10:50:31 +0200 Subject: [PATCH 24/61] carthage now also builds for iOS, added verbose option for debugging --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 6e7c40c..1caf2fa 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ dependencies: - brew install carthage test: override: - - carthage bootstrap --platform Mac + - carthage bootstrap --verbose - xctool -reporter pretty -reporter junit:$CIRCLE_TEST_REPORTS/xcode/results-mac.xml From 9924b5536abbdb7e1892e7de9b29b81f5fef1c74 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Tue, 19 May 2015 10:01:31 +0200 Subject: [PATCH 25/61] removed unneeded get's --- BrightFutures/Future.swift | 20 +++++--------------- BrightFutures/Semaphore.swift | 12 +++++------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 281bee1..2cf49a7 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -187,37 +187,27 @@ public extension Future { /// Returns the value that the future succesfully completed with, or `nil` if the future failed or is still in progress public var value: T? { - get { - return self.result?.value - } + return self.result?.value } /// Returns the error that the future failed with, or `nil` if the future succeeded or is still in progress public var error: E? { - get { - return self.result?.error - } + return self.result?.error } /// `true` if the future completed with success, or `false` otherwise public var isSuccess: Bool { - get { - return result?.analysis(ifSuccess: { _ in return true }, ifFailure: { _ in return false }) ?? false - } + return result?.analysis(ifSuccess: { _ in return true }, ifFailure: { _ in return false }) ?? false } /// `true` if the future failed, or `false` otherwise public var isFailure: Bool { - get { - return !isSuccess - } + return !isSuccess } /// `true` if the future completed (either `isSuccess` or `isFailure` will be `true`) public var isCompleted: Bool { - get { - return self.result != nil - } + return self.result != nil } } diff --git a/BrightFutures/Semaphore.swift b/BrightFutures/Semaphore.swift index cba1ac9..bb69abd 100644 --- a/BrightFutures/Semaphore.swift +++ b/BrightFutures/Semaphore.swift @@ -31,13 +31,11 @@ public enum TimeInterval { /// Returns the `dispatch_time_t` representation of this interval public var dispatchTime: dispatch_time_t { - get { - switch self { - case .Forever: - return DISPATCH_TIME_FOREVER - case .In(let interval): - return dispatch_time(DISPATCH_TIME_NOW, Int64(interval * NSTimeInterval(NSEC_PER_SEC))) - } + switch self { + case .Forever: + return DISPATCH_TIME_FOREVER + case .In(let interval): + return dispatch_time(DISPATCH_TIME_NOW, Int64(interval * NSTimeInterval(NSEC_PER_SEC))) } } } From 58f45516b6b681327eda19df7268a9545ebdb05a Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Tue, 19 May 2015 10:29:14 +0200 Subject: [PATCH 26/61] Implemented BrightFuturesError.External error instead of EitherError --- BrightFutures/Error.swift | 27 ++++----------------------- BrightFutures/Future.swift | 18 ++++++++---------- BrightFutures/FutureUtils.swift | 10 +++++----- 3 files changed, 17 insertions(+), 38 deletions(-) diff --git a/BrightFutures/Error.swift b/BrightFutures/Error.swift index ae02b8b..fbea0fb 100644 --- a/BrightFutures/Error.swift +++ b/BrightFutures/Error.swift @@ -28,35 +28,14 @@ extension NSError: ErrorType { } } -public enum EitherError : ErrorType { - case Left(Box) - case Right(Box) - - public static func left(error: E1) -> EitherError { - return .Left(Box(error)) - } - - public static func right(error: E2) -> EitherError { - return .Right(Box(error)) - } - - public var nsError: NSError { - switch self { - case .Left(let boxedError): - return boxedError.value.nsError - case .Right(let boxedError): - return boxedError.value.nsError - } - } -} - public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" /// An enum representing every possible error code for errors returned by BrightFutures -public enum BrightFuturesError: ErrorType, Equatable { +public enum BrightFuturesError: ErrorType { case NoSuchElement case InvalidationTokenInvalidated + case External(error: ErrorType) public var nsError: NSError { switch self { @@ -64,6 +43,8 @@ public enum BrightFuturesError: ErrorType, Equatable { return NSError(domain: BrightFuturesErrorDomain, code: 0, userInfo: nil) case .InvalidationTokenInvalidated: return NSError(domain: BrightFuturesErrorDomain, code: 1, userInfo: nil) + case .External(let error): + return error.nsError } } diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 2cf49a7..04998b2 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -472,14 +472,14 @@ public extension Future { /// (i.e. the given closure returns `true` when invoked with the success value) or an error with code /// `ErrorCode.NoSuchElement` if the test failed. /// If this future fails, the returned future fails with the same error. - public func filter(p: (T -> Bool)) -> Future> { - return self.mapError { error -> EitherError in - return EitherError.left(error) - }.flatMap { value -> Result> in + public func filter(p: (T -> Bool)) -> Future { + return self.mapError { error -> BrightFuturesError in + return BrightFuturesError.External(error: error) + }.flatMap { value -> Result in if p(value) { return Result.success(value) } else { - return Result.failure(EitherError.right(.NoSuchElement)) + return Result.failure(.NoSuchElement) } } } @@ -490,14 +490,12 @@ public extension Future { */ public extension Future { - func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { + func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future { return firstCompletedOf([ self.mapError { - EitherError.left($0) + BrightFuturesError.External(error: $0) }, - promoteValue(token.future.mapError { - EitherError.right($0) - }) + promoteValue(token.future) ] ) } diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index 327cb07..898e5a7 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -68,7 +68,7 @@ public func sequence>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future` -public func find>(seq: S, p: T -> Bool) -> Future> { +public func find>(seq: S, p: T -> Bool) -> Future { return find(seq, context: Queue.global.context, p) } @@ -77,16 +77,16 @@ public func find /// If any of the futures in the given sequence fail, the returned future fails with the /// error of the first failed future in the sequence. /// If no futures in the sequence pass the test, a future with an error with NoSuchElement is returned. -public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future> { +public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future { return sequence(seq).mapError { error in - return EitherError.left(error) - }.flatMap(context: c) { val -> Result> in + return BrightFuturesError.External(error: error) + }.flatMap(context: c) { val -> Result in for elem in val { if (p(elem)) { return Result(value: elem) } } - return Result(error: EitherError.right(.NoSuchElement)) + return Result(error: .NoSuchElement) } } From 53468d7982ca152d9f0c38cf97538f8d8cdda720 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 21 May 2015 17:55:16 +0200 Subject: [PATCH 27/61] Moved runCallbacks to didSet of result --- BrightFutures/Future.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 04998b2..55aba1c 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -85,7 +85,15 @@ public class Future { public typealias FailureCallback = E -> () /// The result of the operation this Future represents or `nil` if it is not yet completed - public internal(set) var result: Result? = nil + public internal(set) var result: Result? = nil { + willSet { + assert(result == nil) + } + + didSet { + runCallbacks() + } + } /// This queue is used for all callback related administrative tasks /// to prevent that a callback is added to a completed future and never @@ -153,7 +161,6 @@ internal extension Future { } self.result = Result(value: value) - self.runCallbacks() return true; }; } @@ -176,7 +183,6 @@ internal extension Future { } self.result = Result(error: error) - self.runCallbacks() return true; }; } From b8150e6fbaa94be13c615a63b2ee48ff0bd8259d Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Thu, 21 May 2015 22:21:41 +0200 Subject: [PATCH 28/61] added tests for forceType(), found & fixed bug --- BrightFutures/Future.swift | 10 +++++--- BrightFuturesTests/BrightFuturesTests.swift | 26 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 55aba1c..7141751 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -264,12 +264,16 @@ public extension Future { /// Returns a new future with the new type. /// The value or error will be casted using `as!` and may cause a runtime error public func forceType() -> Future { - return self.map { $0 as! U }.mapError { $0 as! E1} + return self.map(context: ImmediateExecutionContext) { + $0 as! U + }.mapError(context: ImmediateExecutionContext) { + $0 as! E1 + } } /// Returns a new future that completes with this future, but returns Void on success public func asVoid() -> Future { - return self.map { _ in return () } + return self.map(context: ImmediateExecutionContext) { _ in return () } } } @@ -415,7 +419,7 @@ public extension Future { public func mapError(context c: ExecutionContext, f: E -> E1) -> Future { let p = Promise() - self.onComplete { result in + self.onComplete(context:c) { result in result.analysis( ifSuccess: p.success , ifFailure: { p.failure(f($0)) }) diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index d99516d..05cf6ce 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -126,6 +126,32 @@ extension BrightFuturesTests { XCTAssert(!f.isCompleted) } + func testForceTypeSuccess() { + let f: Future = Future.succeeded(NSTimeInterval(3.0)) + let f1: Future = f.forceType() + + XCTAssertEqual(NSTimeInterval(3.0), f1.result!.value!, "Should be a time interval") + } + + func testForceTypeFailure() { + class TestError: ErrorType { + var nsError: NSError { + return NSError(domain: "", code: 1, userInfo: nil) + } + } + + class SubError: TestError { + override var nsError: NSError { + return NSError(domain: "", code: 2, userInfo: nil) + } + } + + let f: Future = Future.failed(SubError()) + let f1: Future = f.forceType() + + XCTAssertEqual(f1.result!.error!.nsError.code, 2, "Should be a SubError") + } + func testControlFlowSyntax() { let f = future { _ in From 1539c4f9090b43c5c0f172a59eb3aeed55e06dce Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:06:29 +0200 Subject: [PATCH 29/61] BrightFuturesError is now parametrized with the client error it can contain --- BrightFutures/Error.swift | 6 +++--- BrightFutures/Future.swift | 25 +++++++++++++++++++------ BrightFutures/FutureUtils.swift | 6 +++--- BrightFutures/InvalidationToken.swift | 6 +++--- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/BrightFutures/Error.swift b/BrightFutures/Error.swift index fbea0fb..4dee77e 100644 --- a/BrightFutures/Error.swift +++ b/BrightFutures/Error.swift @@ -31,11 +31,11 @@ extension NSError: ErrorType { public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" /// An enum representing every possible error code for errors returned by BrightFutures -public enum BrightFuturesError: ErrorType { +public enum BrightFuturesError: ErrorType { case NoSuchElement case InvalidationTokenInvalidated - case External(error: ErrorType) + case External(error: E) public var nsError: NSError { switch self { @@ -48,4 +48,4 @@ public enum BrightFuturesError: ErrorType { } } -} \ No newline at end of file +} diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 7141751..fe393df 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -482,10 +482,10 @@ public extension Future { /// (i.e. the given closure returns `true` when invoked with the success value) or an error with code /// `ErrorCode.NoSuchElement` if the test failed. /// If this future fails, the returned future fails with the same error. - public func filter(p: (T -> Bool)) -> Future { - return self.mapError { error -> BrightFuturesError in + public func filter(p: (T -> Bool)) -> Future> { + return self.mapError { error -> BrightFuturesError in return BrightFuturesError.External(error: error) - }.flatMap { value -> Result in + }.flatMap { value -> Result> in if p(value) { return Result.success(value) } else { @@ -500,12 +500,12 @@ public extension Future { */ public extension Future { - func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future { + func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { return firstCompletedOf([ self.mapError { - BrightFuturesError.External(error: $0) + BrightFuturesError.External(error: $0) }, - promoteValue(token.future) + promoteError(promoteValue(token.future)) ] ) } @@ -585,6 +585,19 @@ public func promoteError(future: Future) -> Future { return future.mapError { $0 as! E } // future will never fail, so this map block will never get called } +public func promoteError(future: Future>) -> Future> { + return future.mapError { err in + switch err { + case .NoSuchElement: + return BrightFuturesError.NoSuchElement + case .InvalidationTokenInvalidated: + return BrightFuturesError.InvalidationTokenInvalidated + case .External(let err): + fatalError("Encountered BrightFuturesError.External with NoError, which should be impossible") + } + } +} + /// If a future has this as its value type, it will never complete with success public enum NoValue { } diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index 898e5a7..d19d96a 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -68,7 +68,7 @@ public func sequence>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future` -public func find>(seq: S, p: T -> Bool) -> Future { +public func find>(seq: S, p: T -> Bool) -> Future> { return find(seq, context: Queue.global.context, p) } @@ -77,10 +77,10 @@ public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future { +public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future> { return sequence(seq).mapError { error in return BrightFuturesError.External(error: error) - }.flatMap(context: c) { val -> Result in + }.flatMap(context: c) { val -> Result> in for elem in val { if (p(elem)) { return Result(value: elem) diff --git a/BrightFutures/InvalidationToken.swift b/BrightFutures/InvalidationToken.swift index a262532..1328408 100644 --- a/BrightFutures/InvalidationToken.swift +++ b/BrightFutures/InvalidationToken.swift @@ -16,7 +16,7 @@ public protocol InvalidationTokenType { var isInvalid : Bool { get } - var future: Future { get } + var future: Future> { get } var context: ExecutionContext { get } } @@ -29,7 +29,7 @@ public protocol ManualInvalidationTokenType : InvalidationTokenType { /// A default invalidation token implementation public class InvalidationToken : ManualInvalidationTokenType { - let promise = Promise() + let promise = Promise>() /// The synchronous context on which the invalidation and callbacks are executed public let context = toContext(Semaphore(value: 1)) @@ -43,7 +43,7 @@ public class InvalidationToken : ManualInvalidationTokenType { } /// The future will fail with an error with code `InvalidationTokenInvalid` when the token invalidates - public var future: Future { + public var future: Future> { return self.promise.future } From 9cc73cf6bcf7ac7f39b33ec9eda06d2188ac05ae Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:07:06 +0200 Subject: [PATCH 30/61] Updated license header in Error.swift --- BrightFutures/Error.swift | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/BrightFutures/Error.swift b/BrightFutures/Error.swift index 4dee77e..6530567 100644 --- a/BrightFutures/Error.swift +++ b/BrightFutures/Error.swift @@ -1,10 +1,24 @@ +// The MIT License (MIT) // -// Error.swift -// BrightFutures +// Copyright (c) 2014 Thomas Visser // -// Created by Thomas Visser on 14/05/15. -// Copyright (c) 2015 Thomas Visser. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. import Foundation import Box From afb8a5d2c4ffe39a13e82a8f3117d80b626ddc61 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:07:28 +0200 Subject: [PATCH 31/61] renamed Error.swift to Errors.swift --- BrightFutures.xcodeproj/project.pbxproj | 12 ++++++------ BrightFutures/{Error.swift => Errors.swift} | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename BrightFutures/{Error.swift => Errors.swift} (100%) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 86324b2..8bbcae6 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -41,8 +41,8 @@ E9F496E71B05086300F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E9F496EF1B052F1400F82839 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Error.swift */; }; - E9F496F01B052F1400F82839 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Error.swift */; }; + E9F496EF1B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; + E9F496F01B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -111,7 +111,7 @@ E9DF0832194470190083F7F2 /* Future.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Future.swift; sourceTree = ""; }; E9DF0833194470190083F7F2 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = ""; }; E9DF0834194470190083F7F2 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; - E9F496EE1B052F1400F82839 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; + E9F496EE1B052F1400F82839 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -211,7 +211,7 @@ E9DF0818194470060083F7F2 /* Supporting Files */, E979E5F31975B1AA007FE914 /* FutureUtils.swift */, E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */, - E9F496EE1B052F1400F82839 /* Error.swift */, + E9F496EE1B052F1400F82839 /* Errors.swift */, ); path = BrightFutures; sourceTree = ""; @@ -402,7 +402,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E9F496F01B052F1400F82839 /* Error.swift in Sources */, + E9F496F01B052F1400F82839 /* Errors.swift in Sources */, E9D45B1B1AF00668000F6CA7 /* Future.swift in Sources */, E9D45B1E1AF00668000F6CA7 /* Queue.swift in Sources */, E9D45B1A1AF00668000F6CA7 /* ExecutionContext.swift in Sources */, @@ -428,7 +428,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E9F496EF1B052F1400F82839 /* Error.swift in Sources */, + E9F496EF1B052F1400F82839 /* Errors.swift in Sources */, E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */, E9DF0836194470190083F7F2 /* Future.swift in Sources */, E9DF0835194470190083F7F2 /* ExecutionContext.swift in Sources */, diff --git a/BrightFutures/Error.swift b/BrightFutures/Errors.swift similarity index 100% rename from BrightFutures/Error.swift rename to BrightFutures/Errors.swift From 4ab48cf74b074b708cc346289dcb12bd6ff3ba7e Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:07:43 +0200 Subject: [PATCH 32/61] changed order of files in project --- BrightFutures.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 8bbcae6..8a09e58 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -208,10 +208,10 @@ E9DF0832194470190083F7F2 /* Future.swift */, E9DF0833194470190083F7F2 /* Promise.swift */, E9DF0834194470190083F7F2 /* Queue.swift */, - E9DF0818194470060083F7F2 /* Supporting Files */, E979E5F31975B1AA007FE914 /* FutureUtils.swift */, E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */, E9F496EE1B052F1400F82839 /* Errors.swift */, + E9DF0818194470060083F7F2 /* Supporting Files */, ); path = BrightFutures; sourceTree = ""; From be808e941f5d826f9f91c75248279e4cd23660b9 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:28:29 +0200 Subject: [PATCH 33/61] Updated documentation --- BrightFutures/Errors.swift | 8 +++++++- BrightFutures/Future.swift | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BrightFutures/Errors.swift b/BrightFutures/Errors.swift index 6530567..7e139cd 100644 --- a/BrightFutures/Errors.swift +++ b/BrightFutures/Errors.swift @@ -28,6 +28,8 @@ public protocol ErrorType { var nsError: NSError { get } } +/// Can be used as the value type of a `Future` or `Result` to indicate it can never fail. +/// This is guaranteed by the type system, because `NoError` has no possible values and thus cannot be created. public enum NoError {} extension NoError: ErrorType { @@ -36,15 +38,19 @@ extension NoError: ErrorType { } } +/// Ensures `NSError` conforms to `ErrorType` extension NSError: ErrorType { public var nsError: NSError { return self } } +/// The name of the domain that will be used when returning `NSError` representations of `BrightFuturesError` instances public let BrightFuturesErrorDomain = "nl.thomvis.BrightFutures" -/// An enum representing every possible error code for errors returned by BrightFutures +/// An enum representing every possible error for errors returned by BrightFutures +/// A `BrightFuturesError` can also wrap an external error (e.g. coming from a user defined future) +/// in its `External` case. `BrightFuturesError` has the type of the external error as its generic parameter. public enum BrightFuturesError: ErrorType { case NoSuchElement diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index fe393df..9b3d48a 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -581,10 +581,19 @@ public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> F }) } +/// 'promotes' a `Future` with error type `NoError` to a `Future` with an error type of choice. +/// This allows the `Future` to be used more easily in combination with other futures +/// for operations such as `sequence` and `firstCompletedOf` +/// This is a safe operation, because a `Future` with error type `NoError` is guaranteed never to fail public func promoteError(future: Future) -> Future { return future.mapError { $0 as! E } // future will never fail, so this map block will never get called } +/// 'promotes' a `Future` with error type `BrightFuturesError` to a `Future` with an +/// `BrightFuturesError` error type where `E` can be any type conforming to `ErrorType`. +/// This allows the `Future` to be used more easily in combination with other futures +/// for operations such as `sequence` and `firstCompletedOf` +/// This is a safe operation, because a `BrightFuturesError` will never be `.External` public func promoteError(future: Future>) -> Future> { return future.mapError { err in switch err { @@ -598,9 +607,14 @@ public func promoteError(future: Future>) - } } -/// If a future has this as its value type, it will never complete with success +/// Can be used as the value type of a `Future` or `Result` to indicate it can never be a success. +/// This is guaranteed by the type system, because `NoValue` has no possible values and thus cannot be created. public enum NoValue { } +/// 'promotes' a `Future` with value type `NoValue` to a `Future` with a value type of choice. +/// This allows the `Future` to be used more easily in combination with other futures +/// for operations such as `sequence` and `firstCompletedOf` +/// This is a safe operation, because a `Future` with value type `NoValue` is guaranteed never to succeed public func promoteValue(future: Future) -> Future { return future.map { $0 as! T } // future will never succeed, so this map block will never get called } From 8db49dd812f9975a60933dbd385940e759a6f5ee Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:29:01 +0200 Subject: [PATCH 34/61] promoteError and promoteValue now use the ImmediateExecutionContext --- BrightFutures/Future.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 9b3d48a..9a1f458 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -586,7 +586,7 @@ public func ?? (lhs: Future, @autoclosure(escaping) rhs: () -> F /// for operations such as `sequence` and `firstCompletedOf` /// This is a safe operation, because a `Future` with error type `NoError` is guaranteed never to fail public func promoteError(future: Future) -> Future { - return future.mapError { $0 as! E } // future will never fail, so this map block will never get called + return future.mapError(context: ImmediateExecutionContext) { $0 as! E } // future will never fail, so this map block will never get called } /// 'promotes' a `Future` with error type `BrightFuturesError` to a `Future` with an @@ -595,7 +595,7 @@ public func promoteError(future: Future) -> Future { /// for operations such as `sequence` and `firstCompletedOf` /// This is a safe operation, because a `BrightFuturesError` will never be `.External` public func promoteError(future: Future>) -> Future> { - return future.mapError { err in + return future.mapError(context: ImmediateExecutionContext) { err in switch err { case .NoSuchElement: return BrightFuturesError.NoSuchElement @@ -616,5 +616,5 @@ public enum NoValue { } /// for operations such as `sequence` and `firstCompletedOf` /// This is a safe operation, because a `Future` with value type `NoValue` is guaranteed never to succeed public func promoteValue(future: Future) -> Future { - return future.map { $0 as! T } // future will never succeed, so this map block will never get called + return future.map(context: ImmediateExecutionContext) { $0 as! T } // future will never succeed, so this map block will never get called } From 0294e34ebf430ed887b221d07874b47aa19c95db Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 15:33:38 +0200 Subject: [PATCH 35/61] Improved test for recover, ensuring the recover case is not evaluated when it is not needed --- BrightFuturesTests/BrightFuturesTests.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index 05cf6ce..e3b86fe 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -386,7 +386,8 @@ extension BrightFuturesTests { future { _ in 3 }.recover { _ in - 5 + XCTFail("recover task should not be executed") + return 5 }.onSuccess { value in XCTAssert(value == 3) e.fulfill() @@ -394,7 +395,13 @@ extension BrightFuturesTests { let e1 = self.expectation() - (future(3) ?? 5).onSuccess { value in + + let recov: () -> Int = { + XCTFail("recover task should not be executed") + return 5 + } + + (future(3) ?? recov()).onSuccess { value in XCTAssert(value == 3) e1.fulfill() } From fd8960ac06eee40606ad5d42118d429cd08c5fb7 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 16:09:06 +0200 Subject: [PATCH 36/61] added missing documentation, now at 100% coverage --- BrightFutures/Errors.swift | 12 +++++++++++- BrightFutures/ExecutionContext.swift | 1 + BrightFutures/Future.swift | 7 ++++++- BrightFutures/InvalidationToken.swift | 6 +++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/BrightFutures/Errors.swift b/BrightFutures/Errors.swift index 7e139cd..3103771 100644 --- a/BrightFutures/Errors.swift +++ b/BrightFutures/Errors.swift @@ -23,6 +23,8 @@ import Foundation import Box +/// To be able to use a type as an error type with BrightFutures, it needs to conform +/// to this protocol. public protocol ErrorType { /// An NSError describing this error var nsError: NSError { get } @@ -32,14 +34,21 @@ public protocol ErrorType { /// This is guaranteed by the type system, because `NoError` has no possible values and thus cannot be created. public enum NoError {} +/// Extends `NSError` to conform to `ErrorType` extension NoError: ErrorType { + + /// From `ErrorType`: an NSError describing this error. + /// Since `NoError` cannot be constructed, this property can also never be accessed. public var nsError: NSError { fatalError("Impossible to construct NoError") } } -/// Ensures `NSError` conforms to `ErrorType` +/// An extension of `NSError` to make it conform to `ErrorType` extension NSError: ErrorType { + + /// From `ErrorType`: An NSError describing this error. + /// Will return `self`. public var nsError: NSError { return self } @@ -57,6 +66,7 @@ public enum BrightFuturesError: ErrorType { case InvalidationTokenInvalidated case External(error: E) + /// From `ErrorType`: An NSError describing this error. public var nsError: NSError { switch self { case .NoSuchElement: diff --git a/BrightFutures/ExecutionContext.swift b/BrightFutures/ExecutionContext.swift index 5ed8f1a..70369bd 100644 --- a/BrightFutures/ExecutionContext.swift +++ b/BrightFutures/ExecutionContext.swift @@ -27,6 +27,7 @@ import Foundation public typealias ExecutionContext = (() -> ()) -> () public func ImmediateExecutionContext(task: () -> ()) { +/// Immediately executes the given task. No threading, no semaphores. task() } diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index 9a1f458..cbdda29 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -412,10 +412,15 @@ public extension Future { return p.future } + /// See `mapError(context c: ExecutionContext, f: E -> E1) -> Future` + /// The given closure is executed according to the default threading model (see README.md) public func mapError(f: E -> E1) -> Future { return mapError(context: executionContextForCurrentContext(), f: f) } + /// Returns a future that fails with the error returned from the given closure when it is invoked with the error + /// from this future. If this future succeeds, the returned future succeeds with the same value and the closure is not executed. + /// The closure is executed on the given context. public func mapError(context c: ExecutionContext, f: E -> E1) -> Future { let p = Promise() @@ -500,7 +505,7 @@ public extension Future { */ public extension Future { - func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { + private func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { return firstCompletedOf([ self.mapError { BrightFuturesError.External(error: $0) diff --git a/BrightFutures/InvalidationToken.swift b/BrightFutures/InvalidationToken.swift index 1328408..d5318f1 100644 --- a/BrightFutures/InvalidationToken.swift +++ b/BrightFutures/InvalidationToken.swift @@ -14,15 +14,19 @@ public let InvalidationTokenInvalid = 1 /// The type that all invalidation tokens conform to public protocol InvalidationTokenType { + /// Indicates if the token is invalid var isInvalid : Bool { get } + /// The future will fail with an error with .InvalidationTokenInvalidated when the token invalidates var future: Future> { get } + /// The synchronous context on which the invalidation and callbacks are executed var context: ExecutionContext { get } } /// The type that all invalidation tokens that can be manually invalidated conform to public protocol ManualInvalidationTokenType : InvalidationTokenType { + /// Invalidates the token func invalidate() } @@ -42,7 +46,7 @@ public class InvalidationToken : ManualInvalidationTokenType { return promise.future.isCompleted } - /// The future will fail with an error with code `InvalidationTokenInvalid` when the token invalidates + /// The future will fail with an error with .InvalidationTokenInvalidated when the token invalidates public var future: Future> { return self.promise.future } From 9d1eabee1e5703c1e4581f90dc20a5e269f361a8 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 23 May 2015 18:13:36 +0200 Subject: [PATCH 37/61] Marked ImmediateExecutionContext's parameter as @noescape --- BrightFutures/ExecutionContext.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrightFutures/ExecutionContext.swift b/BrightFutures/ExecutionContext.swift index 70369bd..b4c667c 100644 --- a/BrightFutures/ExecutionContext.swift +++ b/BrightFutures/ExecutionContext.swift @@ -26,8 +26,8 @@ import Foundation /// By default, an execution context can be assumed to be asynchronous unless stated otherwise public typealias ExecutionContext = (() -> ()) -> () -public func ImmediateExecutionContext(task: () -> ()) { /// Immediately executes the given task. No threading, no semaphores. +public func ImmediateExecutionContext(@noescape task: () -> ()) { task() } From 3188be1c13ca16ba8b2fe5478c32357332123602 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 30 May 2015 08:40:59 +0200 Subject: [PATCH 38/61] fixes compiler crash in Release mode by properly boxing the external error in the BrightFuturesError type (see #53) --- BrightFutures/Errors.swift | 10 +++++++--- BrightFutures/Future.swift | 4 ++-- BrightFutures/FutureUtils.swift | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/BrightFutures/Errors.swift b/BrightFutures/Errors.swift index 3103771..9b31ffe 100644 --- a/BrightFutures/Errors.swift +++ b/BrightFutures/Errors.swift @@ -64,8 +64,12 @@ public enum BrightFuturesError: ErrorType { case NoSuchElement case InvalidationTokenInvalidated - case External(error: E) + case External(Box) + public init(external: E) { + self = .External(Box(external)) + } + /// From `ErrorType`: An NSError describing this error. public var nsError: NSError { switch self { @@ -73,8 +77,8 @@ public enum BrightFuturesError: ErrorType { return NSError(domain: BrightFuturesErrorDomain, code: 0, userInfo: nil) case .InvalidationTokenInvalidated: return NSError(domain: BrightFuturesErrorDomain, code: 1, userInfo: nil) - case .External(let error): - return error.nsError + case .External(let boxedError): + return boxedError.value.nsError } } diff --git a/BrightFutures/Future.swift b/BrightFutures/Future.swift index cbdda29..61773fe 100644 --- a/BrightFutures/Future.swift +++ b/BrightFutures/Future.swift @@ -489,7 +489,7 @@ public extension Future { /// If this future fails, the returned future fails with the same error. public func filter(p: (T -> Bool)) -> Future> { return self.mapError { error -> BrightFuturesError in - return BrightFuturesError.External(error: error) + return BrightFuturesError(external: error) }.flatMap { value -> Result> in if p(value) { return Result.success(value) @@ -508,7 +508,7 @@ public extension Future { private func firstCompletedOfSelfAndToken(token: InvalidationTokenType) -> Future> { return firstCompletedOf([ self.mapError { - BrightFuturesError.External(error: $0) + BrightFuturesError(external: $0) }, promoteError(promoteValue(token.future)) ] diff --git a/BrightFutures/FutureUtils.swift b/BrightFutures/FutureUtils.swift index d19d96a..9186b61 100644 --- a/BrightFutures/FutureUtils.swift +++ b/BrightFutures/FutureUtils.swift @@ -79,7 +79,7 @@ public func find>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future> { return sequence(seq).mapError { error in - return BrightFuturesError.External(error: error) + return BrightFuturesError(external: error) }.flatMap(context: c) { val -> Result> in for elem in val { if (p(elem)) { From 8a32709ef2f94033cb5f5aa7b970d55a70b7f9d3 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 30 May 2015 08:43:48 +0200 Subject: [PATCH 39/61] removed test scheme, not needed --- .../xcschemes/BrightFuturesTests-iOS.xcscheme | 96 ------------------- 1 file changed, 96 deletions(-) delete mode 100644 BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFuturesTests-iOS.xcscheme diff --git a/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFuturesTests-iOS.xcscheme b/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFuturesTests-iOS.xcscheme deleted file mode 100644 index 1a172fa..0000000 --- a/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFuturesTests-iOS.xcscheme +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 1bd50703d87b37b8167cb42be4d046c0fd646387 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 30 May 2015 08:44:01 +0200 Subject: [PATCH 40/61] work in progress of the 2.0 migration guide --- Documentation/Migration_2.0.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/Migration_2.0.md diff --git a/Documentation/Migration_2.0.md b/Documentation/Migration_2.0.md new file mode 100644 index 0000000..754fd8e --- /dev/null +++ b/Documentation/Migration_2.0.md @@ -0,0 +1,31 @@ +BrightFutures 2.0 has two new dependencies: Result and Box. If you're using CocoaPods, `pod update` should automatically integrate it into your project. If you're using Carthage, after running `carthage update`, you need to add `Result.framework` and `Box.framework` to your target like you have also done for `BrightFutures.framework`. + +In files where you're using `Result` or `Box`, you'll also need to add an import statement for the respecive frameworks. If you fail to do this, you will see errors like "Use of undeclared type 'Result'". + +If you see error messages around `import Result` or `import Box`, the new dependencies have not yet been integrated correctly. + +`Future` and `Result` in BrightFutures 1.0 have only one generic parameter: the value type. In BrightFutures 2.0, both types have gained a second generic parameter: the error type. This removes the dependency on `NSError`. If you want to continue to use `NSError`, this means that you'll have to go through your code and update the occurrences of `Future` and `Result` + +Examples: + +```swift +// BrightFutures 1.0: +`func loadNextBatch() -> Future` + +// BrightFutures 2.0: +`func loadNextBatch() -> Future` +``` + +For people that have been using an enum to represent all possible errors, you can now use that error type directly in `Future` and `Result` types without translating it to a `NSError` first. + +If you have been using `Future` in cases where you know the future will never succeed, you can now also use the `NoValue` type. `NoValue` is an enum with no cases, meaning it cannot be initiated. This offers a strong guarantee that a `Future` will never be able to succeed. The `InvalidationToken` uses this. + +If you have futures that you know can never fail, consider using the `NoError` as the error type. Like `NoValue`, `NoError` cannot be instantiated. + +The easiest way to create `Result` instances is through its two constructors. You won't need to use `Box` then: + +```swift +Result(success: 1314) // a Result.Success +Result(error: .DeserializationFailed(object: json) // a Result.Failure +``` + From 2341837da4be8854d49224e70d029a5e188c741c Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 31 May 2015 20:32:09 +0200 Subject: [PATCH 41/61] added workspace, tweaked dependency set-up --- BrightFutures.xcodeproj/project.pbxproj | 72 +++++++------------ .../xcshareddata/BrightFutures.xccheckout | 4 +- .../contents.xcworkspacedata | 13 ++++ .../xcshareddata/BrightFutures.xccheckout | 65 +++++++++++++++++ 4 files changed, 104 insertions(+), 50 deletions(-) create mode 100644 BrightFutures.xcworkspace/contents.xcworkspacedata create mode 100644 BrightFutures.xcworkspace/xcshareddata/BrightFutures.xccheckout diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 8a09e58..fc59bfd 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -7,12 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - B31A24291B02691A0016AE7A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; }; - B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; }; E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */; }; E907D1DE1A6AE4A000AB8075 /* Semaphore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */; }; - E92B90781B088CBF005EDB6C /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E92B90791B088CC2005EDB6C /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E90E9D581B1B8A3600A8F9F3 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E90E9D571B1B8A3600A8F9F3 /* Box.framework */; }; + E90E9D5E1B1B8A7200A8F9F3 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E90E9D591B1B8A4100A8F9F3 /* Box.framework */; }; + E90E9D601B1B8A9B00A8F9F3 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E90E9D5F1B1B8A9B00A8F9F3 /* Result.framework */; }; + E90E9D621B1B8AA500A8F9F3 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E90E9D611B1B8AA500A8F9F3 /* Result.framework */; }; E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9319EA31A397D2C00A0604A /* QueueTests.swift */; }; E979E5F41975B1AA007FE914 /* FutureUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E979E5F31975B1AA007FE914 /* FutureUtils.swift */; }; E9D45B0B1AF00659000F6CA7 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; }; @@ -37,10 +37,6 @@ E9DF0838194470190083F7F2 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0834194470190083F7F2 /* Queue.swift */; }; E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9DF0815194470060083F7F2 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E9F496E61B05086100F82839 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24221B0268F80016AE7A /* Result.framework */; }; - E9F496E71B05086300F82839 /* Box.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B31A24231B0268F80016AE7A /* Box.framework */; }; - E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24271B02691A0016AE7A /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B31A24281B02691A0016AE7A /* Box.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496EF1B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; E9F496F01B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; /* End PBXBuildFile section */ @@ -70,8 +66,6 @@ dstSubfolderSpec = 10; files = ( E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */, - E92B90781B088CBF005EDB6C /* Result.framework in CopyFiles */, - E92B90791B088CC2005EDB6C /* Box.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -82,20 +76,18 @@ dstSubfolderSpec = 10; files = ( E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */, - E9F496EC1B05089F00F82839 /* Result.framework in CopyFiles */, - E9F496ED1B0508A100F82839 /* Box.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - B31A24221B0268F80016AE7A /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; }; - B31A24231B0268F80016AE7A /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Build/iOS/Box.framework; sourceTree = ""; }; - B31A24271B02691A0016AE7A /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; }; - B31A24281B02691A0016AE7A /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Build/Mac/Box.framework; sourceTree = ""; }; E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = ""; }; E907D1DD1A6AE4A000AB8075 /* Semaphore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Semaphore.swift; sourceTree = ""; }; + E90E9D571B1B8A3600A8F9F3 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = Carthage/Checkouts/Result/Carthage/Checkouts/Box/build/Debug/Box.framework; sourceTree = ""; }; + E90E9D591B1B8A4100A8F9F3 /* Box.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Box.framework; path = "../../../Library/Developer/Xcode/DerivedData/BrightFutures-brlkgomqpbjfnpbdastdlmrhcaoc/Build/Products/Debug/Box.framework"; sourceTree = ""; }; + E90E9D5F1B1B8A9B00A8F9F3 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = "Carthage/Checkouts/Result/build/Debug-iphoneos/Result.framework"; sourceTree = ""; }; + E90E9D611B1B8AA500A8F9F3 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Checkouts/Result/build/Debug/Result.framework; sourceTree = ""; }; E9319EA31A397D2C00A0604A /* QueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueueTests.swift; sourceTree = ""; }; E979E5F31975B1AA007FE914 /* FutureUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FutureUtils.swift; sourceTree = ""; }; E9D45B001AF00659000F6CA7 /* BrightFutures.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BrightFutures.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -119,8 +111,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B31A24291B02691A0016AE7A /* Result.framework in Frameworks */, - B31A242A1B02691A0016AE7A /* Box.framework in Frameworks */, + E90E9D621B1B8AA500A8F9F3 /* Result.framework in Frameworks */, + E90E9D581B1B8A3600A8F9F3 /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -136,8 +128,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E9F496E61B05086100F82839 /* Result.framework in Frameworks */, - E9F496E71B05086300F82839 /* Box.framework in Frameworks */, + E90E9D601B1B8A9B00A8F9F3 /* Result.framework in Frameworks */, + E90E9D5E1B1B8A7200A8F9F3 /* Box.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -152,37 +144,37 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - B31A24261B0269060016AE7A /* Frameworks */ = { + E90E9D5B1B1B8A4F00A8F9F3 /* Frameworks */ = { isa = PBXGroup; children = ( - B31A242B1B0269280016AE7A /* Mac */, - B31A242C1B02692E0016AE7A /* iOS */, + E90E9D5D1B1B8A5B00A8F9F3 /* Mac */, + E90E9D5C1B1B8A5400A8F9F3 /* iOS */, ); name = Frameworks; sourceTree = ""; }; - B31A242B1B0269280016AE7A /* Mac */ = { + E90E9D5C1B1B8A5400A8F9F3 /* iOS */ = { isa = PBXGroup; children = ( - B31A24271B02691A0016AE7A /* Result.framework */, - B31A24281B02691A0016AE7A /* Box.framework */, + E90E9D5F1B1B8A9B00A8F9F3 /* Result.framework */, + E90E9D591B1B8A4100A8F9F3 /* Box.framework */, ); - name = Mac; + name = iOS; sourceTree = ""; }; - B31A242C1B02692E0016AE7A /* iOS */ = { + E90E9D5D1B1B8A5B00A8F9F3 /* Mac */ = { isa = PBXGroup; children = ( - B31A24221B0268F80016AE7A /* Result.framework */, - B31A24231B0268F80016AE7A /* Box.framework */, + E90E9D611B1B8AA500A8F9F3 /* Result.framework */, + E90E9D571B1B8A3600A8F9F3 /* Box.framework */, ); - name = iOS; + name = Mac; sourceTree = ""; }; E9DF080B194470060083F7F2 = { isa = PBXGroup; children = ( - B31A24261B0269060016AE7A /* Frameworks */, + E90E9D5B1B1B8A4F00A8F9F3 /* Frameworks */, E9DF0817194470060083F7F2 /* BrightFutures */, E9DF0824194470060083F7F2 /* BrightFuturesTests */, E9DF0816194470060083F7F2 /* Products */, @@ -476,10 +468,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); FRAMEWORK_VERSION = A; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -508,10 +496,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); FRAMEWORK_VERSION = A; GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = BrightFutures/Info.plist; @@ -664,10 +648,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = BrightFutures/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -686,10 +666,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = BrightFutures/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout b/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout index cc47d9a..43cd5e9 100644 --- a/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout +++ b/BrightFutures.xcodeproj/project.xcworkspace/xcshareddata/BrightFutures.xccheckout @@ -7,14 +7,14 @@ IDESourceControlProjectIdentifier 3A31C8B6-B0CD-4D79-9DFB-AE57C9B04C9F IDESourceControlProjectName - project + BrightFutures IDESourceControlProjectOriginsDictionary 93608FFF008CD82ADDFA850BB9C1630FA754586F github.com:Thomvis/BrightFutures.git IDESourceControlProjectPath - BrightFutures.xcodeproj/project.xcworkspace + BrightFutures.xcodeproj IDESourceControlProjectRelativeInstallPathDictionary 93608FFF008CD82ADDFA850BB9C1630FA754586F diff --git a/BrightFutures.xcworkspace/contents.xcworkspacedata b/BrightFutures.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1c79443 --- /dev/null +++ b/BrightFutures.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/BrightFutures.xcworkspace/xcshareddata/BrightFutures.xccheckout b/BrightFutures.xcworkspace/xcshareddata/BrightFutures.xccheckout new file mode 100644 index 0000000..95f753f --- /dev/null +++ b/BrightFutures.xcworkspace/xcshareddata/BrightFutures.xccheckout @@ -0,0 +1,65 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 3A31C8B6-B0CD-4D79-9DFB-AE57C9B04C9F + IDESourceControlProjectName + BrightFutures + IDESourceControlProjectOriginsDictionary + + 57C931977B7D2307CC013C2BD93F90CF7C676790 + https://github.com/robrix/Box.git + 93608FFF008CD82ADDFA850BB9C1630FA754586F + github.com:Thomvis/BrightFutures.git + 956D2B21DD155C49504BB67697A67F7C5351A353 + https://github.com/antitypical/Result.git + + IDESourceControlProjectPath + BrightFutures.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + 57C931977B7D2307CC013C2BD93F90CF7C676790 + ..Carthage/Checkouts/Result/Carthage/Checkouts/Box + 93608FFF008CD82ADDFA850BB9C1630FA754586F + .. + 956D2B21DD155C49504BB67697A67F7C5351A353 + ..Carthage/Checkouts/Result + + IDESourceControlProjectURL + github.com:Thomvis/BrightFutures.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 93608FFF008CD82ADDFA850BB9C1630FA754586F + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 57C931977B7D2307CC013C2BD93F90CF7C676790 + IDESourceControlWCCName + Box + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 93608FFF008CD82ADDFA850BB9C1630FA754586F + IDESourceControlWCCName + BrightFutures + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 956D2B21DD155C49504BB67697A67F7C5351A353 + IDESourceControlWCCName + Result + + + + From 37f836bd55346460de137b870c7a425eb1d07827 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 31 May 2015 21:22:00 +0200 Subject: [PATCH 42/61] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bf3b6fd..df75cb2 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ BrightFutures ============= +### Please note you are currently on the 2.0-development branch. 2.0 will be the next major version and until it is released, breaking changes might occur. See the [Migration guide](Documentation/Migration_2.0.md). + How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven functional concepts (i.e. [futures and promises](http://en.wikipedia.org/wiki/Futures_and_promises)) to provide a powerful alternative to completion blocks and `NSErrorPointer`s. From 54ff87cd6a0fdb0c2e341c1c59eedcda764f43aa Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 31 May 2015 21:29:39 +0200 Subject: [PATCH 43/61] Update README.md --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index df75cb2..131b23e 100755 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ +### Warning + +You are currently on the 2.0-development branch. + +- 2.0 will be the next major version +- Until it is released, breaking changes might occur +- PR #51 tracks the progress +- See the [Migration guide](Documentation/Migration_2.0.md) +- The README has not yet been updated + BrightFutures ============= -### Please note you are currently on the 2.0-development branch. 2.0 will be the next major version and until it is released, breaking changes might occur. See the [Migration guide](Documentation/Migration_2.0.md). - How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven functional concepts (i.e. [futures and promises](http://en.wikipedia.org/wiki/Futures_and_promises)) to provide a powerful alternative to completion blocks and `NSErrorPointer`s. From c595505eae00dbcb7deb779a57fa24e27e0846b9 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sun, 31 May 2015 21:34:57 +0200 Subject: [PATCH 44/61] updated podspec --- BrightFutures.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BrightFutures.podspec b/BrightFutures.podspec index ec37f6b..6c4deb8 100644 --- a/BrightFutures.podspec +++ b/BrightFutures.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrightFutures' - s.version = '1.0.1' + s.version = '2.0.0-beta.1' s.license = 'MIT' s.summary = 'A simple Futures & Promises library for iOS and OS X written in Swift' s.homepage = 'https://github.com/Thomvis/BrightFutures' From 401c96e8384a5e28d50d569e41c3b1443c5b2232 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Wed, 3 Jun 2015 23:11:44 +0200 Subject: [PATCH 45/61] added missing tests for Errors --- BrightFutures.xcodeproj/project.pbxproj | 6 +++ BrightFuturesTests/ErrorsTests.swift | 67 +++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 BrightFuturesTests/ErrorsTests.swift diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index fc59bfd..5b51cf4 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ E9DF0836194470190083F7F2 /* Future.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0832194470190083F7F2 /* Future.swift */; }; E9DF0837194470190083F7F2 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0833194470190083F7F2 /* Promise.swift */; }; E9DF0838194470190083F7F2 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0834194470190083F7F2 /* Queue.swift */; }; + E9E422C61B1FA28F0000558F /* ErrorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C51B1FA28F0000558F /* ErrorsTests.swift */; }; + E9E422C71B1FA28F0000558F /* ErrorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C51B1FA28F0000558F /* ErrorsTests.swift */; }; E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9DF0815194470060083F7F2 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496EF1B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; @@ -103,6 +105,7 @@ E9DF0832194470190083F7F2 /* Future.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Future.swift; sourceTree = ""; }; E9DF0833194470190083F7F2 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = ""; }; E9DF0834194470190083F7F2 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + E9E422C51B1FA28F0000558F /* ErrorsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorsTests.swift; sourceTree = ""; }; E9F496EE1B052F1400F82839 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -224,6 +227,7 @@ E9319EA31A397D2C00A0604A /* QueueTests.swift */, E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */, E9D923AD1A6CE69B00CADD9F /* InvalidationTokenTests.swift */, + E9E422C51B1FA28F0000558F /* ErrorsTests.swift */, ); path = BrightFuturesTests; sourceTree = ""; @@ -409,6 +413,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E9E422C71B1FA28F0000558F /* ErrorsTests.swift in Sources */, E9D45B241AF00676000F6CA7 /* InvalidationTokenTests.swift in Sources */, E9D45B211AF00676000F6CA7 /* BrightFuturesTests.swift in Sources */, E9D45B221AF00676000F6CA7 /* QueueTests.swift in Sources */, @@ -435,6 +440,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E9E422C61B1FA28F0000558F /* ErrorsTests.swift in Sources */, E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */, E9D923AE1A6CE69B00CADD9F /* InvalidationTokenTests.swift in Sources */, E9DF0828194470060083F7F2 /* BrightFuturesTests.swift in Sources */, diff --git a/BrightFuturesTests/ErrorsTests.swift b/BrightFuturesTests/ErrorsTests.swift new file mode 100644 index 0000000..d9e6bd6 --- /dev/null +++ b/BrightFuturesTests/ErrorsTests.swift @@ -0,0 +1,67 @@ +// The MIT License (MIT) +// +// Copyright (c) 2014 Thomas Visser +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import XCTest +import BrightFutures + +enum TestError: ErrorType { + case JustAnError + + var nsError: NSError { + return NSError(domain: "TestError", code: 1, userInfo: nil) + } +} + +class ErrorsTests: XCTestCase { + + func testNSError() { + let error = NSError() + XCTAssert(error === error.nsError, "nsError should return itself") + } + + func testNoSuchElementError() { + let error = BrightFuturesError.NoSuchElement + XCTAssertEqual(error.nsError.domain, BrightFuturesErrorDomain) + XCTAssertEqual(error.nsError.code, 0) + } + + func testInvalidationError() { + let error = BrightFuturesError.InvalidationTokenInvalidated + XCTAssertEqual(error.nsError.domain, BrightFuturesErrorDomain) + XCTAssertEqual(error.nsError.code, 1) + } + + func testExternalError() { + let externalError = TestError.JustAnError + let error = BrightFuturesError(external: externalError) + + switch error { + case .External(let boxedError): + XCTAssertEqual(boxedError.value, TestError.JustAnError, "Should be same error") + default: + XCTFail("Should match with the external case") + } + + XCTAssertEqual(externalError.nsError, error.nsError) + } + +} \ No newline at end of file From f3a710fb0e77fde74c173bc151ee5b52392562ca Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Wed, 3 Jun 2015 23:39:40 +0200 Subject: [PATCH 46/61] improved test code coverage --- BrightFutures.xcodeproj/project.pbxproj | 6 ++ BrightFuturesTests/BrightFuturesTests.swift | 25 +++++- .../ExecutionContextTests.swift | 88 +++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 BrightFuturesTests/ExecutionContextTests.swift diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 5b51cf4..17a1ec5 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ E9DF0838194470190083F7F2 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DF0834194470190083F7F2 /* Queue.swift */; }; E9E422C61B1FA28F0000558F /* ErrorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C51B1FA28F0000558F /* ErrorsTests.swift */; }; E9E422C71B1FA28F0000558F /* ErrorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C51B1FA28F0000558F /* ErrorsTests.swift */; }; + E9E422C91B1FA5300000558F /* ExecutionContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C81B1FA5300000558F /* ExecutionContextTests.swift */; }; + E9E422CA1B1FA5300000558F /* ExecutionContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E422C81B1FA5300000558F /* ExecutionContextTests.swift */; }; E9F496E31B0397EA00F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9DF0815194470060083F7F2 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496E51B03981100F82839 /* BrightFutures.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9D45B001AF00659000F6CA7 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; E9F496EF1B052F1400F82839 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F496EE1B052F1400F82839 /* Errors.swift */; }; @@ -106,6 +108,7 @@ E9DF0833194470190083F7F2 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = ""; }; E9DF0834194470190083F7F2 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; E9E422C51B1FA28F0000558F /* ErrorsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorsTests.swift; sourceTree = ""; }; + E9E422C81B1FA5300000558F /* ExecutionContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExecutionContextTests.swift; sourceTree = ""; }; E9F496EE1B052F1400F82839 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -228,6 +231,7 @@ E9039ADC1A45DF8D000DD6F1 /* ResultTests.swift */, E9D923AD1A6CE69B00CADD9F /* InvalidationTokenTests.swift */, E9E422C51B1FA28F0000558F /* ErrorsTests.swift */, + E9E422C81B1FA5300000558F /* ExecutionContextTests.swift */, ); path = BrightFuturesTests; sourceTree = ""; @@ -417,6 +421,7 @@ E9D45B241AF00676000F6CA7 /* InvalidationTokenTests.swift in Sources */, E9D45B211AF00676000F6CA7 /* BrightFuturesTests.swift in Sources */, E9D45B221AF00676000F6CA7 /* QueueTests.swift in Sources */, + E9E422CA1B1FA5300000558F /* ExecutionContextTests.swift in Sources */, E9D45B231AF00676000F6CA7 /* ResultTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -444,6 +449,7 @@ E9039ADD1A45DF8D000DD6F1 /* ResultTests.swift in Sources */, E9D923AE1A6CE69B00CADD9F /* InvalidationTokenTests.swift in Sources */, E9DF0828194470060083F7F2 /* BrightFuturesTests.swift in Sources */, + E9E422C91B1FA5300000558F /* ExecutionContextTests.swift in Sources */, E9319EA41A397D2C00A0604A /* QueueTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index e3b86fe..bb217fb 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -23,6 +23,7 @@ import XCTest import Result import BrightFutures +import Result class BrightFuturesTests: XCTestCase { @@ -187,7 +188,7 @@ extension BrightFuturesTests { func testAutoClosure() { let names = ["Steve", "Tim"] - let f = Future.succeeded(names.count) + let f = future(names.count) let e = self.expectation() f.onSuccess { value in @@ -206,6 +207,28 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } + func testAutoClosureWithResult() { + let f = future(Result(value:2)) + let e = self.expectation() + + f.onSuccess { value in + XCTAssert(value == 2) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + + let f1 = future(Result>(error: .NoSuchElement)) + let e1 = self.expectation() + + f1.onFailure { error in + XCTAssertEqual(error.nsError, BrightFuturesError.NoSuchElement.nsError) + e1.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + func testPromise() { let p = Promise() diff --git a/BrightFuturesTests/ExecutionContextTests.swift b/BrightFuturesTests/ExecutionContextTests.swift new file mode 100644 index 0000000..426ef50 --- /dev/null +++ b/BrightFuturesTests/ExecutionContextTests.swift @@ -0,0 +1,88 @@ +// The MIT License (MIT) +// +// Copyright (c) 2014 Thomas Visser +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import XCTest +import BrightFutures + +class Counter { + var i: Int = 0 +} + +class ExecutionContextTests: XCTestCase { + + func testImmediateOnMainThreadContextOnMainThread() { + let counter = Counter() + + counter.i = 1 + + ImmediateOnMainExecutionContext { + XCTAssert(NSThread.isMainThread()) + counter.i = 2 + } + + XCTAssertEqual(counter.i, 2) + } + + func testImmediateOnMainThreadContextOnBackgroundThread() { + let e = self.expectation() + Queue.global.async { + ImmediateOnMainExecutionContext { + XCTAssert(NSThread.isMainThread()) + e.fulfill() + } + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func testDispatchQueueToContext() { + var key = "key" + let value1 = getMutablePointer("queue1") + + let queue1 = dispatch_queue_create("test1", DISPATCH_QUEUE_SERIAL) + dispatch_queue_set_specific(queue1, &key, value1, nil) + + let e1 = self.expectation() + (toContext(queue1)) { + XCTAssertEqual(dispatch_get_specific(&key), value1) + e1.fulfill() + } + + let value2 = getMutablePointer("queue2") + + let queue2 = dispatch_queue_create("test2", DISPATCH_QUEUE_CONCURRENT) + dispatch_queue_set_specific(queue2, &key, value2, nil) + + let e2 = self.expectation() + (toContext(queue2)) { + XCTAssertEqual(dispatch_get_specific(&key), value2) + e2.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func getMutablePointer (object: AnyObject) -> UnsafeMutablePointer { + return UnsafeMutablePointer(bitPattern: Word(ObjectIdentifier(object).uintValue)) + } + +} \ No newline at end of file From 0308be77b8fdef107a7cf471cb3821fb3212d2af Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Wed, 3 Jun 2015 23:54:39 +0200 Subject: [PATCH 47/61] updated CircleCI config --- circle.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 1caf2fa..eba432e 100644 --- a/circle.yml +++ b/circle.yml @@ -3,10 +3,9 @@ machine: version: "6.3.1" dependencies: pre: - - brew install carthage + - git submodule update --init --recursive test: override: - - carthage bootstrap --verbose - xctool -reporter pretty -reporter junit:$CIRCLE_TEST_REPORTS/xcode/results-mac.xml @@ -15,6 +14,6 @@ test: CODE_SIGN_IDENTITY= PROVISIONING_PROFILE= -sdk macosx - -project BrightFutures.xcodeproj + -workspace BrightFutures.xcworkspace -scheme BrightFutures-Mac build build-tests run-tests \ No newline at end of file From 64a8e7827bdad45f84f7aede88aff4f6f6f019ec Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Mon, 8 Jun 2015 11:32:20 +0200 Subject: [PATCH 48/61] updated build configuration --- BrightFutures.xcodeproj/project.pbxproj | 6 ++++-- .../xcshareddata/xcschemes/BrightFutures-Mac.xcscheme | 4 ++-- .../xcshareddata/xcschemes/BrightFutures-iOS.xcscheme | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/BrightFutures.xcodeproj/project.pbxproj b/BrightFutures.xcodeproj/project.pbxproj index 17a1ec5..069b447 100644 --- a/BrightFutures.xcodeproj/project.pbxproj +++ b/BrightFutures.xcodeproj/project.pbxproj @@ -583,7 +583,6 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -629,7 +628,6 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = 1; ENABLE_NS_ASSERTIONS = NO; @@ -656,6 +654,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; DEFINES_MODULE = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -674,6 +673,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; DEFINES_MODULE = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -689,6 +689,7 @@ E9DF082F194470060083F7F2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", @@ -708,6 +709,7 @@ E9DF0830194470060083F7F2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", diff --git a/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFutures-Mac.xcscheme b/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFutures-Mac.xcscheme index f9acc2d..fa60e8e 100644 --- a/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFutures-Mac.xcscheme +++ b/BrightFutures.xcodeproj/xcshareddata/xcschemes/BrightFutures-Mac.xcscheme @@ -22,10 +22,10 @@ + buildForAnalyzing = "NO"> + buildForAnalyzing = "NO"> Date: Fri, 26 Jun 2015 22:19:38 +0200 Subject: [PATCH 49/61] Added missing tests, == for BrightFuturesError --- BrightFutures/Errors.swift | 11 +- BrightFuturesTests/BrightFuturesTests.swift | 118 +- BrightFuturesTests/ErrorsTests.swift | 11 +- .../InvalidationTokenTests.swift | 36 + ...erage.BrightFutures_FutureUtils.swift.html | 1193 +++++++++++++++++ coverage/coverage.html | 431 ++++++ 6 files changed, 1797 insertions(+), 3 deletions(-) create mode 100644 coverage/coverage.BrightFutures_FutureUtils.swift.html create mode 100644 coverage/coverage.html diff --git a/BrightFutures/Errors.swift b/BrightFutures/Errors.swift index 9b31ffe..af49b0d 100644 --- a/BrightFutures/Errors.swift +++ b/BrightFutures/Errors.swift @@ -81,5 +81,14 @@ public enum BrightFuturesError: ErrorType { return boxedError.value.nsError } } - } + +/// Returns `true` if `left` and `right` are both of the same case ignoring .External associated value +public func ==(lhs: BrightFuturesError, rhs: BrightFuturesError) -> Bool { + switch (lhs, rhs) { + case (.NoSuchElement, .NoSuchElement): return true + case (.InvalidationTokenInvalidated, .InvalidationTokenInvalidated): return true + case (.External(let lhs), .External(let rhs)): return lhs == rhs + default: return false + } +} \ No newline at end of file diff --git a/BrightFuturesTests/BrightFuturesTests.swift b/BrightFuturesTests/BrightFuturesTests.swift index bb217fb..cfda422 100644 --- a/BrightFuturesTests/BrightFuturesTests.swift +++ b/BrightFuturesTests/BrightFuturesTests.swift @@ -134,6 +134,17 @@ extension BrightFuturesTests { XCTAssertEqual(NSTimeInterval(3.0), f1.result!.value!, "Should be a time interval") } + func testAsVoid() { + let f = future(fibonacci(10)) + + let e = self.expectation() + f.asVoid().onComplete { v in + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + func testForceTypeFailure() { class TestError: ErrorType { var nsError: NSError { @@ -229,7 +240,7 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } - func testPromise() { + func testSuccessPromise() { let p = Promise() Queue.global.async { @@ -251,6 +262,60 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } + + func testFailurePromise() { + let p = Promise() + + Queue.global.async { + p.failure(TestError.JustAnError) + } + + let e = self.expectationWithDescription("complete expectation") + + p.future.onComplete { result in + switch result { + case .Success(let val): + XCTFail("should not be success") + case .Failure(let err): + XCTAssertEqual(err.value, TestError.JustAnError) + } + + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func testPromiseCompleteWithSuccess() { + let p = Promise() + p.complete(Result(value: 2)) + + XCTAssert(p.future.isSuccess) + XCTAssert(p.future.forced()! == Result(value:2)) + } + + func testPromiseCompleteWithFailure() { + let p = Promise() + p.complete(Result(error: TestError.JustAnError)) + + XCTAssert(p.future.isFailure) + XCTAssert(p.future.forced()! == Result(error:TestError.JustAnError)) + } + + func testPromiseTrySuccessTwice() { + let p = Promise() + XCTAssert(p.trySuccess(1)) + XCTAssertFalse(p.trySuccess(2)) + XCTAssertEqual(p.future.forced()!.value!, 1) + } + + func testPromiseTryFailureTwice() { + let p = Promise() + XCTAssert(p.tryFailure(TestError.JustAnError)) + XCTAssertFalse(p.tryFailure(TestError.JustAnotherError)) + XCTAssertEqual(p.future.forced()!.error!, TestError.JustAnError) + } + func testCustomExecutionContext() { let f = future(context: ImmediateExecutionContext) { @@ -304,6 +369,18 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } + + func testPromoteErrorNoSuchElement() { + let f: Future> = promoteError(future(3).filter { _ in false }) + + let e = self.expectation() + f.onFailure { err in + XCTAssert(err == BrightFuturesError.NoSuchElement) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } } // MARK: Functional Composition @@ -403,6 +480,28 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } + func testRecover() { + let e = self.expectation() + let f = Future.failed(TestError.JustAnError).recover { _ in + return 3 + }.onSuccess { val in + XCTAssertEqual(val, 3) + e.fulfill() + } + + let recov: () -> Int = { + return 5 + } + + let e1 = self.expectation() + (Future.failed(TestError.JustAnError) ?? recov()).onSuccess { value in + XCTAssert(value == 5) + e1.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + func testSkippedRecover() { let e = self.expectation() @@ -557,6 +656,18 @@ extension BrightFuturesTests { self.waitForExpectationsWithTimeout(2, handler: nil) } + + func testFilterFailedFuture() { + let f = Future.failed(TestError.JustAnError) + + let e = self.expectation() + f.filter { _ in false }.onFailure { error in + XCTAssert(error == BrightFuturesError(external: TestError.JustAnError)) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } func testForcedFuture() { var x = 10 @@ -578,6 +689,11 @@ extension BrightFuturesTests { XCTAssert(f.forced(0.5) != nil) } + func testForcingCompletedFuture() { + let f = Future.succeeded(1) + XCTAssertEqual(f.forced()!.value!, 1) + } + func testFlatMap() { let e = self.expectation() diff --git a/BrightFuturesTests/ErrorsTests.swift b/BrightFuturesTests/ErrorsTests.swift index d9e6bd6..6857e9f 100644 --- a/BrightFuturesTests/ErrorsTests.swift +++ b/BrightFuturesTests/ErrorsTests.swift @@ -25,12 +25,21 @@ import BrightFutures enum TestError: ErrorType { case JustAnError + case JustAnotherError var nsError: NSError { - return NSError(domain: "TestError", code: 1, userInfo: nil) + switch self { + case .JustAnError: + return NSError(domain: "TestError", code: 1, userInfo: nil) + case .JustAnotherError: + return NSError(domain: "AnotherTestError", code: 2, userInfo: nil) + } + } } +extension TestError: Equatable {} + class ErrorsTests: XCTestCase { func testNSError() { diff --git a/BrightFuturesTests/InvalidationTokenTests.swift b/BrightFuturesTests/InvalidationTokenTests.swift index 16406e6..808fc52 100644 --- a/BrightFuturesTests/InvalidationTokenTests.swift +++ b/BrightFuturesTests/InvalidationTokenTests.swift @@ -56,6 +56,42 @@ class InvalidationTokenTests: XCTestCase { self.waitForExpectationsWithTimeout(2, handler: nil) } + func testNonInvalidatedSucceededFutureOnSuccess() { + let token = InvalidationToken() + + let e = self.expectation() + Future.succeeded(3).onSuccess(token: token) { val in + XCTAssertEqual(val, 3) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func testNonInvalidatedSucceededFutureOnComplete() { + let token = InvalidationToken() + + let e = self.expectation() + Future.succeeded(3).onComplete(token: token) { res in + XCTAssertEqual(res.value!, 3) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + + func testNonInvalidatedFailedFutureOnFailure() { + let token = InvalidationToken() + + let e = self.expectation() + Future.failed(TestError.JustAnError).onFailure(token: token) { err in + XCTAssertEqual(err, TestError.JustAnError) + e.fulfill() + } + + self.waitForExpectationsWithTimeout(2, handler: nil) + } + func testStress() { class Counter { var i = 0 diff --git a/coverage/coverage.BrightFutures_FutureUtils.swift.html b/coverage/coverage.BrightFutures_FutureUtils.swift.html new file mode 100644 index 0000000..c6a8b32 --- /dev/null +++ b/coverage/coverage.BrightFutures_FutureUtils.swift.html @@ -0,0 +1,1193 @@ + + + + + + Head + + + + + + + + + + + + + + +
GCC Code Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Directory:BrightFutures/ExecTotalCoverage
File:BrightFutures/FutureUtils.swiftLines:353794.6 %
Date:2015-06-26Branches:000.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineExecSource
1
// The MIT License (MIT)
2
//
3
// Copyright (c) 2014 Thomas Visser
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in all
13
// copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
23
import Foundation
24
import Result
25
26
//// The free functions in this file operate on sequences of Futures
27
28
/// Performs the fold operation over a sequence of futures. The folding is performed
29
/// on `Queue.global`.
30
/// (The Swift compiler does not allow a context parameter with a default value
31
/// so we define some functions twice)
32
public func fold<S: SequenceType, T, R, E where S.Generator.Element == Future<T, E>>(seq: S, zero: R, f: (R, T) -> R) -> Future<R, E> {
33
1
    return fold(seq, context: Queue.global.context, zero, f)
34
}
35
36
/// Performs the fold operation over a sequence of futures. The folding is performed
37
/// in the given context.
38
public func fold<S: SequenceType, T, R, E where S.Generator.Element == Future<T, E>>(seq: S, context c: ExecutionContext, zero: R, f: (R, T) -> R) -> Future<R, E> {
39
2
    return reduce(seq, Future<R, E>.succeeded(zero)) { zero, elem in
40
2
        return zero.flatMap { zeroVal in
41
2
            elem.map(context: c) { elemVal in
42
1
                return f(zeroVal, elemVal)
43
            }
44
        }
45
    }
46
}
47
48
/// See `traverse<S: SequenceType, T, U where S.Generator.Element == T>(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future<U>) -> Future<[U]>`
49
public func traverse<S: SequenceType, T, U, E where S.Generator.Element == T>(seq: S, f: T -> Future<U, E>) -> Future<[U], E> {
50
1
    return traverse(seq, context: Queue.global.context, f)
51
}
52
53
/// Turns a sequence of T's into an array of `Future<U>`'s by calling the given closure for each element in the sequence.
54
/// If no context is provided, the given closure is executed on `Queue.global`
55
1
public func traverse<S: SequenceType, T, U, E where S.Generator.Element == T>(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future<U, E>) -> Future<[U], E> {
56
2
    return fold(map(seq, f), context: c, [U]()) { (list: [U], elem: U) -> [U] in
57
1
        return list + [elem]
58
    }
59
}
60
61
/// Turns a sequence of `Future<T>`'s into a future with an array of T's (Future<[T]>)
62
/// If one of the futures in the given sequence fails, the returned future will fail
63
/// with the error of the first future that comes first in the list.
64
public func sequence<S: SequenceType, T, E where S.Generator.Element == Future<T, E>>(seq: S) -> Future<[T], E> {
65
2
    return traverse(seq) { (fut: Future<T, E>) -> Future<T, E> in
66
1
        return fut
67
    }
68
}
69
70
/// See `find<S: SequenceType, T where S.Generator.Element == Future<T>>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future<T>`
71
public func find<S: SequenceType, T, E: ErrorType where S.Generator.Element == Future<T, E>>(seq: S, p: T -> Bool) -> Future<T, BrightFuturesError<E>> {
72
1
    return find(seq, context: Queue.global.context, p)
73
}
74
75
/// Returns a future that succeeds with the value from the first future in the given
76
/// sequence that passes the test `p`.
77
/// If any of the futures in the given sequence fail, the returned future fails with the
78
/// error of the first failed future in the sequence.
79
/// If no futures in the sequence pass the test, a future with an error with NoSuchElement is returned.
80
public func find<S: SequenceType, T, E: ErrorType where S.Generator.Element == Future<T, E>>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future<T, BrightFuturesError<E>> {
81
1
    return sequence(seq).mapError { error in
82
        return BrightFuturesError(external: error)
83
2
    }.flatMap(context: c) { val -> Result<T, BrightFuturesError<E>> in
84
1
        for elem in val {
85
1
            if (p(elem)) {
86
1
                return Result(value: elem)
87
            }
88
        }
89
1
        return Result(error: .NoSuchElement)
90
    }
91
}
92
93
/// Returns a future that returns with the first future from the given sequence that completes
94
/// (regardless of whether that future succeeds or fails)
95
public func firstCompletedOf<S: SequenceType, T, E where S.Generator.Element == Future<T, E>>(seq: S) -> Future<T, E> {
96
1
    let p = Promise<T, E>()
97
98
1
    for fut in seq {
99
2
        fut.onComplete(context: Queue.global.context) { res in
100
1
            p.tryComplete(res)
101
1
            return
102
        }
103
    }
104
105
1
    return p.future
106
}
107
108
109
/// Enables the chaining of two failable operations where the second operation is asynchronous and
110
/// represented by a future.
111
/// Like map, the given closure (that performs the second operation) is only executed
112
/// if the first operation result is a .Success
113
/// If a regular `map` was used, the result would be `Result<Future<U>>`.
114
/// The implementation of this function uses `map`, but then flattens the result before returning it.
115
public func flatMap<T,U, E>(result: Result<T,E>, @noescape f: T -> Future<U, E>) -> Future<U, E> {
116
1
    return flatten(result.map(f))
117
}
118
119
/// Returns a .Failure with the error from the outer or inner result if either of the two failed
120
/// or a .Success with the success value from the inner Result
121
public func flatten<T, E>(result: Result<Result<T,E>,E>) -> Result<T,E> {
122
    return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) })
123
}
124
125
/// Returns the inner future if the outer result succeeded or a failed future
126
/// with the error from the outer result otherwise
127
public func flatten<T, E>(result: Result<Future<T, E>,E>) -> Future<T, E> {
128
3
    return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) })
129
}
130
131
/// Turns a sequence of `Result<T>`'s into a Result with an array of T's (`Result<[T]>`)
132
/// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the
133
/// error from the first failed result from the sequence.
134
public func sequence<S: SequenceType, T, E where S.Generator.Element == Result<T, E>>(seq: S) -> Result<[T], E> {
135
2
    return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T], E> in
136
1
        switch res {
137
1
        case .Success(let boxedResultSequence):
138
1
            switch elem {
139
1
            case .Success(let boxedElemValue):
140
1
                let newSeq = boxedResultSequence.value + [boxedElemValue.value]
141
1
                return Result<[T], E>(value: newSeq)
142
            case .Failure(let boxedElemError):
143
1
                return Result<[T], E>(error: boxedElemError.value)
144
            }
145
        case .Failure(let err):
146
1
            return res
147
        }
148
    }
149
}
+
+ + + + +
+
+ + + + + diff --git a/coverage/coverage.html b/coverage/coverage.html new file mode 100644 index 0000000..9967b1d --- /dev/null +++ b/coverage/coverage.html @@ -0,0 +1,431 @@ + + + + + + Head + + + + + + + + + + + + + + +
GCC Code Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Directory:BrightFutures/ExecTotalCoverage
Date:2015-06-26Lines:29630497.4 %
Legend: + low: < 75.0 % + medium: >= 75.0 % + high: >= 90.0 % + Branches:000.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

FileLinesBranches
Errors.swift +
+
+
+
77.8 %14 / 18100.0 %0 / 0
ExecutionContext.swift +
+
+
+
100.0 %8 / 8100.0 %0 / 0
Future.swift +
+
+
+
99.5 %187 / 188100.0 %0 / 0
FutureUtils.swift +
+
+
+
94.6 %35 / 37100.0 %0 / 0
InvalidationToken.swift +
+
+
+
100.0 %9 / 9100.0 %0 / 0
Promise.swift +
+
+
+
100.0 %11 / 11100.0 %0 / 0
Queue.swift +
+
+
+
100.0 %21 / 21100.0 %0 / 0
Semaphore.swift +
+
+
+
91.7 %11 / 12100.0 %0 / 0

+
+ + + + +
+
+ + + + + From 805f2a025cb0dd19e3ac5506359ff0917896c135 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:29:18 +0200 Subject: [PATCH 50/61] added missing line of documentation --- BrightFutures/Errors.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/BrightFutures/Errors.swift b/BrightFutures/Errors.swift index af49b0d..d6fca77 100644 --- a/BrightFutures/Errors.swift +++ b/BrightFutures/Errors.swift @@ -66,6 +66,7 @@ public enum BrightFuturesError: ErrorType { case InvalidationTokenInvalidated case External(Box) + /// Constructs a BrightFutures.External with the given external error public init(external: E) { self = .External(Box(external)) } From 821b96a934581e06f92fb7913a24d2ec96cf1909 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:29:24 +0200 Subject: [PATCH 51/61] Added changelog for 2.0.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c78d91..48c1f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2.0.0 +- Replaced homegrown `Result` and `Box` types with Rob Rix' excellent types. +- Futures & Promises are now also parametrizable by their error type, in addition to their value type: `Future`. This allows you to use your own (Swifty) error type, instead of `NSError`! +- Adds `BrightFuturesError` enum, containing all three possible errors that BrightFutures can return +- Renames `asType` to `forceType` to indicate that it is a _dangerous_ operation + +- Adds missing documentation (jazzy reports 100% documentation coverage!) +- Adds a lot of tests (test coverage is now at 97%, according to [SwiftCov](https://github.com/realm/SwiftCov)!) + # 1.0.1 - Updated README to reflect the pre-1.0.0 change from FutureUtils functions to free functions From 5f86a258c897ec812b5e20bca46c8e862bbc5cf0 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:38:46 +0200 Subject: [PATCH 52/61] updated readme for release --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7a9a8d1..dd5dcd9 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ You are currently on the 2.0-development branch. - 2.0 will be the next major version - Until it is released, breaking changes might occur - PR #51 tracks the progress -- See the [Migration guide](Documentation/Migration_2.0.md) +- See the - The README has not yet been updated BrightFutures @@ -21,9 +21,7 @@ Our Big Hairy Audacious Goal (BHAG) is to be copy-pasted into the Swift standard ## Latest news [![CircleCI build status badge](https://img.shields.io/circleci/project/Thomvis/BrightFutures/master.svg)](https://circleci.com/gh/Thomvis/BrightFutures) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods version](https://img.shields.io/cocoapods/v/BrightFutures.svg)](https://cocoapods.org/pods/BrightFutures) [![MIT License](https://img.shields.io/cocoapods/l/BrightFutures.svg)](LICENSE) ![Platform iOS OS X](https://img.shields.io/cocoapods/p/BrightFutures.svg) -The latest stable BrightFutures release is 1.0.1. - -[BrightFutures 2.0](https://github.com/Thomvis/BrightFutures/tree/2.0-development) is currently under development. A beta release is available. It would really help if you'd check it out. +BrightFutures 2.0 is now available! It removes the direct dependency on `NSError` and takes a Swiftier approach. The tests and documentation have been improved as well. Please check the [Migration guide](Documentation/Migration_2.0.md) for help on how to migrate your project to BrightFutures 2.0. ## Requirements @@ -50,10 +48,11 @@ github "Thomvis/BrightFutures" Run `carthage update` and follow the steps as described in Carthage's [README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). ## Documentation -- API documentation is available at the wonderful [cocoadocs.org](http://cocoadocs.org/docsets/BrightFutures) +- API documentation is available at the wonderful [cocoadocs.org](http://cocoadocs.org/docsets/BrightFutures) (100% documentation coverage) - This README covers almost all features of BrightFutures -- The [tests](BrightFuturesTests) contain (trivial) usage examples for every feature +- The [tests](BrightFuturesTests) contain (trivial) usage examples for every feature (97% test coverage) - The primary author, Thomas Visser, gave [a talk](https://www.youtube.com/watch?v=lgJT2KMMEmU) at the April 2015 CocoaHeadsNL meetup +- The [Highstreet Watch App](https://github.com/GetHighstreet/HighstreetWatchApp) is an Open Source WatchKit app that makes extensive use of BrightFutures ## Examples We write a lot of asynchronous code. Whether we're waiting for something to come in from the network or want to perform an expensive calculation off the main thread and then update the UI, we often do the 'fire and callback' dance. Here's a typical snippet of asynchronous code: From 8f82df86d666fdef296cecdec4325d45e541e1e6 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:41:39 +0200 Subject: [PATCH 53/61] added details on test & documentation coverage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd5dcd9..bdec987 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Our Big Hairy Audacious Goal (BHAG) is to be copy-pasted into the Swift standard ## Latest news [![CircleCI build status badge](https://img.shields.io/circleci/project/Thomvis/BrightFutures/master.svg)](https://circleci.com/gh/Thomvis/BrightFutures) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods version](https://img.shields.io/cocoapods/v/BrightFutures.svg)](https://cocoapods.org/pods/BrightFutures) [![MIT License](https://img.shields.io/cocoapods/l/BrightFutures.svg)](LICENSE) ![Platform iOS OS X](https://img.shields.io/cocoapods/p/BrightFutures.svg) -BrightFutures 2.0 is now available! It removes the direct dependency on `NSError` and takes a Swiftier approach. The tests and documentation have been improved as well. Please check the [Migration guide](Documentation/Migration_2.0.md) for help on how to migrate your project to BrightFutures 2.0. +BrightFutures 2.0 is now available! It removes the direct dependency on `NSError` and takes a Swiftier approach. The tests (97% coverage) and documentation (100% coverage) have been improved as well. Please check the [Migration guide](Documentation/Migration_2.0.md) for help on how to migrate your project to BrightFutures 2.0. ## Requirements From 6078cee1408a51b2fab2b4aa1e98af76a1d0958e Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:43:10 +0200 Subject: [PATCH 54/61] deleted accidentally checked-in coverage files --- ...erage.BrightFutures_FutureUtils.swift.html | 1193 ----------------- coverage/coverage.html | 431 ------ 2 files changed, 1624 deletions(-) delete mode 100644 coverage/coverage.BrightFutures_FutureUtils.swift.html delete mode 100644 coverage/coverage.html diff --git a/coverage/coverage.BrightFutures_FutureUtils.swift.html b/coverage/coverage.BrightFutures_FutureUtils.swift.html deleted file mode 100644 index c6a8b32..0000000 --- a/coverage/coverage.BrightFutures_FutureUtils.swift.html +++ /dev/null @@ -1,1193 +0,0 @@ - - - - - - Head - - - - - - - - - - - - - - -
GCC Code Coverage Report
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Directory:BrightFutures/ExecTotalCoverage
File:BrightFutures/FutureUtils.swiftLines:353794.6 %
Date:2015-06-26Branches:000.0 %
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LineExecSource
1
// The MIT License (MIT)
2
//
3
// Copyright (c) 2014 Thomas Visser
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in all
13
// copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
23
import Foundation
24
import Result
25
26
//// The free functions in this file operate on sequences of Futures
27
28
/// Performs the fold operation over a sequence of futures. The folding is performed
29
/// on `Queue.global`.
30
/// (The Swift compiler does not allow a context parameter with a default value
31
/// so we define some functions twice)
32
public func fold<S: SequenceType, T, R, E where S.Generator.Element == Future<T, E>>(seq: S, zero: R, f: (R, T) -> R) -> Future<R, E> {
33
1
    return fold(seq, context: Queue.global.context, zero, f)
34
}
35
36
/// Performs the fold operation over a sequence of futures. The folding is performed
37
/// in the given context.
38
public func fold<S: SequenceType, T, R, E where S.Generator.Element == Future<T, E>>(seq: S, context c: ExecutionContext, zero: R, f: (R, T) -> R) -> Future<R, E> {
39
2
    return reduce(seq, Future<R, E>.succeeded(zero)) { zero, elem in
40
2
        return zero.flatMap { zeroVal in
41
2
            elem.map(context: c) { elemVal in
42
1
                return f(zeroVal, elemVal)
43
            }
44
        }
45
    }
46
}
47
48
/// See `traverse<S: SequenceType, T, U where S.Generator.Element == T>(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future<U>) -> Future<[U]>`
49
public func traverse<S: SequenceType, T, U, E where S.Generator.Element == T>(seq: S, f: T -> Future<U, E>) -> Future<[U], E> {
50
1
    return traverse(seq, context: Queue.global.context, f)
51
}
52
53
/// Turns a sequence of T's into an array of `Future<U>`'s by calling the given closure for each element in the sequence.
54
/// If no context is provided, the given closure is executed on `Queue.global`
55
1
public func traverse<S: SequenceType, T, U, E where S.Generator.Element == T>(seq: S, context c: ExecutionContext = Queue.global.context, f: T -> Future<U, E>) -> Future<[U], E> {
56
2
    return fold(map(seq, f), context: c, [U]()) { (list: [U], elem: U) -> [U] in
57
1
        return list + [elem]
58
    }
59
}
60
61
/// Turns a sequence of `Future<T>`'s into a future with an array of T's (Future<[T]>)
62
/// If one of the futures in the given sequence fails, the returned future will fail
63
/// with the error of the first future that comes first in the list.
64
public func sequence<S: SequenceType, T, E where S.Generator.Element == Future<T, E>>(seq: S) -> Future<[T], E> {
65
2
    return traverse(seq) { (fut: Future<T, E>) -> Future<T, E> in
66
1
        return fut
67
    }
68
}
69
70
/// See `find<S: SequenceType, T where S.Generator.Element == Future<T>>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future<T>`
71
public func find<S: SequenceType, T, E: ErrorType where S.Generator.Element == Future<T, E>>(seq: S, p: T -> Bool) -> Future<T, BrightFuturesError<E>> {
72
1
    return find(seq, context: Queue.global.context, p)
73
}
74
75
/// Returns a future that succeeds with the value from the first future in the given
76
/// sequence that passes the test `p`.
77
/// If any of the futures in the given sequence fail, the returned future fails with the
78
/// error of the first failed future in the sequence.
79
/// If no futures in the sequence pass the test, a future with an error with NoSuchElement is returned.
80
public func find<S: SequenceType, T, E: ErrorType where S.Generator.Element == Future<T, E>>(seq: S, context c: ExecutionContext, p: T -> Bool) -> Future<T, BrightFuturesError<E>> {
81
1
    return sequence(seq).mapError { error in
82
        return BrightFuturesError(external: error)
83
2
    }.flatMap(context: c) { val -> Result<T, BrightFuturesError<E>> in
84
1
        for elem in val {
85
1
            if (p(elem)) {
86
1
                return Result(value: elem)
87
            }
88
        }
89
1
        return Result(error: .NoSuchElement)
90
    }
91
}
92
93
/// Returns a future that returns with the first future from the given sequence that completes
94
/// (regardless of whether that future succeeds or fails)
95
public func firstCompletedOf<S: SequenceType, T, E where S.Generator.Element == Future<T, E>>(seq: S) -> Future<T, E> {
96
1
    let p = Promise<T, E>()
97
98
1
    for fut in seq {
99
2
        fut.onComplete(context: Queue.global.context) { res in
100
1
            p.tryComplete(res)
101
1
            return
102
        }
103
    }
104
105
1
    return p.future
106
}
107
108
109
/// Enables the chaining of two failable operations where the second operation is asynchronous and
110
/// represented by a future.
111
/// Like map, the given closure (that performs the second operation) is only executed
112
/// if the first operation result is a .Success
113
/// If a regular `map` was used, the result would be `Result<Future<U>>`.
114
/// The implementation of this function uses `map`, but then flattens the result before returning it.
115
public func flatMap<T,U, E>(result: Result<T,E>, @noescape f: T -> Future<U, E>) -> Future<U, E> {
116
1
    return flatten(result.map(f))
117
}
118
119
/// Returns a .Failure with the error from the outer or inner result if either of the two failed
120
/// or a .Success with the success value from the inner Result
121
public func flatten<T, E>(result: Result<Result<T,E>,E>) -> Result<T,E> {
122
    return result.analysis(ifSuccess: { $0 }, ifFailure: { Result(error: $0) })
123
}
124
125
/// Returns the inner future if the outer result succeeded or a failed future
126
/// with the error from the outer result otherwise
127
public func flatten<T, E>(result: Result<Future<T, E>,E>) -> Future<T, E> {
128
3
    return result.analysis(ifSuccess: { $0 }, ifFailure: { Future.failed($0) })
129
}
130
131
/// Turns a sequence of `Result<T>`'s into a Result with an array of T's (`Result<[T]>`)
132
/// If one of the results in the given sequence is a .Failure, the returned result is a .Failure with the
133
/// error from the first failed result from the sequence.
134
public func sequence<S: SequenceType, T, E where S.Generator.Element == Result<T, E>>(seq: S) -> Result<[T], E> {
135
2
    return reduce(seq, Result(value: [])) { (res, elem) -> Result<[T], E> in
136
1
        switch res {
137
1
        case .Success(let boxedResultSequence):
138
1
            switch elem {
139
1
            case .Success(let boxedElemValue):
140
1
                let newSeq = boxedResultSequence.value + [boxedElemValue.value]
141
1
                return Result<[T], E>(value: newSeq)
142
            case .Failure(let boxedElemError):
143
1
                return Result<[T], E>(error: boxedElemError.value)
144
            }
145
        case .Failure(let err):
146
1
            return res
147
        }
148
    }
149
}
-
- - - - -
-
- - - - - diff --git a/coverage/coverage.html b/coverage/coverage.html deleted file mode 100644 index 9967b1d..0000000 --- a/coverage/coverage.html +++ /dev/null @@ -1,431 +0,0 @@ - - - - - - Head - - - - - - - - - - - - - - -
GCC Code Coverage Report
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Directory:BrightFutures/ExecTotalCoverage
Date:2015-06-26Lines:29630497.4 %
Legend: - low: < 75.0 % - medium: >= 75.0 % - high: >= 90.0 % - Branches:000.0 %
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

FileLinesBranches
Errors.swift -
-
-
-
77.8 %14 / 18100.0 %0 / 0
ExecutionContext.swift -
-
-
-
100.0 %8 / 8100.0 %0 / 0
Future.swift -
-
-
-
99.5 %187 / 188100.0 %0 / 0
FutureUtils.swift -
-
-
-
94.6 %35 / 37100.0 %0 / 0
InvalidationToken.swift -
-
-
-
100.0 %9 / 9100.0 %0 / 0
Promise.swift -
-
-
-
100.0 %11 / 11100.0 %0 / 0
Queue.swift -
-
-
-
100.0 %21 / 21100.0 %0 / 0
Semaphore.swift -
-
-
-
91.7 %11 / 12100.0 %0 / 0

-
- - - - -
-
- - - - - From 2b483a4ce96fdc038610bccea7b95b88e8f8a0c1 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:46:40 +0200 Subject: [PATCH 55/61] added link to the diff from the migration guide --- Documentation/Migration_2.0.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/Migration_2.0.md b/Documentation/Migration_2.0.md index 754fd8e..eeda9eb 100644 --- a/Documentation/Migration_2.0.md +++ b/Documentation/Migration_2.0.md @@ -1,3 +1,5 @@ +Please read the [changelog](CHANGELOG.md) for 2.0 first. + BrightFutures 2.0 has two new dependencies: Result and Box. If you're using CocoaPods, `pod update` should automatically integrate it into your project. If you're using Carthage, after running `carthage update`, you need to add `Result.framework` and `Box.framework` to your target like you have also done for `BrightFutures.framework`. In files where you're using `Result` or `Box`, you'll also need to add an import statement for the respecive frameworks. If you fail to do this, you will see errors like "Use of undeclared type 'Result'". @@ -28,4 +30,4 @@ The easiest way to create `Result` instances is through its two constructors. Yo Result(success: 1314) // a Result.Success Result(error: .DeserializationFailed(object: json) // a Result.Failure ``` - +In addition to this migration guide, you can take a look at the changes that were made to the tests during the development of 2.0. These can be found in the [2.0 PR diff](https://github.com/Thomvis/BrightFutures/pull/51/files#diff-a6ad99ed0ef578b716f34ca4e2d578f7L43). \ No newline at end of file From 06748610d9416f444723f4cd605e89022902b22c Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Fri, 26 Jun 2015 22:47:49 +0200 Subject: [PATCH 56/61] Update Migration_2.0.md --- Documentation/Migration_2.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Migration_2.0.md b/Documentation/Migration_2.0.md index eeda9eb..2cb6aef 100644 --- a/Documentation/Migration_2.0.md +++ b/Documentation/Migration_2.0.md @@ -1,4 +1,4 @@ -Please read the [changelog](CHANGELOG.md) for 2.0 first. +Please read the [changelog](../CHANGELOG.md) for 2.0 first. BrightFutures 2.0 has two new dependencies: Result and Box. If you're using CocoaPods, `pod update` should automatically integrate it into your project. If you're using Carthage, after running `carthage update`, you need to add `Result.framework` and `Box.framework` to your target like you have also done for `BrightFutures.framework`. @@ -30,4 +30,4 @@ The easiest way to create `Result` instances is through its two constructors. Yo Result(success: 1314) // a Result.Success Result(error: .DeserializationFailed(object: json) // a Result.Failure ``` -In addition to this migration guide, you can take a look at the changes that were made to the tests during the development of 2.0. These can be found in the [2.0 PR diff](https://github.com/Thomvis/BrightFutures/pull/51/files#diff-a6ad99ed0ef578b716f34ca4e2d578f7L43). \ No newline at end of file +In addition to this migration guide, you can take a look at the changes that were made to the tests during the development of 2.0. These can be found in the [2.0 PR diff](https://github.com/Thomvis/BrightFutures/pull/51/files#diff-a6ad99ed0ef578b716f34ca4e2d578f7L43). From aa35596fb8e7a3b793c07daedb09a084c8b0ca73 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 27 Jun 2015 11:53:04 +0200 Subject: [PATCH 57/61] removed warning from readme --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index bdec987..7d484d6 100755 --- a/README.md +++ b/README.md @@ -1,13 +1,3 @@ -### Warning - -You are currently on the 2.0-development branch. - -- 2.0 will be the next major version -- Until it is released, breaking changes might occur -- PR #51 tracks the progress -- See the -- The README has not yet been updated - BrightFutures ============= From f6691db20c0b202bf47d8b691ce4828b7902828a Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 27 Jun 2015 12:21:45 +0200 Subject: [PATCH 58/61] updated examples in the readme, making sure they still compile --- README.md | 141 +++++++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 7d484d6..e9266b5 100755 --- a/README.md +++ b/README.md @@ -49,13 +49,13 @@ We write a lot of asynchronous code. Whether we're waiting for something to come ```swift User.logIn(username, password) { user, error in - if !error { - Posts.fetchPosts(user, success: { posts in - // do something with the user's posts - }, failure: handleError) - } else { - handleError(error) // handeError is a custom function to handle errors - } + if !error { + Posts.fetchPosts(user, success: { posts in + // do something with the user's posts + }, failure: handleError) + } else { + handleError(error) // handeError is a custom function to handle errors + } } ``` @@ -63,11 +63,11 @@ Now let's see what BrightFutures can do for you: ```swift User.logIn(username,password).flatMap { user in - Posts.fetchPosts(user) + Posts.fetchPosts(user) }.onSuccess { posts in - // do something with the user's posts + // do something with the user's posts }.onFailure { error in - // either logging in or fetching posts failed + // either logging in or fetching posts failed } ``` @@ -82,50 +82,48 @@ If you already have a function (or really any expression) defined that you just ```swift future { - fibonacci(50) + fibonacci(50) }.onSuccess { num in - // value is 12586269025 + // value is 12586269025 } ``` While this is really short and simple, it is equally limited. In many cases, you will need a way to indicate that the task failed. To do this, instead of returning the value, you can return a Result. Results can indicate either a success or a failure: ```swift -let f = future { () -> Result in - let now: NSDate? = serverTime() - if let someNow = now { - return .Success(Box(someNow)) - } - - return .Failure(NSError(domain: "TimeServiceErrorDomain", code: 404, userInfo: nil)) +let f = future { () -> Result in + let now: NSDate? = serverTime() + if let now = now { + return Result(value: now) + } + + return Result(error: ReadmeError.TimeServiceError) } f.onSuccess { value in - // value will the NSDate from the server + // value will the NSDate from the server } ``` -(The future block needs an explict type because the Swift compiler is not able to deduce the type of multi-statement blocks. The returned date needs to be _boxed_ because the Swift compiler does not yet support variable layout enums.) +The future block needs an explict type because the Swift compiler is not able to deduce the type of multi-statement blocks. `ReadmeError` is an enum consisting of all errors that can happen in this readme. ## Providing Futures Now let's assume the role of an API author who wants to use BrightFutures. The 'producer' of a future is called a `Promise`. A promise contains a future that you can immediately hand to the client. The promise is kept around while performing the asynchronous operation, until calling `Promise.success(result)` or `Promise.failure(error)` when the operation ended. Futures can only be completed through a Promise. ```swift -func asyncCalculation() -> Future { - let promise = Promise() - - Queue.global.async { - - // do a complicated task +func asyncCalculation() -> Future { + let promise = Promise() - promise.success("forty-two") - } - - return promise.future + Queue.global.async { + // do a complicated task and then hand the result to the promise: + promise.success("forty-two") + } + + return promise.future } ``` -`Queue` is a simple wrapper around a dispatch queue. +`Queue` is a simple wrapper around a dispatch queue. `NoError` indicates that the `Future` cannot fail. This is guaranteed by the type system, since `NoError` has no initializers. ## Callbacks You can be informed of the result of a `Future` by registering callbacks: `onComplete`, `onSuccess` and `onFailure`. The order in which the callbacks are executed upon completion of the future is not guaranteed, but it is guaranteed that the callbacks are executed serially. It is not safe to add a new callback from within a callback of the same future. @@ -137,25 +135,22 @@ Using the `andThen` function on a `Future`, the order of callbacks can be explic ```swift var answer = 10 -let f = Future.succeeded(4).andThen { result in +let f = Future.succeeded(4).andThen { result in switch result { - case .Success(let val): + case .Success(let val): answer *= val.value - case .Failure(_): + case .Failure(_): break } }.andThen { result in if let val = result.value { - answer += 2 + answer += 2 } - return } // answer will be 42 (not 48) ``` -`result` is an instance of `Result`, which mimics a typical `Try` construct as much as the Swift compiler currently allows. Due to limitations of generic enum types, the actual value needs to be boxed. (See [#8](https://github.com/Thomvis/BrightFutures/issues/8).) - ## Functional Composition ### map @@ -190,12 +185,12 @@ f.zip(f1).onSuccess { (let a, let b) in ### filter ```swift -Future.succeeded(3).filter { $0 > 5 }.onComplete { result in - // failed with error NoSuchElementError +future(3).filter { $0 > 5 }.onComplete { result in + // failed with error NoSuchElementError } -Future.succeeded("Swift").filter { $0.hasPrefix("Sw") }.onComplete { result in - // succeeded with value "Swift" +future("Swift").filter { $0.hasPrefix("Sw") }.onComplete { result in + // succeeded with value "Swift" } ``` @@ -203,17 +198,12 @@ Future.succeeded("Swift").filter { $0.hasPrefix("Sw") }.onComplete { result in If a `Future` fails, use `recover` to offer a default or alternative value and continue the callback chain. ```swift -let f = future { () -> Result in - // request something from the web - - if (request.error) { // it could fail - return .Failure(request.error) - } - - return .Success(Box(10)) +let f = future { + // imagine a request failed + return Result(error: ReadmeError.RequestFailed) }.recover { _ in // provide an offline default return 5 -}.onSuccess { value in // either the request or the recovery succeeded +}.onSuccess { value in // value is 5 if the request failed or 10 if the request succeeded } ``` @@ -229,11 +219,11 @@ The built-in `fold` function allows you to turn a list of values into a single v Folding a list of Futures is not very convenient with the built-in `fold` function, which is why BrightFutures provides one that works especially well for our use case. BrightFutures' `fold` turns a list of Futures into a single Future that contains the resulting value. This allows us to, for example, calculate the sum of the first 10 Future-wrapped elements of the fibonacci sequence: ```swift -// 1+1+2+3+5+8+13+21+34+55 -let fibonacciSequence = [Future.succeeded(fibonacci(1)), Future.succeeded(fibonacci(2)), ... Future.succeeded(fibonacci(10))] +let fibonacciSequence = [future(fibonacci(1)), future(fibonacci(2)), ..., future(fibonacci(10))] -fold(fibonacciSequence, zero: 0, op: { $0 + $1 }).onSuccess { val in - // value is 143 +// 1+1+2+3+5+8+13+21+34+55 +fold(fibonacciSequence, 0, { $0 + $1 }).onSuccess { sum in + // sum is 143 } ``` @@ -241,9 +231,8 @@ fold(fibonacciSequence, zero: 0, op: { $0 + $1 }).onSuccess { val in With `sequence`, you can turn a list of Futures into a single Future that contains a list of the results from those futures. ```swift -// 1+1+2+3+5+8+13+21+34+55 -let fibonacciSequence = [Future.succeeded(fibonacci(1)), Future.succeeded(fibonacci(2)), ... Future.succeeded(fibonacci(10))] - +let fibonacciSequence = [future(fibonacci(1)), future(fibonacci(2)), ..., future(fibonacci(10))] + sequence(fibonacciSequence).onSuccess { fibNumbers in // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.] } @@ -253,10 +242,10 @@ sequence(fibonacciSequence).onSuccess { fibNumbers in `traverse` combines `map` and `fold` in one convenient function. `traverse` takes a list of values and a closure that takes a single value from that list and turns it into a Future. The result of `traverse` is a single Future containing an array of the values from the Futures returned by the given closure. ```swift -traverse(Array(1...10)) { - Future.succeeded(fibonacci($0)) +traverse(1...10) { + i in future(fibonacci(i)) }.onSuccess { fibNumbers in - // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.] + // fibNumbers is an array of Ints: [1, 1, 2, 3, etc.] } ``` @@ -276,12 +265,12 @@ If you want to have custom threading behavior, skip do do not the section. next The default threading behavior can be overridden by providing explicit execution contexts. By default, BrightFutures comes with three contexts: `Queue.main`, `Queue.global`, and `ImmediateExecutionContext`. You can also create your own by implementing the `ExecutionContext` protocol. ```swift -let f = future(context: ImmediateExecutionContext { _ in - fibonacci(10) +let f = future(context: ImmediateExecutionContext) { + fibonacci(10) } - + f.onComplete(context: Queue.main.context) { value in - // update the UI, we're on the main thread + // update the UI, we're on the main thread } ``` @@ -292,19 +281,19 @@ An invalidation token can be used to invalidate a callback, preventing it from b ```swift class MyCell : UICollectionViewCell { - var token = InvalidationToken() + var token = InvalidationToken() - public override func prepareForReuse() { - super.prepareForReuse() - token.invalidate() - token = InvalidationToken() - } + public override func prepareForReuse() { + super.prepareForReuse() + token.invalidate() + token = InvalidationToken() + } - public func setModel(model: Model) { - ImageLoader.loadImage(model.image).onSuccess(token: token) { [weak self] UIImage in - self.imageView.image = UIImage + public func setModel(model: Model) { + ImageLoader.loadImage(model.image).onSuccess(token: token) { [weak self] UIImage in + self.imageView.image = UIImage + } } - } } ``` From 1028f5d699c73b55530071f8f4c7006a4cdf3b6b Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 27 Jun 2015 12:31:26 +0200 Subject: [PATCH 59/61] added note on Swift 2.0 compatibility, created releases section --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e9266b5..ed33358 100755 --- a/README.md +++ b/README.md @@ -13,10 +13,14 @@ Our Big Hairy Audacious Goal (BHAG) is to be copy-pasted into the Swift standard BrightFutures 2.0 is now available! It removes the direct dependency on `NSError` and takes a Swiftier approach. The tests (97% coverage) and documentation (100% coverage) have been improved as well. Please check the [Migration guide](Documentation/Migration_2.0.md) for help on how to migrate your project to BrightFutures 2.0. -## Requirements +A Swift 2.0 compatible version is in the works. Check out the [swift-2.0](https://github.com/Thomvis/BrightFutures/tree/swift-2.0) branch. -- Built for Swift 1.2 -- Runs on iOS 8 / OS X 10.10 and above +## Releases + +Latest releases: +- 2.0.0 + - Built for Swift 1.2 + - Runs on iOS 8 / OS X 10.10 and above ## Installation ### [CocoaPods](http://cocoapods.org/) From 7d92603aa14f7412955a48b7adfcc9e81a234efb Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 27 Jun 2015 12:33:14 +0200 Subject: [PATCH 60/61] added the 1.0.1 release --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ed33358..f2a1b2e 100755 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ Latest releases: - 2.0.0 - Built for Swift 1.2 - Runs on iOS 8 / OS X 10.10 and above +- 1.0.1 + - Superseded by 2.0.0, 1.x is in maintenance mode + - Built for Swift 1.2 + - Runs on iOS 8 / OS X 10.10 and above ## Installation ### [CocoaPods](http://cocoapods.org/) From fa2dcfac9333cd8f0567da582241e6ead2428cb0 Mon Sep 17 00:00:00 2001 From: Thomas Visser Date: Sat, 27 Jun 2015 12:34:05 +0200 Subject: [PATCH 61/61] Bumped version & Result dependency --- BrightFutures.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BrightFutures.podspec b/BrightFutures.podspec index 6c4deb8..c5a0096 100644 --- a/BrightFutures.podspec +++ b/BrightFutures.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrightFutures' - s.version = '2.0.0-beta.1' + s.version = '2.0.0' s.license = 'MIT' s.summary = 'A simple Futures & Promises library for iOS and OS X written in Swift' s.homepage = 'https://github.com/Thomvis/BrightFutures' @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'BrightFutures/*.swift' - s.dependency 'Result', '~> 0.4.2' + s.dependency 'Result', '~> 0.4.3' s.requires_arc = true end \ No newline at end of file