From 599cadf95296cdffa53f4f350dfaceae9edc6224 Mon Sep 17 00:00:00 2001 From: Karl Bowden Date: Sun, 27 Dec 2015 16:51:26 +1100 Subject: [PATCH] Refactor createStore to remove StateStream usage Signed-off-by: Karl Bowden --- .swiftlint.yml | 6 + ReduxKitRxSwift.xcodeproj/project.pbxproj | 128 +++++++++++++++--- .../UserInterfaceState.xcuserstate | Bin 19116 -> 26382 bytes ReduxKitRxSwift/ObservableAction.swift | 22 ++- ReduxKitRxSwift/ObservableMiddleware.swift | 32 ----- ReduxKitRxSwift/ReduxKitRxSwift.swift | 65 --------- ReduxKitRxSwift/ReduxRxDisposable.swift | 34 +++++ ReduxKitRxSwift/Store+init.swift | 37 +++++ ReduxKitRxSwift/createStore.swift | 36 +++++ ReduxKitRxSwift/observableMiddleware.swift | 39 ++++++ ReduxKitRxSwift/setupStoreBacking.swift | 52 +++++++ ReduxKitRxSwiftTests/Mocks.swift | 65 +++++++-- 12 files changed, 380 insertions(+), 136 deletions(-) create mode 100644 .swiftlint.yml delete mode 100644 ReduxKitRxSwift/ObservableMiddleware.swift delete mode 100644 ReduxKitRxSwift/ReduxKitRxSwift.swift create mode 100644 ReduxKitRxSwift/ReduxRxDisposable.swift create mode 100644 ReduxKitRxSwift/Store+init.swift create mode 100644 ReduxKitRxSwift/createStore.swift create mode 100644 ReduxKitRxSwift/observableMiddleware.swift create mode 100644 ReduxKitRxSwift/setupStoreBacking.swift 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 8f8e7feb0c8ff72c9a775f71880779020052fab6..a1d9337ee3dc7866019298ce0e634611728c1ce4 100644 GIT binary patch literal 26382 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 Wn&slT3WVaPB);#QrSGHWuKx$Ws@byu delta 10661 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

QF&68)!b!z>19j#fo?lfnKLx5H+r>o8HHZ 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 Y3hbgG`R@e&KVCF>lhl6=yfw-GfBN!>0{{R3 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) } } }