diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..681d255 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,6 @@ +excluded: # paths to ignore during linting. overridden by `included`. + - Carthage + - Pods + +disabled_rules: # rule identifiers to exclude from running + - force_cast diff --git a/ReduxKitRxSwift.xcodeproj/project.pbxproj b/ReduxKitRxSwift.xcodeproj/project.pbxproj index 27afd15..a215bd7 100644 --- a/ReduxKitRxSwift.xcodeproj/project.pbxproj +++ b/ReduxKitRxSwift.xcodeproj/project.pbxproj @@ -11,10 +11,6 @@ 7398B0161C25FEF5000ED3FD /* ReduxKitRxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7398B00B1C25FEF5000ED3FD /* ReduxKitRxSwift.framework */; }; 7398B0411C25FF34000ED3FD /* ReduxKitRxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7398B0371C25FF34000ED3FD /* ReduxKitRxSwift.framework */; }; 7398B05D1C25FF4B000ED3FD /* ReduxKitRxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7398B0531C25FF4B000ED3FD /* ReduxKitRxSwift.framework */; }; - 7398B06B1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */; }; - 7398B06C1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */; }; - 7398B06D1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */; }; - 7398B06E1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */; }; 7398B0741C2602BF000ED3FD /* ReduxKitRxSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7398B01A1C25FEF5000ED3FD /* ReduxKitRxSwiftTests.swift */; }; 7398B0791C260489000ED3FD /* ReduxKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7398B0771C260489000ED3FD /* ReduxKit.framework */; }; 7398B07A1C260489000ED3FD /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7398B0781C260489000ED3FD /* RxSwift.framework */; }; @@ -42,10 +38,26 @@ 73A125DC1C2A187400E4E928 /* ObservableAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DA1C2A187400E4E928 /* ObservableAction.swift */; }; 73A125DD1C2A187400E4E928 /* ObservableAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DA1C2A187400E4E928 /* ObservableAction.swift */; }; 73A125DE1C2A187400E4E928 /* ObservableAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DA1C2A187400E4E928 /* ObservableAction.swift */; }; - 73A125E01C2A196600E4E928 /* ObservableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */; }; - 73A125E11C2A196600E4E928 /* ObservableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */; }; - 73A125E21C2A196600E4E928 /* ObservableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */; }; - 73A125E31C2A196600E4E928 /* ObservableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */; }; + 73A125E01C2A196600E4E928 /* observableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */; }; + 73A125E11C2A196600E4E928 /* observableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */; }; + 73A125E21C2A196600E4E928 /* observableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */; }; + 73A125E31C2A196600E4E928 /* observableMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */; }; + 73E4859A1C2F7BAD00462A12 /* Store+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485991C2F7BAD00462A12 /* Store+init.swift */; }; + 73E4859B1C2F7BAD00462A12 /* Store+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485991C2F7BAD00462A12 /* Store+init.swift */; }; + 73E4859C1C2F7BAD00462A12 /* Store+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485991C2F7BAD00462A12 /* Store+init.swift */; }; + 73E4859D1C2F7BAD00462A12 /* Store+init.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485991C2F7BAD00462A12 /* Store+init.swift */; }; + 73E4859F1C2F7BE000462A12 /* setupStoreBacking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */; }; + 73E485A01C2F7BE000462A12 /* setupStoreBacking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */; }; + 73E485A11C2F7BE000462A12 /* setupStoreBacking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */; }; + 73E485A21C2F7BE000462A12 /* setupStoreBacking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */; }; + 73E485A41C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */; }; + 73E485A51C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */; }; + 73E485A61C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */; }; + 73E485A71C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */; }; + 73EF72FC1C2F4F4E00F5E57D /* createStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */; }; + 73EF72FD1C2F515600F5E57D /* createStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */; }; + 73EF72FE1C2F515700F5E57D /* createStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */; }; + 73EF72FF1C2F515800F5E57D /* createStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -84,7 +96,6 @@ 7398B0401C25FF34000ED3FD /* ReduxKitRxSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReduxKitRxSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7398B0531C25FF4B000ED3FD /* ReduxKitRxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReduxKitRxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7398B05C1C25FF4B000ED3FD /* ReduxKitRxSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReduxKitRxSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReduxKitRxSwift.swift; sourceTree = ""; }; 7398B06F1C26022A000ED3FD /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 7398B0761C260480000ED3FD /* Cartfile.resolved */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.resolved; sourceTree = ""; }; 7398B0771C260489000ED3FD /* ReduxKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReduxKit.framework; path = Carthage/Build/iOS/ReduxKit.framework; sourceTree = ""; }; @@ -101,7 +112,11 @@ 73A125D51C2A067600E4E928 /* ReduxKitRxSwift.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReduxKitRxSwift.podspec; sourceTree = ""; }; 73A125D61C2A149900E4E928 /* ObservableMiddlewareTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableMiddlewareTests.swift; sourceTree = ""; }; 73A125DA1C2A187400E4E928 /* ObservableAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableAction.swift; sourceTree = ""; }; - 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableMiddleware.swift; sourceTree = ""; }; + 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = observableMiddleware.swift; sourceTree = ""; }; + 73E485991C2F7BAD00462A12 /* Store+init.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Store+init.swift"; sourceTree = ""; }; + 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = setupStoreBacking.swift; sourceTree = ""; }; + 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReduxRxDisposable.swift; sourceTree = ""; }; + 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = createStore.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -211,9 +226,12 @@ children = ( 7398B00E1C25FEF5000ED3FD /* ReduxKitRxSwift.h */, 7398B0101C25FEF5000ED3FD /* Info.plist */, + 73EF72FB1C2F4F4E00F5E57D /* createStore.swift */, 73A125DA1C2A187400E4E928 /* ObservableAction.swift */, - 73A125DF1C2A196600E4E928 /* ObservableMiddleware.swift */, - 7398B06A1C26007D000ED3FD /* ReduxKitRxSwift.swift */, + 73A125DF1C2A196600E4E928 /* observableMiddleware.swift */, + 73E485A31C2F7CA000462A12 /* ReduxRxDisposable.swift */, + 73E4859E1C2F7BE000462A12 /* setupStoreBacking.swift */, + 73E485991C2F7BAD00462A12 /* Store+init.swift */, ); path = ReduxKitRxSwift; sourceTree = ""; @@ -309,6 +327,7 @@ 7398B0081C25FEF5000ED3FD /* Headers */, 7398B0091C25FEF5000ED3FD /* Resources */, 7398B0981C261059000ED3FD /* ShellScript */, + 73FDC45D1C2FB377006AB4EC /* ShellScript */, ); buildRules = ( ); @@ -347,6 +366,7 @@ 7398B0271C25FF1F000ED3FD /* Headers */, 7398B0281C25FF1F000ED3FD /* Resources */, 7398B09A1C261090000ED3FD /* ShellScript */, + 73FDC45F1C2FB38C006AB4EC /* ShellScript */, ); buildRules = ( ); @@ -366,6 +386,7 @@ 7398B0341C25FF34000ED3FD /* Headers */, 7398B0351C25FF34000ED3FD /* Resources */, 7398B09B1C2610AB000ED3FD /* ShellScript */, + 73FDC45E1C2FB384006AB4EC /* ShellScript */, ); buildRules = ( ); @@ -404,6 +425,7 @@ 7398B0501C25FF4B000ED3FD /* Headers */, 7398B0511C25FF4B000ED3FD /* Resources */, 7398B08D1C260619000ED3FD /* ShellScript */, + 73E485A91C2FA1D000462A12 /* ShellScript */, ); buildRules = ( ); @@ -647,6 +669,58 @@ shellPath = /bin/sh; shellScript = "/usr/local/bin/carthage copy-frameworks"; }; + 73E485A91C2FA1D000462A12 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + 73FDC45D1C2FB377006AB4EC /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + 73FDC45E1C2FB384006AB4EC /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi"; + }; + 73FDC45F1C2FB38C006AB4EC /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -654,9 +728,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73E4859A1C2F7BAD00462A12 /* Store+init.swift in Sources */, 73A125DB1C2A187400E4E928 /* ObservableAction.swift in Sources */, - 7398B06B1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */, - 73A125E01C2A196600E4E928 /* ObservableMiddleware.swift in Sources */, + 73E485A41C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */, + 73EF72FC1C2F4F4E00F5E57D /* createStore.swift in Sources */, + 73A125E01C2A196600E4E928 /* observableMiddleware.swift in Sources */, + 73E4859F1C2F7BE000462A12 /* setupStoreBacking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -674,9 +751,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73E4859D1C2F7BAD00462A12 /* Store+init.swift in Sources */, 73A125DE1C2A187400E4E928 /* ObservableAction.swift in Sources */, - 7398B06C1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */, - 73A125E31C2A196600E4E928 /* ObservableMiddleware.swift in Sources */, + 73E485A71C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */, + 73EF72FF1C2F515800F5E57D /* createStore.swift in Sources */, + 73A125E31C2A196600E4E928 /* observableMiddleware.swift in Sources */, + 73E485A21C2F7BE000462A12 /* setupStoreBacking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -684,9 +764,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73E4859C1C2F7BAD00462A12 /* Store+init.swift in Sources */, 73A125DD1C2A187400E4E928 /* ObservableAction.swift in Sources */, - 7398B06D1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */, - 73A125E21C2A196600E4E928 /* ObservableMiddleware.swift in Sources */, + 73E485A61C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */, + 73EF72FE1C2F515700F5E57D /* createStore.swift in Sources */, + 73A125E21C2A196600E4E928 /* observableMiddleware.swift in Sources */, + 73E485A11C2F7BE000462A12 /* setupStoreBacking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -704,9 +787,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 73E4859B1C2F7BAD00462A12 /* Store+init.swift in Sources */, 73A125DC1C2A187400E4E928 /* ObservableAction.swift in Sources */, - 7398B06E1C26007D000ED3FD /* ReduxKitRxSwift.swift in Sources */, - 73A125E11C2A196600E4E928 /* ObservableMiddleware.swift in Sources */, + 73E485A51C2F7CA000462A12 /* ReduxRxDisposable.swift in Sources */, + 73EF72FD1C2F515600F5E57D /* createStore.swift in Sources */, + 73A125E11C2A196600E4E928 /* observableMiddleware.swift in Sources */, + 73E485A01C2F7BE000462A12 /* setupStoreBacking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -752,6 +838,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; @@ -805,6 +892,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; diff --git a/ReduxKitRxSwift.xcodeproj/project.xcworkspace/xcuserdata/agentk.xcuserdatad/UserInterfaceState.xcuserstate b/ReduxKitRxSwift.xcodeproj/project.xcworkspace/xcuserdata/agentk.xcuserdatad/UserInterfaceState.xcuserstate index 8f8e7fe..a1d9337 100644 Binary files a/ReduxKitRxSwift.xcodeproj/project.xcworkspace/xcuserdata/agentk.xcuserdatad/UserInterfaceState.xcuserstate and b/ReduxKitRxSwift.xcodeproj/project.xcworkspace/xcuserdata/agentk.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ReduxKitRxSwift/ObservableAction.swift b/ReduxKitRxSwift/ObservableAction.swift index ed4604f..7caba71 100644 --- a/ReduxKitRxSwift/ObservableAction.swift +++ b/ReduxKitRxSwift/ObservableAction.swift @@ -9,20 +9,34 @@ import ReduxKit import RxSwift +/** + ObservableActions take a payload type that executes once observed then + dispatches the result if successful. + */ public protocol ObservableAction: Action { + var rawPayload: Observable { get set } - func replacePayload(payload: Observable) -> ObservableAction - // TODO: Refactor mutating functions back to ReduxKit.Action as protocols + + /** + Immutabily modify the payload of an ObservableAction + + - parameter payload: New payload + + - returns: Returns a copy of the current ObservableAction with the + existing payload replaced with the supplied payload + */ + func replacePayload(payload: Observable) -> Self } public extension ObservableAction { + /// Default implementation of the standard action payload and type public var payload: Any? { return rawPayload } } -public protocol ObservableActionError: SimpleStandardAction, ErrorType { -} +public protocol ObservableActionError: SimpleStandardAction, ErrorType {} public extension ObservableActionError { + public var error: Bool { return true } } diff --git a/ReduxKitRxSwift/ObservableMiddleware.swift b/ReduxKitRxSwift/ObservableMiddleware.swift deleted file mode 100644 index 0233cf3..0000000 --- a/ReduxKitRxSwift/ObservableMiddleware.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// ObservableMiddleware.swift -// ReduxKitRxSwift -// -// Created by Karl Bowden on 23/12/2015. -// Copyright © 2015 Redux. All rights reserved. -// - -import ReduxKit - -/// Return an observable action with success and failure states connected to dispatch -public func observableMiddleware(store: Store) -> DispatchTransformer { - return { next in - { action in - guard let originalAction = action as? ObservableAction else { return next(action) } - - let observable = originalAction.rawPayload - .doOn( - onNext: { successAction in - store.dispatch(successAction) - }, - onError: { error in - guard let errorAction = error as? Action else { return } - store.dispatch(errorAction) - }) - - let newAction = originalAction.replacePayload(observable) - - return next(newAction) - } - } -} diff --git a/ReduxKitRxSwift/ReduxKitRxSwift.swift b/ReduxKitRxSwift/ReduxKitRxSwift.swift deleted file mode 100644 index 4ccba61..0000000 --- a/ReduxKitRxSwift/ReduxKitRxSwift.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// ReduxKitRxSwift.swift -// ReduxKitRxSwift -// -// Created by Karl Bowden on 20/12/2015. -// Copyright © 2015 ReduxKit. All rights reserved. -// - -import RxSwift -import ReduxKit - -/** - - Uses `createStateStream` to create a `ReduxKit.Store` using an - `RxSwift.Variable` stream. - - */ -public func createStore( - reducer: ((State?, ReduxKit.Action) -> State), - state: State? = nil) - -> Store { - - return createStreamStore(createStream, reducer: reducer, state: state) -} - -/** - - Accepts a `State` and returns `ReduxKit.StateStream` using an - `RxSwift.Variable` as the stream provider. - - */ -public func createStream(state: State) -> StateStream { - - typealias Subscriber = State -> () - typealias Dispatcher = State -> () - - let variable = Variable(state) - - let dispatch: Dispatcher = { state in - variable.value = state - } - - let subscribe: (Subscriber) -> ReduxDisposable = { subscriber in - return createDisposable(variable.subscribeNext(subscriber)) - } - - let getState: () -> State = { - return variable.value - } - - return StateStream(dispatch: dispatch, subscribe: subscribe, getState: getState) -} - -/** - - Accepts an `RxSwift.Disposable` and returns it wrapped as a `ReduxDisposable`. - - The returned disposable only supports the `disposable.dispose()` function and - does not return disposed state (`disposable.disposed` always returns `false`). - - */ -public func createDisposable(disposable: RxSwift.Disposable) -> ReduxDisposable { - - return SimpleReduxDisposable(disposed: { false }, dispose: disposable.dispose) -} diff --git a/ReduxKitRxSwift/ReduxRxDisposable.swift b/ReduxKitRxSwift/ReduxRxDisposable.swift new file mode 100644 index 0000000..f0beb9e --- /dev/null +++ b/ReduxKitRxSwift/ReduxRxDisposable.swift @@ -0,0 +1,34 @@ +// +// ReduxRxDisposable.swift +// ReduxKitRxSwift +// +// Created by Karl Bowden on 27/12/2015. +// Copyright © 2015 Redux. All rights reserved. +// + +import RxSwift +import ReduxKit + +/** + Simple implementation of ReduxDisposable to wrap RxSwift.Disposable types. + + - note: RxSwift.Disposable does not have a `disposed` parameter that can be + exposed. Hence the disposed parameter always returns false. + */ +public struct ReduxRxDisposable: ReduxDisposable { + + /// Fallback implementation. Always returns `false` + public let disposed: Bool = false + + /// Cancels the observation and disposes of the connection + public let dispose: () -> () + + /** + Create a ReduxDisposable from an RxSwift.Disposable + + - parameter disposable: RxSwift.Disposable + */ + public init(disposable: Disposable) { + dispose = disposable.dispose + } +} diff --git a/ReduxKitRxSwift/Store+init.swift b/ReduxKitRxSwift/Store+init.swift new file mode 100644 index 0000000..09ed639 --- /dev/null +++ b/ReduxKitRxSwift/Store+init.swift @@ -0,0 +1,37 @@ +// +// Store+init.swift +// ReduxKitRxSwift +// +// Created by Karl Bowden on 27/12/2015. +// Copyright © 2015 Redux. All rights reserved. +// + +import RxSwift +import ReduxKit + +extension Store { + + /** + Initialise a ReduxKit Store from an RxSwift PublishSubject and ReplayAction + + - parameter subjectDispatcher: RxSwift.PublishSubject + - parameter stateSubject: ReplaySubject + */ + public init(subjectDispatcher: PublishSubject, stateSubject: ReplaySubject) { + + dispatch = { action in + subjectDispatcher.onNext(action) + return action + } + + subscribe = { subscriber in + return ReduxRxDisposable(disposable: stateSubject.subscribeNext(subscriber)) + } + + getState = { + var state: State! + stateSubject.take(1).subscribeNext { state = $0 }.dispose() + return state + } + } +} diff --git a/ReduxKitRxSwift/createStore.swift b/ReduxKitRxSwift/createStore.swift new file mode 100644 index 0000000..2d71c76 --- /dev/null +++ b/ReduxKitRxSwift/createStore.swift @@ -0,0 +1,36 @@ +// +// createStore.swift +// ReduxKitRxSwift +// +// Created by Karl Bowden on 27/12/2015. +// Copyright © 2015 Redux. All rights reserved. +// + +import RxSwift +import ReduxKit + +/** + Create a ReduxKit Store backed by an RxSwift PublishSubject and ReplaySubject. + + - parameter reducer: ReduxKit reducer + - parameter state: Optional initial state + + - returns: Store + */ +public func createStore( + reducer: ((State?, ReduxKit.Action) -> State), + state: State? = nil) + -> Store { + + typealias StoreCreator = ( + reducer: ((State?, ReduxKit.Action) -> State), + state: State?) + -> Store + + let backing: ( + publisher: PublishSubject, + subject: ReplaySubject, + createStore: StoreCreator) = setupStoreBacking() + + return backing.createStore(reducer: reducer, state: state) +} diff --git a/ReduxKitRxSwift/observableMiddleware.swift b/ReduxKitRxSwift/observableMiddleware.swift new file mode 100644 index 0000000..9879b8c --- /dev/null +++ b/ReduxKitRxSwift/observableMiddleware.swift @@ -0,0 +1,39 @@ +// +// observableMiddleware.swift +// ReduxKitRxSwift +// +// Created by Karl Bowden on 23/12/2015. +// Copyright © 2015 Redux. All rights reserved. +// + +import ReduxKit + +/** + ReduxKit middleware that dispatches the result of an ObservableAction payload. + + The success and failure states of the ObservableAction are connected to dispatch. + + - parameter store: Store supplied by the StoreEnhancer + + - returns: Dispatch transformer middleware +*/ +public func observableMiddleware(store: Store) -> DispatchTransformer { + return { next in { action in + guard let originalAction = action as? ObservableAction else { return next(action) } + + let observable = originalAction.rawPayload + .doOn( + onNext: { successAction in + store.dispatch(successAction) + }, + onError: { error in + guard let errorAction = error as? Action else { return } + store.dispatch(errorAction) + }) + + let newAction = originalAction.replacePayload(observable) + + return next(newAction) + } + } +} diff --git a/ReduxKitRxSwift/setupStoreBacking.swift b/ReduxKitRxSwift/setupStoreBacking.swift new file mode 100644 index 0000000..70dc1ee --- /dev/null +++ b/ReduxKitRxSwift/setupStoreBacking.swift @@ -0,0 +1,52 @@ +// +// setupStoreBacking.swift +// ReduxKitRxSwift +// +// Created by Karl Bowden on 27/12/2015. +// Copyright © 2015 Redux. All rights reserved. +// + +import RxSwift +import ReduxKit + +/** + Sets up a createStore function to create a ReduxKit.Store using an + RxSwift PublishSubject and ReplaySubject. + + __publisher__: `RxSwift.PublishSubject` + - Reduces and action and publishes it to the stateSubject + + __subject__: `RxSwift.ReplaySubject` + - A subscribable hot signal of resulting states + + __createStore__: `(Reducer, State?) -> Store` + - the createStore function to be used when creating a ReduxKit Store + + - returns: ( + RxSwift.PublishSubject, + RxSwift.ReplaySubject, + StoreCreator(reducer, state) -> Store) +*/ +public func setupStoreBacking() -> ( + publisher: PublishSubject, + subject: ReplaySubject, + createStore: (reducer: ((State?, ReduxKit.Action) -> State), state: State?) -> Store) { + + typealias Subscriber = State -> () + + let subjectDispatcher: PublishSubject = PublishSubject() + let stateSubject = ReplaySubject.create(bufferSize: 1) + + return (subjectDispatcher, stateSubject, { reducer, state in + + let initalState = state ?? reducer(state, DefaultAction()) + + // have the stateSubject subscribe to the dispatcher + subjectDispatcher + .scan(initalState, accumulator: reducer) + .startWith(initalState) + .subscribe(stateSubject) + + return Store(subjectDispatcher: subjectDispatcher, stateSubject: stateSubject) + }) +} diff --git a/ReduxKitRxSwiftTests/Mocks.swift b/ReduxKitRxSwiftTests/Mocks.swift index d0f960e..d895c01 100644 --- a/ReduxKitRxSwiftTests/Mocks.swift +++ b/ReduxKitRxSwiftTests/Mocks.swift @@ -12,6 +12,9 @@ import ReduxKitRxSwift // MARK: - State +/** + Mock State + */ struct State { let count: Int let sendStatus: String! @@ -19,6 +22,9 @@ struct State { // MARK: - Actions +/** + Increment by the payload value + */ struct IncrementAction: StandardAction { let meta: Any? let error: Bool @@ -31,7 +37,9 @@ struct IncrementAction: StandardAction { } } - +/** + Mock ObservableAction + */ struct TestObservableAction: ObservableAction { typealias PayloadType = Int @@ -51,17 +59,16 @@ struct TestObservableAction: ObservableAction { self.rawPayload = rawPayload } - func replacePayload(payload: Observable) -> ObservableAction { + func replacePayload(payload: Observable) -> TestObservableAction { return self.dynamicType.init(meta: self.meta, error: self.error, rawPayload: payload) } static func Payload(success: Bool = true) -> Observable { return create { observer in - if (success) { + if success { observer.on(.Next(TestObservableSuccessAction())) - } - else { + } else { observer.on(.Error(TestObservableErrorAction())) } @@ -72,18 +79,27 @@ struct TestObservableAction: ObservableAction { } } -struct TestObservableSuccessAction: SimpleStandardAction{ +/** + Succes signal of the TestObservableAction + */ +struct TestObservableSuccessAction: SimpleStandardAction { let meta: Any? = nil let error: Bool = false let rawPayload: String = "Success" } +/** + Error signal of the TestObservableAction + */ struct TestObservableErrorAction: ObservableActionError { let meta: Any? = nil let rawPayload: String = "Error" } -enum TestObservableError: ErrorType{ +/** + Error value of the TestObservableAction + */ +enum TestObservableError: ErrorType { case ObservableFailed } @@ -97,7 +113,14 @@ func reducer(state: State? = nil, action: Action) -> State { ) } -/// Add IncrementAction payload values to the previous value +/** + Add IncrementAction payload values to the previous value + + - parameter previousState: Int? (defaults to 0) + - parameter action: Action + + - returns: Int + */ func countReducer(previousState: Int? = nil, action: Action) -> Int { let state = previousState ?? 0 @@ -109,11 +132,18 @@ func countReducer(previousState: Int? = nil, action: Action) -> Int { } } +/** + Set the state to an ObservableAction payload if dispatched -func observableReducer(previousState: String?, action: Action) -> String{ + - parameter previousState: String? (defaults to "") + - parameter action: Action + + - returns: String + */ +func observableReducer(previousState: String?, action: Action) -> String { var state = previousState ?? "" - switch action{ + switch action { case let action as TestObservableSuccessAction: state = action.rawPayload return state @@ -128,12 +158,17 @@ func observableReducer(previousState: String?, action: Action) -> String{ // MARK: - Middleware -/// Logs dispatched actions to the console for debugging +/** + ReduxKit middleware that logs dispatched actions to the console for debugging + + - parameter store: Store + + - returns: DispatchTransformer +*/ func loggerMiddleware(store: Store) -> DispatchTransformer { - return { next in - { action in - print(action.type) - return next(action) + return { next in { action in + print(action.type) + return next(action) } } }