diff --git a/Interstellar.xcodeproj/project.pbxproj b/Interstellar.xcodeproj/project.pbxproj index 39fd82d..ef40fa3 100644 --- a/Interstellar.xcodeproj/project.pbxproj +++ b/Interstellar.xcodeproj/project.pbxproj @@ -9,35 +9,21 @@ /* Begin PBXBuildFile section */ 2873E1191D0FF50B00CB521F /* Mutex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2DC41B1CC03E380055DC5C /* Mutex.swift */; }; 4EE634761CD0C23B007A602B /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775C91C118F6B002E134D /* Result.swift */; }; - 4EE634771CD0C23B007A602B /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775CA1C118F6B002E134D /* Signal.swift */; }; 4EE634781CD0C23B007A602B /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D38E1C1A300A002F998A /* Observable.swift */; }; 4EE634791CD0C23B007A602B /* Observable+Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D39A1C1A5126002F998A /* Observable+Result.swift */; }; - 4EE6347A1CD0C23B007A602B /* ResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D3921C1A3870002F998A /* ResultType.swift */; }; 8F2B8D701B07A92C0063EB9C /* Interstellar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F2B8D641B07A92C0063EB9C /* Interstellar.framework */; }; 8F2DC41C1CC03E380055DC5C /* Mutex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2DC41B1CC03E380055DC5C /* Mutex.swift */; }; 8F4D98571C1B1E68009A91E5 /* Observable+Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D39A1C1A5126002F998A /* Observable+Result.swift */; }; 8F4D98581C1B1E68009A91E5 /* Observable+Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D39A1C1A5126002F998A /* Observable+Result.swift */; }; 8F50D38F1C1A300A002F998A /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D38E1C1A300A002F998A /* Observable.swift */; }; - 8F50D3931C1A3870002F998A /* ResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D3921C1A3870002F998A /* ResultType.swift */; }; - 8F50D3941C1A3DA7002F998A /* ResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D3921C1A3870002F998A /* ResultType.swift */; }; - 8F50D3951C1A3DA8002F998A /* ResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D3921C1A3870002F998A /* ResultType.swift */; }; 8F50D3981C1A3DAF002F998A /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D38E1C1A300A002F998A /* Observable.swift */; }; 8F50D3991C1A3DB0002F998A /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D38E1C1A300A002F998A /* Observable.swift */; }; 8F50D39B1C1A5126002F998A /* Observable+Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F50D39A1C1A5126002F998A /* Observable+Result.swift */; }; 8F5775CB1C118F6B002E134D /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775C91C118F6B002E134D /* Result.swift */; }; 8F5775CC1C118F6B002E134D /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775C91C118F6B002E134D /* Result.swift */; }; 8F5775CD1C118F6B002E134D /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775C91C118F6B002E134D /* Result.swift */; }; - 8F5775CE1C118F6B002E134D /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775CA1C118F6B002E134D /* Signal.swift */; }; - 8F5775CF1C118F6B002E134D /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775CA1C118F6B002E134D /* Signal.swift */; }; - 8F5775D01C118F6B002E134D /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F5775CA1C118F6B002E134D /* Signal.swift */; }; 8FC7A40E1CC12EAA00710873 /* Mutex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2DC41B1CC03E380055DC5C /* Mutex.swift */; }; 8FC7A40F1CC12EAB00710873 /* Mutex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2DC41B1CC03E380055DC5C /* Mutex.swift */; }; - B4010EDC1D99919D00E6AC8E /* WaitingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ECC1D99916E00E6AC8E /* WaitingTests.swift */; }; - B4010EDD1D99919D00E6AC8E /* ThreadingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ECD1D99916E00E6AC8E /* ThreadingTests.swift */; }; - B4010EDE1D99919D00E6AC8E /* DelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ECE1D99916E00E6AC8E /* DelayTests.swift */; }; - B4010EDF1D99919D00E6AC8E /* DebounceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ECF1D99916E00E6AC8E /* DebounceTests.swift */; }; - B4010EE01D9991A100E6AC8E /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ED41D99918000E6AC8E /* SignalTests.swift */; }; - B4010EE11D9991A100E6AC8E /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ED51D99918000E6AC8E /* ResultTests.swift */; }; B4010EE21D9991A100E6AC8E /* ResultObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ED61D99918000E6AC8E /* ResultObservableTests.swift */; }; B4010EE31D9991A100E6AC8E /* ObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4010ED71D99918000E6AC8E /* ObservableTests.swift */; }; B44BB1941D92B0FF00CE3CB2 /* ObserverToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44BB1931D92B0FF00CE3CB2 /* ObserverToken.swift */; }; @@ -81,19 +67,11 @@ 8F2B8D6F1B07A92C0063EB9C /* InterstellarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InterstellarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8F2DC41B1CC03E380055DC5C /* Mutex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Mutex.swift; path = Sources/Interstellar/Mutex.swift; sourceTree = SOURCE_ROOT; }; 8F50D38E1C1A300A002F998A /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Observable.swift; path = Sources/Interstellar/Observable.swift; sourceTree = SOURCE_ROOT; }; - 8F50D3921C1A3870002F998A /* ResultType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ResultType.swift; path = Sources/Interstellar/ResultType.swift; sourceTree = SOURCE_ROOT; }; 8F50D39A1C1A5126002F998A /* Observable+Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Observable+Result.swift"; path = "Sources/Interstellar/Observable+Result.swift"; sourceTree = SOURCE_ROOT; }; 8F5775C91C118F6B002E134D /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Sources/Interstellar/Result.swift; sourceTree = SOURCE_ROOT; }; - 8F5775CA1C118F6B002E134D /* Signal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Signal.swift; path = Sources/Interstellar/Signal.swift; sourceTree = SOURCE_ROOT; }; 8F5775E11C118F93002E134D /* Interstellar_Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Interstellar_Info.plist; path = Sources/Interstellar_Info.plist; sourceTree = SOURCE_ROOT; }; 8F5775E21C118F93002E134D /* Interstellar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Interstellar.h; path = Sources/Interstellar.h; sourceTree = SOURCE_ROOT; }; 8F5775E31C118FAC002E134D /* InterstellarTests_Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = InterstellarTests_Info.plist; path = Tests/InterstellarTests_Info.plist; sourceTree = SOURCE_ROOT; }; - B4010ECC1D99916E00E6AC8E /* WaitingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WaitingTests.swift; path = Tests/WarpdriveTests/WaitingTests.swift; sourceTree = SOURCE_ROOT; }; - B4010ECD1D99916E00E6AC8E /* ThreadingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ThreadingTests.swift; path = Tests/WarpdriveTests/ThreadingTests.swift; sourceTree = SOURCE_ROOT; }; - B4010ECE1D99916E00E6AC8E /* DelayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DelayTests.swift; path = Tests/WarpdriveTests/DelayTests.swift; sourceTree = SOURCE_ROOT; }; - B4010ECF1D99916E00E6AC8E /* DebounceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DebounceTests.swift; path = Tests/WarpdriveTests/DebounceTests.swift; sourceTree = SOURCE_ROOT; }; - B4010ED41D99918000E6AC8E /* SignalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalTests.swift; path = Tests/InterstellarTests/SignalTests.swift; sourceTree = SOURCE_ROOT; }; - B4010ED51D99918000E6AC8E /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ResultTests.swift; path = Tests/InterstellarTests/ResultTests.swift; sourceTree = SOURCE_ROOT; }; B4010ED61D99918000E6AC8E /* ResultObservableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ResultObservableTests.swift; path = Tests/InterstellarTests/ResultObservableTests.swift; sourceTree = SOURCE_ROOT; }; B4010ED71D99918000E6AC8E /* ObservableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ObservableTests.swift; path = Tests/InterstellarTests/ObservableTests.swift; sourceTree = SOURCE_ROOT; }; B44BB1931D92B0FF00CE3CB2 /* ObserverToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ObserverToken.swift; path = Sources/Interstellar/ObserverToken.swift; sourceTree = SOURCE_ROOT; }; @@ -170,11 +148,9 @@ children = ( 8F4870BE1BEF936C0098C305 /* Warpdrive */, 8F5775C91C118F6B002E134D /* Result.swift */, - 8F5775CA1C118F6B002E134D /* Signal.swift */, 8F50D38E1C1A300A002F998A /* Observable.swift */, B44BB1931D92B0FF00CE3CB2 /* ObserverToken.swift */, 8F50D39A1C1A5126002F998A /* Observable+Result.swift */, - 8F50D3921C1A3870002F998A /* ResultType.swift */, 8F2DC41B1CC03E380055DC5C /* Mutex.swift */, ); path = Interstellar; @@ -204,8 +180,6 @@ 8F4870C51BEF938E0098C305 /* InterstellarTests */ = { isa = PBXGroup; children = ( - B4010ED41D99918000E6AC8E /* SignalTests.swift */, - B4010ED51D99918000E6AC8E /* ResultTests.swift */, B4010ED61D99918000E6AC8E /* ResultObservableTests.swift */, B4010ED71D99918000E6AC8E /* ObservableTests.swift */, ); @@ -215,10 +189,6 @@ 8F4870C61BEF93960098C305 /* WarpdriveTests */ = { isa = PBXGroup; children = ( - B4010ECC1D99916E00E6AC8E /* WaitingTests.swift */, - B4010ECD1D99916E00E6AC8E /* ThreadingTests.swift */, - B4010ECE1D99916E00E6AC8E /* DelayTests.swift */, - B4010ECF1D99916E00E6AC8E /* DebounceTests.swift */, ); path = WarpdriveTests; sourceTree = ""; @@ -380,21 +350,22 @@ }; 8F2B8D631B07A92C0063EB9C = { CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0900; + LastSwiftMigration = 1020; ProvisioningStyle = Manual; }; 8F2B8D6E1B07A92C0063EB9C = { CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0900; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = 8F2B8D5E1B07A92C0063EB9C /* Build configuration list for PBXProject "Interstellar" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 8F2B8D5A1B07A92C0063EB9C; productRefGroup = 8F2B8D651B07A92C0063EB9C /* Products */; @@ -453,7 +424,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8F50D3951C1A3DA8002F998A /* ResultType.swift in Sources */, B44BB1961D92B0FF00CE3CB2 /* ObserverToken.swift in Sources */, 8F50D3991C1A3DB0002F998A /* Observable.swift in Sources */, EDC55FF71EF7BCD5005C53EB /* Threading.swift in Sources */, @@ -463,7 +433,6 @@ 8F4D98581C1B1E68009A91E5 /* Observable+Result.swift in Sources */, EDC55FF51EF7BCD5005C53EB /* Delay.swift in Sources */, EDC55FF41EF7BCD5005C53EB /* Debounce.swift in Sources */, - 8F5775D01C118F6B002E134D /* Signal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -471,7 +440,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8F50D3941C1A3DA7002F998A /* ResultType.swift in Sources */, B44BB1951D92B0FF00CE3CB2 /* ObserverToken.swift in Sources */, 8F50D3981C1A3DAF002F998A /* Observable.swift in Sources */, EDC55FFB1EF7BCD6005C53EB /* Threading.swift in Sources */, @@ -481,7 +449,6 @@ 8F4D98571C1B1E68009A91E5 /* Observable+Result.swift in Sources */, EDC55FF91EF7BCD6005C53EB /* Delay.swift in Sources */, EDC55FF81EF7BCD6005C53EB /* Debounce.swift in Sources */, - 8F5775CF1C118F6B002E134D /* Signal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -489,7 +456,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4EE6347A1CD0C23B007A602B /* ResultType.swift in Sources */, B44BB1971D92B0FF00CE3CB2 /* ObserverToken.swift in Sources */, 4EE634781CD0C23B007A602B /* Observable.swift in Sources */, EDC55FF31EF7BCD5005C53EB /* Threading.swift in Sources */, @@ -499,7 +465,6 @@ 4EE634791CD0C23B007A602B /* Observable+Result.swift in Sources */, EDC55FF11EF7BCD5005C53EB /* Delay.swift in Sources */, EDC55FF01EF7BCD5005C53EB /* Debounce.swift in Sources */, - 4EE634771CD0C23B007A602B /* Signal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -509,7 +474,6 @@ files = ( B44BB1941D92B0FF00CE3CB2 /* ObserverToken.swift in Sources */, 8F5775CB1C118F6B002E134D /* Result.swift in Sources */, - 8F50D3931C1A3870002F998A /* ResultType.swift in Sources */, EDC55FFF1EF7BCD6005C53EB /* Threading.swift in Sources */, 8F50D38F1C1A300A002F998A /* Observable.swift in Sources */, 8F2DC41C1CC03E380055DC5C /* Mutex.swift in Sources */, @@ -517,7 +481,6 @@ 8F50D39B1C1A5126002F998A /* Observable+Result.swift in Sources */, EDC55FFD1EF7BCD6005C53EB /* Delay.swift in Sources */, EDC55FFC1EF7BCD6005C53EB /* Debounce.swift in Sources */, - 8F5775CE1C118F6B002E134D /* Signal.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -527,12 +490,6 @@ files = ( B4010EE31D9991A100E6AC8E /* ObservableTests.swift in Sources */, B4010EE21D9991A100E6AC8E /* ResultObservableTests.swift in Sources */, - B4010EE11D9991A100E6AC8E /* ResultTests.swift in Sources */, - B4010EDD1D99919D00E6AC8E /* ThreadingTests.swift in Sources */, - B4010EDC1D99919D00E6AC8E /* WaitingTests.swift in Sources */, - B4010EDF1D99919D00E6AC8E /* DebounceTests.swift in Sources */, - B4010EE01D9991A100E6AC8E /* SignalTests.swift in Sources */, - B4010EDE1D99919D00E6AC8E /* DelayTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -821,7 +778,7 @@ PRODUCT_NAME = Interstellar; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.1; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -843,7 +800,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "de.nerdgeschoss.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = Interstellar; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.1; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -864,7 +821,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "de.nerdgeschoss.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.1; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -880,7 +837,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "de.nerdgeschoss.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Interstellar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Interstellar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Interstellar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Sources/Interstellar/Observable+Result.swift b/Sources/Interstellar/Observable+Result.swift index cc4b4c5..e7e259a 100644 --- a/Sources/Interstellar/Observable+Result.swift +++ b/Sources/Interstellar/Observable+Result.swift @@ -6,48 +6,59 @@ // Copyright © 2015 nerdgeschoss GmbH. All rights reserved. // +public protocol ResultType { + associatedtype Success + var _result: Result {get} +} + +extension Result: ResultType { + public var _result: Result { + return self as! Result + } +} + public extension Observable where T : ResultType { /// Observables containing a Result can be chained to only continue in the success case. - public func then(_ transform: @escaping (T.Value) -> Result) -> Observable> { - return map { $0.result.flatMap(transform) } + func then(_ transform: @escaping (T.Success) -> Result) -> Observable> { + return map { $0._result.flatMap(transform) } } /// Observables containing a Result can be chained to only continue in the success case. - public func then(_ transform: @escaping (T.Value) -> U) -> Observable> { - return map { $0.result.map(transform) } + func then(_ transform: @escaping (T.Success) -> U) -> Observable> { + return map { $0._result.map(transform) } } - + /// Observables containing a Result can be chained to only continue in the success case. - public func then(_ transform: @escaping (T.Value) throws -> U) -> Observable> { - return map { $0.result.flatMap(transform) } + func then(_ transform: @escaping (T.Success) throws -> U) -> Observable> { + return map { $0._result.flatMap { value in Result { try transform(value) } }} } /// Observables containing a Result can be chained to only continue in the success case. - public func then(_ transform: @escaping (T.Value) -> Observable) -> Observable> { + func then(_ transform: @escaping (T.Success) -> Observable) -> Observable> { return flatMap { [options] in - let observable = Observable>(options: options) - switch $0.result { + let observable = Observable>(options: options) + switch $0._result { case let .success(v): transform(v).subscribe { observable.update(.success($0)) } - case let .error(error): observable.update(.error(error)) + case let .failure(error): observable.update(.failure(error)) } return observable } } /// Observables containing a Result can be chained to only continue in the success case. - public func then(_ transform: @escaping (T.Value) -> Observable>) -> Observable> { + func then(_ transform: @escaping (T.Success) -> Observable>) -> Observable> { return flatMap { [options] in - switch $0.result { + switch $0._result { case let .success(v): return transform(v) - case let .error(error): return Observable>(Result.error(error), options: options) + case let .failure(error): return Observable>(Result.failure(error), options: options) } } } /// Only subscribe to successful events. - @discardableResult public func next(_ block: @escaping (T.Value) -> Void) -> Observable { + @discardableResult func next(_ block: @escaping (T.Success) -> Void) -> Observable { subscribe { result in - if let value = result.value { + if let value = try? result._result.get() { block(value) } } @@ -55,9 +66,9 @@ public extension Observable where T : ResultType { } /// Only subscribe to errors. - @discardableResult public func error(_ block: @escaping (Error) -> Void) -> Observable { + @discardableResult func error(_ block: @escaping (Error) -> Void) -> Observable { subscribe { result in - if let error = result.error { + if case let Result.failure(error) = result._result { block(error) } } @@ -65,7 +76,7 @@ public extension Observable where T : ResultType { } /// Peek at the value of the observable. - public func peek() -> T.Value? { - return self.value?.value + func peek() -> T.Success? { + return try? self.value?._result.get() } } diff --git a/Sources/Interstellar/Observable.swift b/Sources/Interstellar/Observable.swift index ed023ee..3c98b21 100644 --- a/Sources/Interstellar/Observable.swift +++ b/Sources/Interstellar/Observable.swift @@ -78,8 +78,8 @@ public final class Observable: Unsubscribable { @discardableResult public func subscribe(_ observer: @escaping (T) -> Void) -> ObserverToken { var token: ObserverToken! mutex.lock { - let newHashValue = (observers.keys.map({$0.hashValue}).max() ?? -1) + 1 - token = ObserverToken(observable: self, hashValue: newHashValue) + let newHashValue = (observers.keys.map({$0.token}).max() ?? -1) + 1 + token = ObserverToken(observable: self, token: newHashValue) if !(options.contains(.Once) && value != nil) { observers[token] = observer } @@ -154,10 +154,10 @@ extension Observable { /** Creates a new observable with a transform applied. The value of the observable will be wrapped in a Result in case the transform throws. */ - public func map(_ transform: @escaping (T) throws -> U) -> Observable> { - let observable = Observable>(options: options) + public func map(_ transform: @escaping (T) throws -> U) -> Observable> { + let observable = Observable>(options: options) subscribe { value in - observable.update(Result(block: { return try transform(value) })) + observable.update(Result(catching: { return try transform(value) })) } return observable } diff --git a/Sources/Interstellar/ObserverToken.swift b/Sources/Interstellar/ObserverToken.swift index d390bd0..96be44a 100644 --- a/Sources/Interstellar/ObserverToken.swift +++ b/Sources/Interstellar/ObserverToken.swift @@ -21,18 +21,22 @@ /// Observer tokens are created by observables to hande unsubscription. You are not supposed to create them directly. public final class ObserverToken: Hashable { private weak var observable: Unsubscribable? - public let hashValue: Int + public let token: Int - internal init (observable: Unsubscribable, hashValue: Int) { + internal init (observable: Unsubscribable, token: Int) { self.observable = observable - self.hashValue = hashValue + self.token = token } public func unsubscribe() { observable?.unsubscribe(self) } + + public func hash(into hasher: inout Hasher) { + hasher.combine(token) + } } public func ==(lhs: ObserverToken, rhs: ObserverToken) -> Bool { - return lhs.hashValue == rhs.hashValue + return lhs.token == rhs.token } diff --git a/Sources/Interstellar/Result.swift b/Sources/Interstellar/Result.swift index 67434cd..5d4968c 100644 --- a/Sources/Interstellar/Result.swift +++ b/Sources/Interstellar/Result.swift @@ -27,130 +27,130 @@ ErrorType). You can read more about the implementation in [this blog post](http://jensravens.de/a-swifter-way-of-handling-errors/). */ -public enum Result: ResultType { - case success(T) - case error(Error) - - /** - Initialize a result containing a successful value. - */ - public init(success value: T) { - self = Result.success(value) - } - - /** - Initialize a result containing an error - */ - public init(error: Error) { - self = .error(error) - } - - /** - Transform a result into another result using a function. If the result was an error, - the function will not be executed and the error returned instead. - */ - public func map(_ f: @escaping (T) -> U) -> Result { - switch self { - case let .success(v): return .success(f(v)) - case let .error(error): return .error(error) - } - } - - /** - Transform a result into another result using a function. If the result was an error, - the function will not be executed and the error returned instead. - */ - public func flatMap(_ f: (T) -> Result) -> Result { - switch self { - case let .success(v): return f(v) - case let .error(error): return .error(error) - } - } - - /** - Transform a result into another result using a function. If the result was an error, - the function will not be executed and the error returned instead. - */ - public func flatMap(_ f: (T) throws -> U) -> Result { - return flatMap { t in - do { - return .success(try f(t)) - } catch let error { - return .error(error) - } - } - } - /** - Transform a result into another result using a function. If the result was an error, - the function will not be executed and the error returned instead. - */ - public func flatMap(_ f:@escaping (T, (@escaping(Result)->Void))->Void) -> (@escaping(Result)->Void)->Void { - return { g in - switch self { - case let .success(v): f(v, g) - case let .error(error): g(.error(error)) - } - } - } - - /** - Call a function with the result as an argument. Use this if the function should be - executed no matter if the result was a success or not. - */ - public func ensure(_ f: (Result) -> Result) -> Result { - return f(self) - } - - /** - Call a function with the result as an argument. Use this if the function should be - executed no matter if the result was a success or not. - */ - public func ensure(_ f:@escaping (Result, ((Result)->Void))->Void) -> ((Result)->Void)->Void { - return { g in - f(self, g) - } - } - - /** - Direct access to the content of the result as an optional. If the result was a success, - the optional will contain the value of the result. - */ - public var value: T? { - switch self { - case let .success(v): return v - case .error(_): return nil - } - } - - /** - Direct access to the error of the result as an optional. If the result was an error, - the optional will contain the error of the result. - */ - public var error: Error? { - switch self { - case .success: return nil - case .error(let x): return x - } - } - - /** - Access the value of this result. If the result contains an error, that error is thrown. - */ - public func get() throws -> T { - switch self { - case let .success(value): return value - case .error(let error): throw error - } - } -} - - -/** - Provide a default value for failed results. -*/ -public func ?? (result: Result, defaultValue: @autoclosure () -> T) -> T { - switch result { - case .success(let x): return x - case .error: return defaultValue() - } -} +//public enum Result: ResultType { +// case success(T) +// case error(Error) +// +// /** +// Initialize a result containing a successful value. +// */ +// public init(success value: T) { +// self = Result.success(value) +// } +// +// /** +// Initialize a result containing an error +// */ +// public init(error: Error) { +// self = .error(error) +// } +// +// /** +// Transform a result into another result using a function. If the result was an error, +// the function will not be executed and the error returned instead. +// */ +// public func map(_ f: @escaping (T) -> U) -> Result { +// switch self { +// case let .success(v): return .success(f(v)) +// case let .error(error): return .error(error) +// } +// } +// +// /** +// Transform a result into another result using a function. If the result was an error, +// the function will not be executed and the error returned instead. +// */ +// public func flatMap(_ f: (T) -> Result) -> Result { +// switch self { +// case let .success(v): return f(v) +// case let .error(error): return .error(error) +// } +// } +// +// /** +// Transform a result into another result using a function. If the result was an error, +// the function will not be executed and the error returned instead. +// */ +// public func flatMap(_ f: (T) throws -> U) -> Result { +// return flatMap { t in +// do { +// return .success(try f(t)) +// } catch let error { +// return .error(error) +// } +// } +// } +// /** +// Transform a result into another result using a function. If the result was an error, +// the function will not be executed and the error returned instead. +// */ +// public func flatMap(_ f:@escaping (T, (@escaping(Result)->Void))->Void) -> (@escaping(Result)->Void)->Void { +// return { g in +// switch self { +// case let .success(v): f(v, g) +// case let .error(error): g(.error(error)) +// } +// } +// } +// +// /** +// Call a function with the result as an argument. Use this if the function should be +// executed no matter if the result was a success or not. +// */ +// public func ensure(_ f: (Result) -> Result) -> Result { +// return f(self) +// } +// +// /** +// Call a function with the result as an argument. Use this if the function should be +// executed no matter if the result was a success or not. +// */ +// public func ensure(_ f:@escaping (Result, ((Result)->Void))->Void) -> ((Result)->Void)->Void { +// return { g in +// f(self, g) +// } +// } +// +// /** +// Direct access to the content of the result as an optional. If the result was a success, +// the optional will contain the value of the result. +// */ +// public var value: T? { +// switch self { +// case let .success(v): return v +// case .error(_): return nil +// } +// } +// +// /** +// Direct access to the error of the result as an optional. If the result was an error, +// the optional will contain the error of the result. +// */ +// public var error: Error? { +// switch self { +// case .success: return nil +// case .error(let x): return x +// } +// } +// +// /** +// Access the value of this result. If the result contains an error, that error is thrown. +// */ +// public func get() throws -> T { +// switch self { +// case let .success(value): return value +// case .error(let error): throw error +// } +// } +//} +// +// +///** +// Provide a default value for failed results. +//*/ +//public func ?? (result: Result, defaultValue: @autoclosure () -> T) -> T { +// switch result { +// case .success(let x): return x +// case .error: return defaultValue() +// } +//} diff --git a/Sources/Interstellar/ResultType.swift b/Sources/Interstellar/ResultType.swift deleted file mode 100644 index 5bdb932..0000000 --- a/Sources/Interstellar/ResultType.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// ResultType.swift -// Interstellar -// -// Created by Jens Ravens on 10/12/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -/// Conform to ResultType to use your own result type, e.g. from other libraries with Interstellar. -public protocol ResultType { - /// Describes the contained successful type of this result. - associatedtype Value - - /// Return an error if the result is unsuccessful, otherwise nil. - var error: Error? { get } - - /// Return the value if the result is successful, otherwise nil. - var value: Value? { get } - - /// Convert this result into an `Interstellar.Result`. This implementation is optional. - var result: Result { get } -} - -extension ResultType { - public var result: Result { - return Result(value: value, error: error) - } -} - -extension Result { - public init(value: T?, error: Error?) { - if let error = error { - self = .error(error) - } else { - self = .success(value!) - } - } - - public init(block: () throws -> T) { - do { - self = try .success(block()) - } catch let e { - self = .error(e) - } - } - - public var result: Result { - return self - } -} diff --git a/Sources/Interstellar/Signal.swift b/Sources/Interstellar/Signal.swift deleted file mode 100644 index 6303f79..0000000 --- a/Sources/Interstellar/Signal.swift +++ /dev/null @@ -1,264 +0,0 @@ -// Signal.swift -// -// Copyright (c) 2015 Jens Ravens (http://jensravens.de) -// -// 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. - -/** - A Signal is value that will or will not contain a value in the future (just - like the concept of futures). In contrast to futures, the value of a signal - can change at any time. - - Use next to subscribe to .Success updates, .error for .Error updates and - update to update the current value of the signal. - - let text = Signal() - - text.next { string in - println("Hello \(string)") - } - - text.update(.success("World")) - -*/ - -@available(*, deprecated: 2.0, message:"Use Observable> instead.") -public final class Signal { - - fileprivate var value: Result? - fileprivate var callbacks: [(Result) -> Void] = [] - fileprivate let mutex = Mutex() - - /// Automatically infer the type of the signal from the argument. - public convenience init(_ value: T){ - self.init() - self.value = .success(value) - } - - public init() { - - } - - /** - Transform the signal into another signal using a function. - */ - public func map(_ f: @escaping (T) -> U) -> Signal { - let signal = Signal() - subscribe { result in - signal.update(result.map(f)) - } - return signal - } - - /** - Transform the signal into another signal using a function. - */ - public func flatMap(_ f: @escaping (T) -> Result) -> Signal { - let signal = Signal() - subscribe { result in - signal.update(result.flatMap(f)) - } - return signal - } - - /** - Transform the signal into another signal using a function. - */ - public func flatMap(_ f: @escaping (T) throws -> U) -> Signal { - let signal = Signal() - subscribe { result in - signal.update(result.flatMap(f)) - } - return signal - } - - /** - Transform the signal into another signal using a function. - */ - public func flatMap(_ f: @escaping (T, (@escaping(Result)->Void))->Void) -> Signal { - let signal = Signal() - subscribe { result in - result.flatMap(f)(signal.update) - } - return signal - } - - /** - Transform the signal into another signal using a function, return the - value of the inner signal - */ - public func flatMap(_ f: @escaping ((T) -> Signal)) -> Signal { - let signal = Signal() - subscribe { result in - switch(result) { - case let .success(value): - let innerSignal = f(value) - innerSignal.subscribe { innerResult in - signal.update(innerResult) - } - case let .error(error): - signal.update(.error(error)) - } - } - return signal - } - - /** - Call a function with the result as an argument. Use this if the function should be - executed no matter if the signal is a success or not. - This method can also be used to convert an .Error into a .Success which might be handy - for retry logic. - */ - public func ensure(_ f: @escaping (Result, ((Result)->Void))->Void) -> Signal { - let signal = Signal() - subscribe { result in - f(result) { signal.update($0) } - } - return signal - } - - /** - Subscribe to the changes of this signal (.Error and .Success). - This method is chainable. - */ - @discardableResult public func subscribe(_ f: @escaping (Result) -> Void) -> Signal { - if let value = value { - f(value) - } - mutex.lock { - callbacks.append(f) - } - return self - } - - public func filter(_ f: @escaping (T) -> Bool) -> Signal{ - let signal = Signal() - subscribe { result in - switch(result) { - case let .success(value): - if f(value) { - signal.update(result) - } - case let .error(error): signal.update(.error(error)) - } - } - return signal - } - - /** - Subscribe to the changes of this signal (.Success only). - This method is chainable. - */ - @discardableResult public func next(_ g: @escaping (T) -> Void) -> Signal { - subscribe { result in - switch(result) { - case let .success(value): g(value) - case .error(_): return - } - } - return self - } - - /** - Subscribe to the changes of this signal (.Error only). - This method is chainable. - */ - @discardableResult public func error(_ g: @escaping (Error) -> Void) -> Signal { - subscribe { result in - switch(result) { - case .success(_): return - case let .error(error): g(error) - } - } - return self - } - - /** - Merge another signal into the current signal. This creates a signal that is - a success if both source signals are a success. The value of the signal is a - Tuple of the values of the contained signals. - - let signal = Signal("Hello").merge(Signal("World")) - signal.value! == ("Hello", "World") - - */ - public func merge(_ merge: Signal) -> Signal<(T,U)> { - let signal = Signal<(T,U)>() - self.next { a in - if let b = merge.peek() { - signal.update(.success((a,b))) - } - } - merge.next { b in - if let a = self.peek() { - signal.update(.success((a,b))) - } - } - let errorHandler = { (error: Error) in - signal.update(error) - } - self.error(errorHandler) - merge.error(errorHandler) - return signal - } - - /** - Update the content of the signal. This will notify all subscribers of this signal - about the new value. - */ - public func update(_ result: Result) { - mutex.lock { - value = result - callbacks.forEach{$0(result)} - } - } - - /** - Update the content of the signal. This will notify all subscribers of this signal - about the new value. - */ - public func update(_ value: T) { - update(.success(value)) - } - - /** - Update the content of the signal. This will notify all subscribers of this signal - about the new value. - */ - public func update(_ error: Error) { - update(.error(error)) - } - - /** - Direct access to the content of the signal as an optional. If the result was a success, - the optional will contain the value of the result. - */ - public func peek() -> T? { - return value?.value - } -} - -@available(*, deprecated: 2.0) -extension Signal { - func observable() -> Observable> { - let observable = Observable>() - subscribe(observable.update) - return observable - } -} diff --git a/Sources/Interstellar/Warpdrive/Debounce.swift b/Sources/Interstellar/Warpdrive/Debounce.swift index acc2e47..a6c4541 100644 --- a/Sources/Interstellar/Warpdrive/Debounce.swift +++ b/Sources/Interstellar/Warpdrive/Debounce.swift @@ -22,50 +22,13 @@ import Foundation -@available(*, deprecated: 2.0) -public extension Signal { - /** - Creates a new signal that is only firing once per specified time interval. The last - call to update will always be delivered (although it might be delayed up to the - specified amount of seconds). - */ - public func debounce(_ seconds: TimeInterval) -> Signal { - let signal = Signal() - var lastCalled: Date? - - subscribe { result in - let currentTime = Date() - func updateIfNeeded(_ signal: Signal) -> (Result) -> Void { - return { result in - let timeSinceLastCall = lastCalled?.timeIntervalSinceNow - if timeSinceLastCall == nil || timeSinceLastCall! <= -seconds { - // no update before or update outside of debounce window - lastCalled = Date() - signal.update(result) - } else { - // skip result if there was a newer result - if currentTime.compare(lastCalled!) == .orderedDescending { - let s = Signal() - s.delay(seconds - timeSinceLastCall!).subscribe(updateIfNeeded(signal)) - s.update(result) - } - } - } - } - updateIfNeeded(signal)(result) - } - - return signal - } -} - public extension Observable { /** Creates a new signal that is only firing once per specified time interval. The last call to update will always be delivered (although it might be delayed up to the specified amount of seconds). */ - public func debounce(_ seconds: TimeInterval) -> Observable { + func debounce(_ seconds: TimeInterval) -> Observable { let observable = Observable() var lastCalled: Date? diff --git a/Sources/Interstellar/Warpdrive/Delay.swift b/Sources/Interstellar/Warpdrive/Delay.swift index 8f8a538..f5d35ef 100644 --- a/Sources/Interstellar/Warpdrive/Delay.swift +++ b/Sources/Interstellar/Warpdrive/Delay.swift @@ -23,27 +23,11 @@ import Dispatch import Foundation -@available(*, deprecated: 2.0) -public extension Signal { - /** - Creates a new signal that mirrors the original signal but is delayed by x seconds. If no queue is specified, the new signal will call it's observers and transforms on the main queue. - */ - public func delay(_ seconds: TimeInterval, queue: DispatchQueue = DispatchQueue.main) -> Signal { - let signal = Signal() - subscribe { result in - queue.asyncAfter(deadline: DispatchTime.now() + seconds) { - signal.update(result) - } - } - return signal - } -} - public extension Observable { /** Creates a new observable that mirrors the original observable but is delayed by x seconds. If no queue is specified, the new observable will call it's observers and transforms on the main queue. */ - public func delay(_ seconds: TimeInterval, queue: DispatchQueue = DispatchQueue.main) -> Observable { + func delay(_ seconds: TimeInterval, queue: DispatchQueue = DispatchQueue.main) -> Observable { let observable = Observable() subscribe { result in queue.asyncAfter(deadline: DispatchTime.now() + seconds) { diff --git a/Sources/Interstellar/Warpdrive/Waiting.swift b/Sources/Interstellar/Warpdrive/Waiting.swift index 6a317bd..91a768f 100644 --- a/Sources/Interstellar/Warpdrive/Waiting.swift +++ b/Sources/Interstellar/Warpdrive/Waiting.swift @@ -34,37 +34,12 @@ public struct TimeoutError: Error { internal init() {} } -@available(*, deprecated: 2.0) -public extension Signal { - /** - Wait until the signal updates the next time. This will block the current thread until there - is an error or a successfull value. In case of an error, the error will be thrown. - */ - public func wait(_ timeout: TimeInterval? = nil) throws -> T { - let group = DispatchGroup() - var result: Result? - group.enter() - subscribe { r in - result = r - group.leave() - } - let timestamp = timeout.map{ DispatchTime.now() + $0 } ?? DispatchTime.distantFuture - if group.wait(timeout: timestamp) != .success { - throw TimeoutError() - } - switch result! { - case let .success(t): return t - case let .error(e): throw e - } - } -} - public extension Observable { /** Wait until the observable updates the next time. This will block the current thread until there is a new value. */ - public func wait(_ timeout: TimeInterval? = nil) throws -> T { + func wait(_ timeout: TimeInterval? = nil) throws -> T { let group = DispatchGroup() var value: T! = nil group.enter() diff --git a/Tests/InterstellarTests/ResultObservableTests.swift b/Tests/InterstellarTests/ResultObservableTests.swift index 00bafcd..4d0dfee 100644 --- a/Tests/InterstellarTests/ResultObservableTests.swift +++ b/Tests/InterstellarTests/ResultObservableTests.swift @@ -9,6 +9,19 @@ import XCTest import Interstellar +internal class Fail: Error, Equatable { + public static func ==(lhs: Fail, rhs: Fail) -> Bool { + return lhs.error == rhs.error + } + + let error: String + + internal init(_ error: String) { + self.error = error + } +} + + class ResultObservableTests: XCTestCase { func greeter(_ subject: String) -> String { @@ -27,21 +40,21 @@ class ResultObservableTests: XCTestCase { return Observable("Hello \(subject)") } - func asyncFail(_ subject: String) -> Observable> { - return Observable(.error(Fail("Fail"))) + func asyncFail(_ subject: String) -> Observable> { + return Observable(.failure(Fail("Fail"))) } - func neverCallMe(_ subject: String) -> Observable> { + func neverCallMe(_ subject: String) -> Observable> { XCTFail() return Observable() } - var world: Observable> { - return Observable(Result(success: "World")) + var world: Observable> { + return Observable(Result.success("World")) } - var nothing: Observable> { - return Observable(Result(success: "")) + var nothing: Observable> { + return Observable(Result.success("")) } func testContinuingTheChain() { @@ -53,12 +66,12 @@ class ResultObservableTests: XCTestCase { let greeting = nothing.then(throwingGreeter).peek() XCTAssertNil(greeting) } - + func testAsyncChain() { let greeting = world.then(asyncGreeter) XCTAssertEqual(greeting.peek()!, "Hello World") } - + func testAsyncFail() { let greeting = world.then(asyncFail).then(neverCallMe) var error: Error? diff --git a/Tests/InterstellarTests/ResultTests.swift b/Tests/InterstellarTests/ResultTests.swift deleted file mode 100644 index 8552e4d..0000000 --- a/Tests/InterstellarTests/ResultTests.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// ResultTests.swift -// Interstellar -// -// Created by Jens Ravens on 04/12/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -import XCTest -import Interstellar - -class ResultTests: XCTestCase { - - func greeter(_ subject: String) -> Result { - if subject.count > 0 { - return .success("Hello \(subject)") - } else { - return .error(Fail("No one to greet!")) - } - } - - func throwingGreeter(_ subject: String) throws -> String { - return try greeter(subject).get() - } - - func identity(_ a: String) -> String { - return a - } - - struct NastyError: Error {} - - func testAccessingAValue() { - let result = Result(success: "hello") - XCTAssertEqual(result.value, "hello") - XCTAssertNil(result.error) - } - - func testAccessingAnError() { - let error = Fail("") - let result = Result(error: error) - XCTAssertNil(result.value) - XCTAssertEqual(result.error as? Fail, error) - } - - func testThrowingAccessorReturns() { - let result = Result(success: "hello") - XCTAssertEqual(try! result.get(), "hello") - } - - func testThrowingAccessorThrows() { - let error = Fail("") - let result = Result(error: error) - do { - let _ = try result.get() - XCTFail() - } catch let e { - XCTAssertEqual(e as! Fail, error) - } - } - - func testMappingAResult() { - let result = Result(success: "Hello World").map(identity) - XCTAssertEqual(result.value, "Hello World") - } - - func testFlatMappingAResult() { - let greeting = Result(success: "World").flatMap(greeter) - XCTAssertEqual(greeting.value, "Hello World") - } - - func testError() { - let greeting = Result(success: "").flatMap(greeter) - XCTAssertNil(greeting.value) - XCTAssertNotNil(greeting.error) - } - - func testThrowingFunction() { - let result = Result(success: "World").flatMap(throwingGreeter) - XCTAssertEqual(result.value, "Hello World") - } - - func testNoEscape() { - let result = Result(success: "World") - let mapped = result.flatMap { string in - return greeter(string) - } - XCTAssertEqual(mapped.value, "Hello World") - } - - func testDefaultValue() { - let a = Result(success: "Hello") ?? "Bonjour" - XCTAssertEqual(a, "Hello") - - let b = Result(error: NastyError()) ?? "Bonjour" - XCTAssertEqual(b, "Bonjour") - } -} diff --git a/Tests/InterstellarTests/SignalTests.swift b/Tests/InterstellarTests/SignalTests.swift deleted file mode 100644 index f7ff930..0000000 --- a/Tests/InterstellarTests/SignalTests.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// InterstellarTests.swift -// InterstellarTests -// -// Created by Jens Ravens on 16/05/15. -// Copyright (c) 2015 nerdgeschoss GmbH. All rights reserved. -// - -import XCTest -import Interstellar - -internal class Fail: Error, Equatable { - public static func ==(lhs: Fail, rhs: Fail) -> Bool { - return lhs.error == rhs.error - } - - let error: String - - internal init(_ error: String) { - self.error = error - } -} - -@available(*, deprecated: 2.0) -class SignalTests: XCTestCase { - - func greeter(_ subject: String) -> Result { - if subject.characters.count > 0 { - return .success("Hello \(subject)") - } else { - return .error(Fail("No one to greet!")) - } - } - - func identity(_ a: String) -> Result { - return .success(a) - } - - func asyncIdentity(_ a: String, completion: (Result)->Void) { - completion(identity(a)) - } - - func testMappingASignal() { - let greeting = Signal("World").map { subject in - "Hello \(subject)" - } - XCTAssertEqual(greeting.peek(), "Hello World") - } - - func testBindingASignal() { - let greeting = Signal("World").flatMap(greeter).peek() - XCTAssertEqual(greeting, "Hello World") - } - - func testFlatMappingASignal() { - let greeting = Signal("Hello").flatMap { greeting in - Signal(greeting + " World") - }.peek() - XCTAssertEqual(greeting, "Hello World") - } - - func testError() { - let greeting = Signal("").flatMap(greeter).peek() - XCTAssertNil(greeting) - } - - func testSubscription() { - let signal = Signal() - let expectation = self.expectation(description: "subscription not completed") - signal.next { a in - expectation.fulfill() - } - signal.update(Result(success:"Hello")) - waitForExpectations(timeout: 0.2, handler: nil) - } - - func testThrowingFunction() { - func throwing(_ i: Int) throws -> Int { - throw Fail("Error") - } - - let transformed = Result(success: 1).flatMap(throwing) - - XCTAssertNil(transformed.value) - } - - func testThrowingSignal() { - func throwing(_ i: Int) throws -> Int { - throw Fail("Error") - } - - let signal = Signal() - let expectation = self.expectation(description: "subscription not completed") - - signal.flatMap(throwing).error { _ in expectation.fulfill() } - signal.update(.success(1)) - - waitForExpectations(timeout: 0.2, handler: nil) - } -} diff --git a/Tests/WarpdriveTests/DebounceTests.swift b/Tests/WarpdriveTests/DebounceTests.swift deleted file mode 100644 index 051b923..0000000 --- a/Tests/WarpdriveTests/DebounceTests.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// WarpDriveTests.swift -// WarpDriveTests -// -// Created by Jens Ravens on 13/10/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -import Foundation -import XCTest -@testable import Interstellar - -@available(*, deprecated: 2.0) -class DebounceTests: XCTestCase { - - func testDebounceImmediateley() { - var string: String? = nil - let s = Signal() - s.debounce(0).next { string = $0 } - s.update("Hello") - XCTAssertEqual(string, "Hello") - } - - func testDebounceImmediatelyIfFirst () { - var string: String? = nil - let s = Signal() - s.debounce(5).next { string = $0 } - s.update("Hello") - XCTAssertEqual(string, "Hello") - } - - func testDebounce() { - var string: String? = nil - var called = 0 - let signal = Signal() - let expectation = self.expectation(description: "Wait for debounce") - - signal.debounce(0.5).next { called += 1; string = $0 } - signal.update("Hello") - signal.update("World") - - Signal(0).delay(1).next { _ in - XCTAssertEqual(called, 2) - XCTAssertEqual(string, "World") - expectation.fulfill() - } - - XCTAssertEqual(called, 1) - XCTAssertEqual(string, "Hello") - waitForExpectations(timeout: 2, handler: nil) - } - - func testDebounceObservable() { - var string: String? = nil - var called = 0 - let observable = Observable() - let expectation = self.expectation(description: "Wait for debounce") - - observable.debounce(0.5).subscribe { called += 1; string = $0 } - observable.update("Hello") - observable.update("World") - - Signal(1).delay(1).next { _ in - XCTAssertEqual(called, 2) - XCTAssertEqual(string, "World") - expectation.fulfill() - } - - XCTAssertEqual(called, 1) - XCTAssertEqual(string, "Hello") - waitForExpectations(timeout: 2, handler: nil) - } - -} diff --git a/Tests/WarpdriveTests/DelayTests.swift b/Tests/WarpdriveTests/DelayTests.swift deleted file mode 100644 index ff2fad2..0000000 --- a/Tests/WarpdriveTests/DelayTests.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// WarpDriveTests.swift -// WarpDriveTests -// -// Created by Jens Ravens on 13/10/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -import Foundation -import XCTest -import Interstellar -import Dispatch - -@available(*, deprecated: 2.0) -class DelayTests: XCTestCase { - func testShouldDispatchToMainQueue() { - let expectation = self.expectation(description: "delay called") - Signal("test").delay(0.1).subscribe { _ in - XCTAssertTrue(Thread.isMainThread) - expectation.fulfill() - } - waitForExpectations(timeout: 0.2, handler: nil) - } - - func testDispatchToSelectedQueue() { - let queue = DispatchQueue.global(qos: .default) - let expectation = self.expectation(description: "delay called") - let s = Signal() - s.delay(0.1, queue: queue) - .subscribe { _ in - XCTAssertFalse(Thread.isMainThread) - expectation.fulfill() - } - s.update("hello") - waitForExpectations(timeout: 0.2, handler: nil) - } - - func testDispatchAfterGivenTime() { - // wait 0.2 seconds and check if action from 0.1 seconds already happened - var value: String? = nil - let expectation = self.expectation(description: "delay called") - Signal("test").delay(0.2).subscribe { _ in - XCTAssertEqual(value, "value") - expectation.fulfill() - } - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { - value = "value" - } - waitForExpectations(timeout: 0.2, handler: nil) - } -} diff --git a/Tests/WarpdriveTests/ThreadingTests.swift b/Tests/WarpdriveTests/ThreadingTests.swift deleted file mode 100644 index 54b9d53..0000000 --- a/Tests/WarpdriveTests/ThreadingTests.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// WarpDriveTests.swift -// WarpDriveTests -// -// Created by Jens Ravens on 13/10/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -import Foundation -import XCTest -@testable import Interstellar - -func mainTest(_ expectation: XCTestExpectation?, _ r: Result, completion:((Result)->Void)) { - XCTAssertTrue(Thread.isMainThread) - expectation?.fulfill() -} - -class ThreadingTests: XCTestCase { -// func testShouldDispatchToMainQueue() { -// let expectation = self.expectation(description: "thread called") -// let queue = DispatchQueue.global(qos: .default) -// queue.async { -// let s = Signal() -// s.ensure(Interstellar.Thread.main) -// .ensure(mainTest(expectation)) -// s.update("hello") -// } -// waitForExpectations(timeout: 0.1, handler: nil) -// } -// -// func testDispatchToSelectedQueue() { -// let expectation = self.expectation(description: "thread called") -// let s = Signal() -// s.ensure(Interstellar.Thread.background) -// .subscribe { _ in -// XCTAssertFalse(NSThread.isMainThread()) -// expectation.fulfill() -// } -// s.update("hello") -// waitForExpectations(timeout: 0.1, handler: nil) -// } - - func testObservable() { - let observable = Observable() - let log: (String) -> Void = { print($0) } - observable.flatMap(Queue.main).subscribe(log) - - } -} diff --git a/Tests/WarpdriveTests/WaitingTests.swift b/Tests/WarpdriveTests/WaitingTests.swift deleted file mode 100644 index fe59f1a..0000000 --- a/Tests/WarpdriveTests/WaitingTests.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// WarpDriveTests.swift -// WarpDriveTests -// -// Created by Jens Ravens on 13/10/15. -// Copyright © 2015 nerdgeschoss GmbH. All rights reserved. -// - -import Foundation -import XCTest -import Interstellar -import Dispatch - -fileprivate func asyncOperation(value: String, completion: (Result)->Void) { - completion(Result.success(value)) -} - -fileprivate func longOperation(value: String, completion: @escaping (Result)->Void) { - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10){ - completion(Result.success(value)) - } -} - -fileprivate func fail(_ t: T) throws -> T { - throw NSError(domain: "Error", code: 400, userInfo: nil) -} - -@available(*, deprecated: 2.0) -class WaitingTests: XCTestCase { - func testWaitingForSuccess() { - let greeting = try! Signal("hello") - .flatMap(asyncOperation) - .wait() - XCTAssertEqual(greeting, "hello") - } - - func testWithinTimeoutForSuccess() { - let greeting = try! Signal("hello") - .flatMap(asyncOperation) - .wait(0.3) - XCTAssertEqual(greeting, "hello") - } - - func testWithinTimeoutForFail() { - let sig = Signal("hello") - .flatMap(longOperation) - let greeting = try? sig.wait(0.1) - XCTAssertEqual(greeting, nil) - } - - func testWaitingForFail() { - do { - let _ = try Signal("hello") - .flatMap(asyncOperation) - .flatMap(fail) - .wait() - XCTFail("This place should never be reached due to an error.") - } catch { - - } - } -}