├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── Resources ├── RxReduceDemo1.png ├── RxReduceDemo2.png ├── RxReduceScheme.gif ├── RxReduce_Logo.ai └── RxReduce_Logo.png ├── RxReduce.podspec ├── RxReduce.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── RxReduce.xcscheme ├── RxReduce ├── Action.swift ├── Info.plist ├── Lens.swift ├── Mutator.swift ├── RxReduce.h └── Store.swift ├── RxReduceDemo ├── Cartfile ├── Cartfile.resolved ├── RxReduceDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── RxReduceDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ ├── Icon-Small-50x50@1x.png │ │ ├── Icon-Small-50x50@2x.png │ │ └── ItunesArtwork@2x.png │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── DependencyContainer.swift │ ├── Extensions │ └── UIViewController+ViewModelBased.swift │ ├── Features │ ├── MovieDetail │ │ ├── MovieDetailViewController.storyboard │ │ ├── MovieDetailViewController.swift │ │ └── MovieDetailViewModel.swift │ └── MovieList │ │ ├── MovieListViewCell.swift │ │ ├── MovieListViewCell.xib │ │ ├── MovieListViewController.storyboard │ │ ├── MovieListViewController.swift │ │ └── MovieListViewModel.swift │ ├── Info.plist │ ├── Models │ ├── DiscoverMovieResponse.swift │ ├── DiscoverTVResponse.swift │ └── Media.swift │ ├── Protocols │ ├── HasProtocols.swift │ ├── Injectable.swift │ ├── ViewModel.swift │ └── ViewModelBased.swift │ ├── Services │ ├── NetworkService.swift │ └── Routes.swift │ └── State │ ├── AppActions.swift │ ├── AppLenses.swift │ ├── AppMutators.swift │ ├── AppReducers.swift │ └── AppState.swift ├── RxReduceTests ├── ActionTests.swift ├── Info.plist ├── State │ ├── Actions.swift │ ├── Reducers.swift │ └── States.swift └── StoreTests.swift └── docs ├── Classes.html ├── Classes ├── DefaultStore.html └── Store.html ├── Extensions.html ├── Extensions ├── Array.html └── Observable.html ├── Protocols.html ├── Protocols ├── Action.html ├── Store.html └── StoreType.html ├── Typealiases.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── RxReduce.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── DefaultStore.html │ │ │ └── Store.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── Array.html │ │ │ └── Observable.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── Action.html │ │ │ ├── Store.html │ │ │ └── StoreType.html │ │ ├── Typealiases.html │ │ ├── badge.svg │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ ├── search.json │ │ └── undocumented.json │ │ └── docSet.dsidx └── RxReduce.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js └── search.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | RxReduce.framework.zip 9 | undocumented.json 10 | 11 | ## macos 12 | .DS_Store 13 | 14 | ## Various settings 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata/ 24 | 25 | ## Other 26 | *.moved-aside 27 | *.xccheckout 28 | *.xcscmblueprint 29 | *.framework.zip 30 | 31 | ## Obj-C/Swift specific 32 | *.hmap 33 | *.ipa 34 | *.dSYM.zip 35 | *.dSYM 36 | 37 | ## Playgrounds 38 | timeline.xctimeline 39 | playground.xcworkspace 40 | 41 | # Swift Package Manager 42 | # 43 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 44 | # Packages/ 45 | # Package.pins 46 | .build/ 47 | 48 | # CocoaPods 49 | # 50 | # We recommend against adding the Pods directory to your .gitignore. However 51 | # you should judge for yourself, the pros and cons are mentioned at: 52 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 53 | # 54 | # Pods/ 55 | 56 | # Carthage 57 | # 58 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 59 | Carthage 60 | Carthage/Checkouts 61 | Carthage/Build 62 | 63 | # fastlane 64 | # 65 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 66 | # screenshots whenever they are needed. 67 | # For more information about the recommended setup visit: 68 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 69 | 70 | fastlane/report.xml 71 | fastlane/Preview.html 72 | fastlane/screenshots 73 | fastlane/test_output 74 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0.1 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # rule identifiers to exclude from running 2 | - todo 3 | 4 | opt_in_rules: # some rules are only opt-in 5 | - empty_count 6 | 7 | included: 8 | - RxReduce 9 | 10 | excluded: # paths to ignore during linting. Takes precedence over `included`. 11 | - Carthage 12 | - Pods 13 | 14 | force_cast: warning # implicitly 15 | force_try: 16 | severity: warning # explicitly 17 | 18 | type_body_length: 19 | - 300 # warning 20 | - 400 # error 21 | 22 | type_name: 23 | min_length: 4 # only warning 24 | max_length: # warning and error 25 | warning: 40 26 | error: 50 27 | excluded: iPhone # excluded via string 28 | 29 | identifier_name: 30 | min_length: # only min_length 31 | error: 3 # only error 32 | excluded: # excluded via string array 33 | - on 34 | - id 35 | - URL 36 | - GlobalAPIKey 37 | 38 | reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji) 39 | 40 | function_body_length: 41 | - 75 42 | - 100 43 | 44 | file_length: 45 | warning: 600 46 | error: 1200 47 | 48 | line_length: 49 | warning: 200 50 | error: 250 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode10.2 3 | 4 | cache: 5 | directories: 6 | - Carthage 7 | 8 | before_install: 9 | - brew update 10 | - brew outdated carthage || brew upgrade carthage 11 | - brew outdated swiftlint || brew upgrade swiftlint 12 | - carthage bootstrap --verbose --platform iOS --cache-builds 13 | script: 14 | - xcodebuild clean test -project RxReduce.xcodeproj -scheme RxReduce -destination "platform=iOS Simulator,name=iPhone X,OS=12.2" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet | xcpretty -c 15 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" == 4.5.0 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" "4.5.0" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) RxSwiftCommunity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | ## Checklist 5 | 6 | - [ ] this PR is based on develop or a 'develop related' branch 7 | - [ ] the commits inside this PR have explicit commit messages 8 | - [ ] the Jazzy documentation has been generated (if needed -> Jazzy RxReduce) 9 | -------------------------------------------------------------------------------- /Resources/RxReduceDemo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/Resources/RxReduceDemo1.png -------------------------------------------------------------------------------- /Resources/RxReduceDemo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/Resources/RxReduceDemo2.png -------------------------------------------------------------------------------- /Resources/RxReduceScheme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/Resources/RxReduceScheme.gif -------------------------------------------------------------------------------- /Resources/RxReduce_Logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/Resources/RxReduce_Logo.ai -------------------------------------------------------------------------------- /Resources/RxReduce_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/Resources/RxReduce_Logo.png -------------------------------------------------------------------------------- /RxReduce.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "RxReduce" 4 | s.version = "0.10" 5 | s.summary = "RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way." 6 | 7 | s.description = <<-DESC 8 | RxReduce provides: 9 | 10 | * State and Action abtractions 11 | * A default, generic and reactive Store 12 | * Type safe Reducers 13 | * An elegant way to deal with asynchronicity outside Reducers 14 | DESC 15 | 16 | s.homepage = "https://github.com/RxSwiftCommunity/RxReduce" 17 | s.screenshots = "https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/develop/Resources/RxReduce_Logo.png" 18 | s.license = { :type => "MIT", :file => "LICENSE" } 19 | s.authors = { "Thibault Wittemberg" => "thibault.wittemberg@gmail.com", "RxSwiftCommunity" => "https://github.com/RxSwiftCommunity" } 20 | s.social_media_url = "http://twitter.com/thwittem" 21 | s.platform = :ios 22 | s.ios.deployment_target = "9.0" 23 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxReduce.git", :tag => s.version.to_s } 24 | s.source_files = "RxReduce/**/*.swift" 25 | s.requires_arc = true 26 | s.dependency 'RxSwift', '>= 4.5.0' 27 | s.dependency 'RxCocoa', '>= 4.5.0' 28 | 29 | end 30 | -------------------------------------------------------------------------------- /RxReduce.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxReduce.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxReduce.xcodeproj/xcshareddata/xcschemes/RxReduce.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RxReduce/Action.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Action.swift 3 | // RxReduce 4 | // 5 | // Created by Thibault Wittemberg on 18-04-14. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | 11 | /// Conform to an Action to mutate the State synchronously or asynchronously 12 | /// Arrays of Actions and Observable of Action are considered to be Actions as well 13 | public protocol Action { 14 | 15 | /// Convert the action into an Observable so it is compliant with asynchronous processing 16 | /// 17 | /// - Returns: the asynchronous action 18 | func toAsync () -> Observable 19 | } 20 | 21 | // MARK: - Default implementation of toAsync (just transform Self in an Observable) 22 | extension Action { 23 | public func toAsync () -> Observable { 24 | return Observable.just(self) 25 | } 26 | } 27 | 28 | // MARK: - Default implementation of toAsync: Array is also an Action 29 | extension Array: Action where Element == Action { 30 | public func toAsync () -> Observable { 31 | return Observable.concat(self.map { $0.toAsync() }) 32 | } 33 | } 34 | 35 | // MARK: - Default implementation of toAsync: Observable is also an Action 36 | extension Observable: Action where Element == Action { 37 | public func toAsync () -> Observable { 38 | return self 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /RxReduce/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /RxReduce/Lens.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lens.swift 3 | // RxReduce 4 | // 5 | // Created by Thibault Wittemberg on 2018-08-20. 6 | // Copyright © 2018 WarpFactor. All rights reserved. 7 | // 8 | 9 | /// A Lens is a generic way to access and mutate a value type 10 | public struct Lens { 11 | 12 | /// retrieves a Substate from a State 13 | let get: (State) -> SubState 14 | 15 | /// Generates a new State based on an original State and on an original SubState 16 | let set: (State, SubState) -> State 17 | 18 | /// Lens initializer 19 | /// 20 | /// - Parameters: 21 | /// - get: the closure that allows to get a subState given a state 22 | /// - set: the closure that allows to mutate a state given a subState 23 | public init (get: @escaping (State) -> SubState, set: @escaping (State, SubState) -> State) { 24 | self.get = get 25 | self.set = set 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /RxReduce/Mutator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mutator.swift 3 | // RxReduce 4 | // 5 | // Created by Thibault Wittemberg on 2018-08-20. 6 | // Copyright © 2018 WarpFactor. All rights reserved. 7 | // 8 | 9 | /// A Mutator holds all the required tools to mutate a State's Substate and 10 | // generate a new State 11 | public struct Mutator { 12 | 13 | /// The functional Lens used to focus on a subState of a State (both for accessing and mutating) 14 | let lens: Lens 15 | 16 | /// The reducer function that allows to mutate a State according to an Action 17 | let reducer: (State, Action) -> SubState 18 | 19 | public init(lens: Lens, reducer: @escaping (State, Action) -> SubState) { 20 | self.lens = lens 21 | self.reducer = reducer 22 | } 23 | 24 | /// Mutates a State according to an Action. 25 | /// It uses the defined lens and reducer to 26 | /// generate this mutation 27 | /// 28 | /// - Parameters: 29 | /// - state: the original State to mutate 30 | /// - action: the action used to decide the mutation 31 | /// - Returns: the mutated State 32 | func apply(state: State, action: Action) -> State { 33 | return self.lens.set(state, self.reducer(state, action)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /RxReduce/RxReduce.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxReduce.h 3 | // RxReduce 4 | // 5 | // Created by Thibault Wittemberg on 18-04-22. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RxReduce. 12 | FOUNDATION_EXPORT double RxReduceVersionNumber; 13 | 14 | //! Project version string for RxReduce. 15 | FOUNDATION_EXPORT const unsigned char RxReduceVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /RxReduce/Store.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Store.swift 3 | // RxReduce 4 | // 5 | // Created by Thibault Wittemberg on 18-04-15. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | import RxCocoa 12 | 13 | /// A Store holds the state, mutate the state through Actions and Reducers. 14 | /// Internally, Reducers come from registered Mutator defines a Reducer. 15 | public final class Store { 16 | 17 | /// Reducer correspond to the apply func of a Mutator 18 | private typealias Reducer = (State, Action) -> State 19 | 20 | private let stateSubject: BehaviorRelay 21 | private var neededReducersPerSubState = [String: Int]() 22 | private var reducers = ContiguousArray() 23 | private let serialQueue = DispatchQueue(label: "com.rxswiftcommunity.rxreduce.serialqueue") 24 | 25 | /// The global State is exposed via an Observable, just like some kind of "middleware". 26 | /// This global State will trigger a new value after a dispatch(action) has triggered a "onNext" event. 27 | public lazy var state: Observable = { 28 | return self.stateSubject.asObservable() 29 | }() 30 | 31 | /// Inits a Store with an initial State 32 | /// 33 | /// - Parameter state: the initial State 34 | public init(withState state: State) { 35 | 36 | // Sets the initial State value 37 | self.stateSubject = BehaviorRelay(value: state) 38 | 39 | // We analyze the State children (aka SubState) 40 | // to be able to check that each of them will be handled 41 | // by a reducer function (it allows to be sure that if we 42 | // add a new SubState to the State, there is a reducer in charge 43 | // of its mutation) 44 | let stateMirror = Mirror(reflecting: state) 45 | stateMirror 46 | .children 47 | .map { Mirror(reflecting: $0.value) } 48 | .map { "\(type(of: $0.subjectType))" } 49 | .forEach { [unowned self] (subStateType) in 50 | if let neededReducerForSubState = self.neededReducersPerSubState[subStateType] { 51 | self.neededReducersPerSubState[subStateType] = neededReducerForSubState + 1 52 | } else { 53 | self.neededReducersPerSubState[subStateType] = 1 54 | } 55 | } 56 | 57 | self.reducers.reserveCapacity(self.neededReducersPerSubState.count) 58 | } 59 | 60 | /// Registers a Mutator dedicated to a State/SubState mutation. 61 | /// The Mutator internally references a reducer that will be later applied to the 62 | /// current State according to an Action. This will produce a new State. 63 | /// Store does not keep a reference on the Mutator, only on the Reducer it defines. 64 | /// Each Reducer is applied in sequence when a new Action is dispatched. 65 | /// There MUST be a Reducer for every State's SubState 66 | /// There MUST be only one Reducer per SubState 67 | /// 68 | /// - Parameter mutator: The Mutator dedicated to a State/Substate mutation 69 | public func register (mutator: Mutator) { 70 | let subStateType = "\(type(of: SubState.self))" 71 | 72 | guard let neededReducerForSubState = self.neededReducersPerSubState[subStateType] else { 73 | fatalError("Mutator \(mutator) is not relevant for this State") 74 | } 75 | 76 | guard neededReducerForSubState > 0 else { 77 | fatalError("No more Mutator cannot be registered for SubState \(subStateType)") 78 | } 79 | 80 | self.reducers.append(mutator.apply) 81 | self.neededReducersPerSubState[subStateType] = neededReducerForSubState - 1 82 | } 83 | 84 | /// Dispatches an Action to the registered Reducers. 85 | /// The Action will first go through the Middlewares and 86 | /// then through the Reducers producing a mutated State. 87 | /// 88 | /// - Parameter action: The Action that will be handled by the reducers 89 | /// - Returns: The mutated State 90 | public func dispatch(action: Action) -> Observable { 91 | 92 | let subStatesNotReduced = !self.neededReducersPerSubState.filter { $0.value > 0 }.isEmpty 93 | 94 | guard !subStatesNotReduced else { 95 | fatalError("All substate must be reduced") 96 | } 97 | 98 | // every received action is converted to an async action 99 | return action 100 | .toAsync() 101 | .map { [unowned self] (action) -> State in 102 | 103 | self.serialQueue.sync { 104 | let newState = self.reducers.reduce(self.stateSubject.value, { (currentState, reducer) -> State in 105 | return reducer(currentState, action) 106 | }) 107 | 108 | self.stateSubject.accept(newState) 109 | } 110 | 111 | return self.stateSubject.value 112 | } 113 | .distinctUntilChanged() 114 | } 115 | 116 | /// Dispatches an Action to the registered Mutators but instead of 117 | /// returning the mutated State, it allows to focus on a sub set of the State. 118 | /// 119 | /// - Parameters: 120 | /// - action: The Action that will be handled by the reducers 121 | /// - focusingOn: The closure that defines the sub set of the State that we want to observe 122 | /// - Returns: The mutated sub set of the State 123 | public func dispatch(action: Action, focusingOn: @escaping (State) -> StateContent) -> Observable { 124 | return self.dispatch(action: action).map { focusingOn($0) }.distinctUntilChanged() 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /RxReduceDemo/Cartfile: -------------------------------------------------------------------------------- 1 | github "twittemb/RxReduce" "develop" 2 | github "ReactiveX/RxSwift" == 4.5.0 3 | github "AliSoftware/Reusable" 4 | github "Alamofire/Alamofire" 5 | github "Alamofire/AlamofireImage" 6 | -------------------------------------------------------------------------------- /RxReduceDemo/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.8.2" 2 | github "Alamofire/AlamofireImage" "3.5.2" 3 | github "AliSoftware/Reusable" "4.0.5" 4 | github "ReactiveX/RxSwift" "4.5.0" 5 | github "twittemb/RxReduce" "8d45d0579936d0052f705cf1d29a552e9c0ad5fa" 6 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxReduce 11 | import RxSwift 12 | import RxCocoa 13 | 14 | @UIApplicationMain 15 | class AppDelegate: UIResponder, UIApplicationDelegate { 16 | 17 | var window: UIWindow? 18 | 19 | let networkService: NetworkService = NetworkService(withBaseUrl: URL(string: "https://api.themoviedb.org/3/")!, andApiKey: "3afafd21270fe0414eb760a41f2620eb") 20 | private lazy var store: Store = { 21 | let store = Store(withState: AppState(movieListState: .empty, movieDetailState: .empty)) 22 | 23 | store.register(mutator: AppMutators.movieListMutator) 24 | store.register(mutator: AppMutators.movieDetailMutator) 25 | 26 | return store 27 | }() 28 | 29 | lazy var dependencyContainer: DependencyContainer = { 30 | return DependencyContainer(withStore: self.store, withNetworkService: self.networkService) 31 | }() 32 | 33 | private let disposeBag = DisposeBag() 34 | 35 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 36 | 37 | guard let window = self.window else { return false } 38 | 39 | // you can observe the State ... the same way you would do with a Middleware 40 | self.store.state.subscribe(onNext: { appState in 41 | print (appState) 42 | }).disposed(by: self.disposeBag) 43 | 44 | let movieListViewModel = MovieListViewModel(with: self.dependencyContainer) 45 | let movieListViewController = MovieListViewController.instantiate(with: movieListViewModel) 46 | window.rootViewController = movieListViewController 47 | window.makeKeyAndVisible() 48 | 49 | return true 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-57x57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-57x57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-App-60x60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-App-60x60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-20x20@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-20x20@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-29x29@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-29x29@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-40x40@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-40x40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50x50@1x.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50x50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-App-72x72@1x.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-App-72x72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-App-76x76@1x.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-App-76x76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-App-83.5x83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ItunesArtwork@2x.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/RxReduceDemo/RxReduceDemo/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/DependencyContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependencyContainer.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | final class DependencyContainer: HasStore, HasNetworkService { 13 | let store: Store 14 | let networkService: NetworkService 15 | 16 | init(withStore store: Store, withNetworkService networkService: NetworkService) { 17 | self.store = store 18 | self.networkService = networkService 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Extensions/UIViewController+ViewModelBased.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+RxReduce.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Reusable 11 | 12 | extension ViewModelBased where Self: StoryboardBased & UIViewController { 13 | static func instantiate(with viewModel: ViewModelType) -> Self { 14 | var viewController = Self.instantiate() 15 | viewController.viewModel = viewModel 16 | return viewController 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieDetail/MovieDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieDetailViewController.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-07. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Reusable 11 | import RxSwift 12 | import RxCocoa 13 | import Alamofire 14 | import AlamofireImage 15 | 16 | class MovieDetailViewController: UIViewController, StoryboardBased, ViewModelBased { 17 | 18 | @IBOutlet weak var posterImageView: UIImageView! 19 | @IBOutlet weak var nameLabel: UILabel! 20 | @IBOutlet weak var overviewTextView: UITextView! 21 | @IBOutlet weak var voteAverageLabel: UILabel! 22 | @IBOutlet weak var popularityLabel: UILabel! 23 | @IBOutlet weak var originalNameLabel: UILabel! 24 | @IBOutlet weak var releaseDateLabel: UILabel! 25 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 26 | 27 | var viewModel: MovieDetailViewModel! 28 | 29 | private let disposeBag = DisposeBag() 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | // ask the view model to load the movie detail and then listen to the state mutation 35 | self.viewModel.loadMovieDetail().drive(onNext: { [weak self] (movieDetailState) in 36 | self?.render(movieDetailState: movieDetailState) 37 | }).disposed(by: self.disposeBag) 38 | } 39 | 40 | private func render (movieDetailState: MovieDetailState) { 41 | switch movieDetailState { 42 | case .empty: 43 | self.posterImageView.image = nil 44 | self.nameLabel.text = "-" 45 | self.overviewTextView.text = "-" 46 | self.voteAverageLabel.text = "-" 47 | self.popularityLabel.text = "-" 48 | self.originalNameLabel.text = "-" 49 | self.releaseDateLabel.text = "-" 50 | self.activityIndicator.stopAnimating() 51 | case .loaded(let movie): 52 | self.activityIndicator.startAnimating() 53 | var posterPath = "https://image.tmdb.org/t/p/w780"+movie.posterPath 54 | if let backdropPath = movie.backdropPath { 55 | posterPath = "https://image.tmdb.org/t/p/w780"+backdropPath 56 | } 57 | Alamofire.request(posterPath).responseImage { [weak self] (response) in 58 | guard response.request?.url?.absoluteString == posterPath else { return } 59 | guard let data = response.data else { return } 60 | 61 | self?.posterImageView.image = UIImage(data: data) 62 | self?.activityIndicator.stopAnimating() 63 | } 64 | self.nameLabel.text = movie.name 65 | self.overviewTextView.text = movie.overview 66 | self.voteAverageLabel.text = "\(movie.voteAverage)" 67 | self.popularityLabel.text = "\(movie.popularity)" 68 | self.originalNameLabel.text = movie.originalName 69 | self.releaseDateLabel.text = movie.releaseDate 70 | } 71 | } 72 | 73 | @IBAction func dismiss(_ sender: UIButton) { 74 | self.dismiss(animated: true) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieDetail/MovieDetailViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieDetailViewModel.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-07. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | import RxSwift 12 | import RxCocoa 13 | 14 | final class MovieDetailViewModel: ViewModel, Injectable { 15 | typealias InjectionContainer = HasStore 16 | var injectionContainer: InjectionContainer 17 | 18 | let movieId: Int 19 | 20 | init(with injectionContainer: InjectionContainer, withMovieId movieId: Int) { 21 | self.injectionContainer = injectionContainer 22 | self.movieId = movieId 23 | } 24 | 25 | func loadMovieDetail () -> Driver { 26 | 27 | // dispatch the synchronous Load Movie Detail action 28 | return self.injectionContainer 29 | .store 30 | .dispatch(action: MovieAction.loadMovie(movieId: self.movieId)) { $0.movieDetailState } 31 | .asDriver(onErrorJustReturn: MovieDetailState.empty) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieList/MovieListViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiscoveryListViewCell.swift 3 | // WarpFactorIOS 4 | // 5 | // Created by Thibault Wittemberg on 18-04-09. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Reusable 11 | 12 | final class MovieListViewCell: UITableViewCell, NibReusable { 13 | 14 | @IBOutlet weak var poster: UIImageView! 15 | @IBOutlet weak var title: UILabel! 16 | @IBOutlet weak var overview: UILabel! 17 | 18 | override func prepareForReuse() { 19 | self.imageView?.image = nil 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieList/MovieListViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieList/MovieListViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieList/MovieListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieListViewController.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import Reusable 13 | import RxReduce 14 | import Alamofire 15 | import AlamofireImage 16 | 17 | final class MovieListViewController: UITableViewController, StoryboardBased, ViewModelBased { 18 | 19 | var viewModel: MovieListViewModel! 20 | 21 | private let disposeBag = DisposeBag() 22 | 23 | private var movies = [DiscoverMovieModel]() 24 | 25 | private lazy var activityIndicator: UIActivityIndicatorView = { 26 | let indicator = UIActivityIndicatorView(style: .gray) 27 | indicator.hidesWhenStopped = true 28 | indicator.color = .black 29 | indicator.frame = self.view.frame 30 | 31 | self.view.addSubview(indicator) 32 | return indicator 33 | }() 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | self.clearsSelectionOnViewWillAppear = false 39 | self.tableView.register(cellType: MovieListViewCell.self) 40 | 41 | // ask the view model to fetch the movie list and then listen to the state mutation 42 | self.viewModel.fetchMovieList().drive(onNext: { [weak self] (movieListState) in 43 | self?.render(movieListState: movieListState) 44 | }).disposed(by: self.disposeBag) 45 | } 46 | 47 | private func render (movieListState: MovieListState) { 48 | switch movieListState { 49 | case .empty: 50 | self.movies.removeAll() 51 | self.activityIndicator.stopAnimating() 52 | case .loading: 53 | self.movies.removeAll() 54 | self.activityIndicator.isHidden = false 55 | self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none 56 | self.activityIndicator.startAnimating() 57 | case .loaded(let movies): 58 | self.movies = movies 59 | self.tableView.isHidden = false 60 | self.activityIndicator.stopAnimating() 61 | self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.singleLine 62 | self.tableView.reloadData() 63 | } 64 | } 65 | 66 | // MARK: - Table view data source 67 | 68 | override func numberOfSections(in tableView: UITableView) -> Int { 69 | return 1 70 | } 71 | 72 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 73 | return self.movies.count 74 | } 75 | 76 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 77 | let cell: MovieListViewCell = tableView.dequeueReusableCell(for: indexPath) 78 | let movie = self.movies[indexPath.row] 79 | cell.title.text = movie.name 80 | cell.overview.text = movie.overview 81 | let posterPath = "https://image.tmdb.org/t/p/w154"+movie.posterPath 82 | Alamofire.request(posterPath).responseImage { (response) in 83 | guard response.request?.url?.absoluteString == posterPath else { return } 84 | guard let data = response.data else { return } 85 | 86 | cell.poster.image = UIImage(data: data) 87 | } 88 | 89 | return cell 90 | } 91 | 92 | // MARK: - Table view delegate 93 | 94 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 95 | return 150 96 | } 97 | 98 | override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { 99 | return 150 100 | } 101 | 102 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 103 | let movie = self.movies[indexPath.row] 104 | 105 | let movieDetailViewModel = MovieDetailViewModel(with: self.viewModel.injectionContainer, withMovieId: movie.id) 106 | let movieDetailViewController = MovieDetailViewController.instantiate(with: movieDetailViewModel) 107 | self.present(movieDetailViewController, animated: true) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Features/MovieList/MovieListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieListViewModel.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-07. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | import RxSwift 12 | import RxCocoa 13 | 14 | final class MovieListViewModel: ViewModel, Injectable { 15 | 16 | typealias InjectionContainer = HasStore & HasNetworkService 17 | var injectionContainer: InjectionContainer 18 | 19 | init(with injectionContainer: InjectionContainer) { 20 | self.injectionContainer = injectionContainer 21 | } 22 | 23 | func fetchMovieList () -> Driver { 24 | // build an asynchronous action to fetch the movies 25 | let loadMovieAction: Observable = self.injectionContainer.networkService 26 | .fetch(withRoute: Routes.discoverMovie) 27 | .asObservable() 28 | .map { $0.movies.filter {$0.backdropPath != nil } } 29 | .map { MovieAction.loadMovies(movies: $0) } 30 | .startWith(MovieAction.startLoadingMovies) 31 | 32 | // dispatch the asynchronous fetch action 33 | return self.injectionContainer 34 | .store 35 | .dispatch(action: loadMovieAction) { $0.movieListState } 36 | .asDriver(onErrorJustReturn: .empty) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Models/DiscoverMovieResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiscoverMovieResponse.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-09. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct DiscoverMovieResponse: Codable { 12 | let page: Int 13 | let totalResults: Int 14 | let totalPages: Int 15 | let movies: [DiscoverMovieModel] 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case totalResults = "total_results" 19 | case totalPages = "total_pages" 20 | case page 21 | case movies = "results" 22 | } 23 | } 24 | 25 | struct DiscoverMovieModel: Codable, Media, Equatable { 26 | let id: Int 27 | let video: Bool 28 | let name: String 29 | let overview: String 30 | let popularity: Float 31 | let adult: Bool 32 | let genre: [Int] 33 | let voteCount: Int 34 | let voteAverage: Float 35 | let posterPath: String 36 | let backdropPath: String? 37 | let originalLanguage: String 38 | let originalName: String 39 | let releaseDate: String 40 | 41 | enum CodingKeys: String, CodingKey { 42 | case id 43 | case video 44 | case name = "title" 45 | case overview 46 | case popularity 47 | case adult 48 | case genre = "genre_ids" 49 | case voteCount = "vote_count" 50 | case voteAverage = "vote_average" 51 | case posterPath = "poster_path" 52 | case backdropPath = "backdrop_path" 53 | case originalLanguage = "original_language" 54 | case originalName = "original_title" 55 | case releaseDate = "release_date" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Models/DiscoverTVResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiscoverTVResponse.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-14. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct DiscoverTVResponse: Codable { 12 | let page: Int 13 | let totalResults: Int 14 | let totalPages: Int 15 | let shows: [DiscoverTVModel] 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case page 19 | case totalResults = "total_results" 20 | case totalPages = "total_pages" 21 | case shows = "results" 22 | } 23 | } 24 | 25 | struct DiscoverTVModel: Codable, Media { 26 | let id: Int 27 | let name: String 28 | let overview: String 29 | let popularity: Float 30 | let genre: [Int] 31 | let voteCount: Int 32 | let voteAverage: Float 33 | let posterPath: String 34 | let backdropPath: String? 35 | let originalLanguage: String 36 | let originalName: String 37 | let firstAirDate: Date 38 | let originCountry: [String] 39 | 40 | enum CodingKeys: String, CodingKey { 41 | case id 42 | case name 43 | case overview 44 | case popularity 45 | case genre = "genre_ids" 46 | case voteCount = "vote_count" 47 | case voteAverage = "vote_average" 48 | case posterPath = "poster_path" 49 | case backdropPath = "backdrop_path" 50 | case originalLanguage = "original_language" 51 | case originalName = "original_name" 52 | case firstAirDate = "first_air_date" 53 | case originCountry = "origin_country" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Models/Media.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Media.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-22. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Media { 12 | var id: Int { get } 13 | var name: String { get } 14 | var overview: String { get } 15 | var popularity: Float { get } 16 | var genre: [Int] { get } 17 | var voteCount: Int { get } 18 | var voteAverage: Float { get } 19 | var posterPath: String { get } 20 | var backdropPath: String? { get } 21 | var originalLanguage: String { get } 22 | var originalName: String { get } 23 | } 24 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Protocols/HasProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HasProtocols.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | protocol HasStore { 13 | var store: Store { get } 14 | } 15 | 16 | protocol HasNetworkService { 17 | var networkService: NetworkService { get } 18 | } 19 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Protocols/Injectable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Injectable.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-04. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Injectable { 12 | associatedtype InjectionContainer 13 | var injectionContainer: InjectionContainer { get set } 14 | } 15 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Protocols/ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-07. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ViewModel {} 12 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Protocols/ViewModelBased.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModelBased.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-07. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ViewModelBased { 12 | associatedtype ViewModelType: ViewModel 13 | 14 | var viewModel: ViewModelType! { get set } 15 | } 16 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Services/NetworkService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkService.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-02. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | import RxSwift 12 | 13 | final class NetworkService { 14 | 15 | let baseUrl: URL 16 | let apiKey: String 17 | 18 | init(withBaseUrl baseUrl: URL, andApiKey apiKey: String) { 19 | self.baseUrl = baseUrl 20 | self.apiKey = apiKey 21 | } 22 | 23 | func fetch (withRoute route: Route) -> Single { 24 | 25 | guard let url = route.getPath(forBaseUrl: self.baseUrl, andApiKey: self.apiKey) else { 26 | return Single.error(NSError(domain: "warpfactor.io", code: 404)) 27 | } 28 | 29 | return Observable.create { (observer) -> Disposable in 30 | let request = Alamofire.request(url).responseData(completionHandler: { (response) in 31 | if let error = response.error { 32 | observer.onError(error) 33 | return 34 | } 35 | 36 | if let data = response.data, 37 | let model = try? JSONDecoder().decode(Model.self, from: data) { 38 | observer.onNext(model) 39 | observer.onCompleted() 40 | } else { 41 | observer.onError(NSError(domain: "warpfactor.io", code: 404)) 42 | } 43 | }) 44 | 45 | return Disposables.create { 46 | request.cancel() 47 | } 48 | }.asSingle() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/Services/Routes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Routes.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-06-02. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | struct Route { 13 | let endpoint: String 14 | var id: Int? 15 | 16 | init (withEndpoint endpoint: String, withId id: Int? = nil) { 17 | self.endpoint = endpoint 18 | self.id = id 19 | } 20 | 21 | func getPath(forBaseUrl baseUrl: URL, andApiKey apiKey: String) -> URL? { 22 | var endpoint = self.endpoint 23 | if let id = self.id { 24 | endpoint = endpoint+"\(id)" 25 | } 26 | 27 | return URL(string: baseUrl.absoluteString+endpoint+"?api_key=\(apiKey)") 28 | } 29 | } 30 | 31 | struct Routes { 32 | static let discoverMovie = Route(withEndpoint: "discover/movie") 33 | static let discoverTV = Route(withEndpoint: "discover/tv") 34 | } 35 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/State/AppActions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IncreaseAction.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-28. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | enum MovieAction: Action { 13 | case startLoadingMovies 14 | case loadMovies (movies: [DiscoverMovieModel]) 15 | case loadMovie (movieId: Int) 16 | } 17 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/State/AppLenses.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppLenses.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 2018-08-22. 6 | // Copyright © 2018 Wittemberg, Thibault. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | struct AppLenses { 13 | 14 | static let movieListLens = Lens(get: { $0.movieListState }, set: { (appState, movieListState) -> AppState in 15 | var mutableState = appState 16 | mutableState.movieListState = movieListState 17 | return mutableState 18 | }) 19 | 20 | static let movieDetailLens = Lens(get: { $0.movieDetailState }, set: { (appState, detailState) -> AppState in 21 | var mutableState = appState 22 | mutableState.movieDetailState = detailState 23 | return mutableState 24 | }) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/State/AppMutators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppMutators.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 2018-08-28. 6 | // Copyright © 2018 Wittemberg, Thibault. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | struct AppMutators { 13 | static let movieListMutator = Mutator(lens: AppLenses.movieListLens, reducer: movieListReducer) 14 | static let movieDetailMutator = Mutator(lens: AppLenses.movieDetailLens, reducer: movieDetailReducer) 15 | } 16 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/State/AppReducers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reducers.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-28. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | func movieListReducer (state: AppState, action: Action) -> MovieListState { 13 | 14 | guard let action = action as? MovieAction else { 15 | return state.movieListState 16 | } 17 | 18 | switch action { 19 | case .startLoadingMovies: 20 | return .loading 21 | case .loadMovies(let movies): 22 | return .loaded(movies) 23 | default: 24 | return state.movieListState 25 | } 26 | } 27 | 28 | func movieDetailReducer (state: AppState, action: Action) -> MovieDetailState { 29 | 30 | guard let action = action as? MovieAction else { 31 | return state.movieDetailState 32 | } 33 | 34 | // according to the action we create a new state 35 | switch action { 36 | case .startLoadingMovies, .loadMovies(_): 37 | return .empty 38 | case .loadMovie(let movieId): 39 | guard case let .loaded(movies) = state.movieListState else { return state.movieDetailState } 40 | let movie = movies.filter { $0.id == movieId }.first 41 | if let movieDetail = movie { 42 | return .loaded(movieDetail) 43 | } 44 | return state.movieDetailState 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /RxReduceDemo/RxReduceDemo/State/AppState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppState.swift 3 | // RxReduceDemo 4 | // 5 | // Created by Thibault Wittemberg on 18-04-28. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | struct AppState: Equatable { 13 | var movieListState: MovieListState 14 | var movieDetailState: MovieDetailState 15 | } 16 | 17 | enum MovieListState: Equatable { 18 | case empty 19 | case loading 20 | case loaded ([DiscoverMovieModel]) 21 | } 22 | 23 | enum MovieDetailState: Equatable { 24 | case empty 25 | case loaded (DiscoverMovieModel) 26 | } 27 | -------------------------------------------------------------------------------- /RxReduceTests/ActionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxReduceTests.swift 3 | // RxReduceTests 4 | // 5 | // Created by Thibault Wittemberg on 18-04-22. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import RxSwift 11 | import RxCocoa 12 | import RxBlocking 13 | import RxReduce 14 | 15 | class ActionTests: XCTestCase { 16 | 17 | let disposeBag = DisposeBag() 18 | 19 | func testSynchronousAction() throws { 20 | 21 | let increaseAction = AppAction.increase(increment: 10) 22 | 23 | let action = try increaseAction.toAsync().toBlocking().single() 24 | if case let AppAction.increase(increment) = action { 25 | XCTAssertEqual(10, increment) 26 | } else { 27 | XCTFail() 28 | } 29 | } 30 | 31 | func testAsynchronousAction () throws { 32 | 33 | let increaseAction = Observable.just(AppAction.increase(increment: 10)) 34 | 35 | let action = try increaseAction.toAsync().toBlocking().single() 36 | if case let AppAction.increase(increment) = action { 37 | XCTAssertEqual(10, increment) 38 | } else { 39 | XCTFail() 40 | } 41 | } 42 | 43 | func testArrayOfActions () throws { 44 | 45 | let actions: [Action] = [AppAction.increase(increment: 10), AppAction.increase(increment: 20), AppAction.increase(increment: 30)] 46 | 47 | var initialIncrement = 10 48 | let actionsToTest = try actions.toAsync().toBlocking().toArray() 49 | 50 | actionsToTest.forEach { 51 | if case let AppAction.increase(increment) = $0 { 52 | XCTAssertEqual(initialIncrement, increment) 53 | initialIncrement += 10 54 | } else { 55 | XCTFail() 56 | } 57 | } 58 | } 59 | 60 | func testArrayOfActionsWithObservable () throws { 61 | 62 | let actions: [Action] = [AppAction.increase(increment: 10), Observable.just(AppAction.increase(increment: 20)), AppAction.increase(increment: 30)] 63 | 64 | var initialIncrement = 10 65 | let actionsToTest = try actions.toAsync().toBlocking().toArray() 66 | 67 | actionsToTest.forEach { 68 | if case let AppAction.increase(increment) = $0 { 69 | XCTAssertEqual(initialIncrement, increment) 70 | initialIncrement += 10 71 | } else { 72 | XCTFail() 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RxReduceTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RxReduceTests/State/Actions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Actions.swift 3 | // RxReduceTests 4 | // 5 | // Created by Thibault Wittemberg on 18-06-11. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | enum AppAction: Action { 13 | case increase(increment: Int) 14 | case decrease(decrement: Int) 15 | case logUser(user: String) 16 | case clear 17 | } 18 | -------------------------------------------------------------------------------- /RxReduceTests/State/Reducers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reducers.swift 3 | // RxReduceTests 4 | // 5 | // Created by Thibault Wittemberg on 18-06-11. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | func counterReduce (state: TestState, action: Action) -> CounterState { 13 | 14 | guard let action = action as? AppAction else { return state.counterState } 15 | 16 | var currentCounter = 0 17 | 18 | // we extract the current counter value from the current state 19 | switch state.counterState { 20 | case .decreasing(let counter), .increasing(let counter): 21 | currentCounter = counter 22 | default: 23 | currentCounter = 0 24 | } 25 | 26 | // according to the action we mutate the counter state 27 | switch action { 28 | case .increase(let increment): 29 | return .increasing(currentCounter+increment) 30 | case .decrease(let decrement): 31 | return .decreasing(currentCounter-decrement) 32 | case .clear: 33 | return .empty 34 | default: 35 | return state.counterState 36 | } 37 | } 38 | 39 | func userReduce (state: TestState, action: Action) -> UserState { 40 | 41 | guard let action = action as? AppAction else { return state.userState } 42 | 43 | // according to the action we mutate the users state 44 | switch action { 45 | case .logUser(let user): 46 | return .loggedIn(name: user) 47 | case .clear: 48 | return .loggedOut 49 | default: 50 | return state.userState 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /RxReduceTests/State/States.swift: -------------------------------------------------------------------------------- 1 | // 2 | // State.swift 3 | // RxReduceTests 4 | // 5 | // Created by Thibault Wittemberg on 18-06-11. 6 | // Copyright (c) RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxReduce 11 | 12 | struct TestState: Equatable { 13 | var counterState: CounterState 14 | var userState: UserState 15 | } 16 | 17 | enum CounterState: Equatable { 18 | case empty 19 | case increasing (Int) 20 | case decreasing (Int) 21 | } 22 | 23 | enum UserState: Equatable { 24 | case loggedIn (name: String) 25 | case loggedOut 26 | } 27 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Classes

80 |

The following classes are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Store 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    A default store that will handle a specific kind of State

    100 | 101 | See more 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    public final class Store<StateType> : StoreType where StateType : State
    108 | 109 |
    110 |
    111 |
    112 |
    113 |
  • 114 |
115 |
116 |
117 |
118 | 122 |
123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Extensions

80 |

The following extensions are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Array 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 | 100 | See more 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    struct Array<Element> : _DestructorSafeContainer
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | Observable 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 | 127 | See more 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    class Observable<Element> : ObservableType
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
141 |
142 |
143 |
144 | 148 |
149 |
150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/Extensions/Array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Array Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Array

80 |
81 |
82 |
struct Array<Element> : _DestructorSafeContainer
83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |
    91 |
  • 92 |
    93 | 94 | 95 | 96 | toAsync() 97 | 98 |
    99 |
    100 |
    101 |
    102 |
    103 |
    104 | 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public func toAsync() -> Observable<Action>
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/Extensions/Observable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Observable Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Observable

80 |
81 |
82 |
class Observable<Element> : ObservableType
83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |
    91 |
  • 92 |
    93 | 94 | 95 | 96 | toAsync() 97 | 98 |
    99 |
    100 |
    101 |
    102 |
    103 |
    104 | 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public func toAsync() -> Observable<Action>
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Protocols

80 |

The following protocols are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Action 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    Conform to an Action to mutate the State synchronously or asynchronously 100 | Arrays of Actions and Observable of Action are considered to be Actions as well

    101 | 102 | See more 103 |
    104 |
    105 |

    Declaration

    106 |
    107 |

    Swift

    108 |
    public protocol Action
    109 | 110 |
    111 |
    112 |
    113 |
    114 |
  • 115 |
116 |
117 |
118 |
    119 |
  • 120 |
    121 | 122 | 123 | 124 | State 125 | 126 |
    127 |
    128 |
    129 |
    130 |
    131 |
    132 |

    Conform to the State protocol to be handled by a Store

    133 | 134 |
    135 |
    136 |

    Declaration

    137 |
    138 |

    Swift

    139 |
    public protocol State
    140 | 141 |
    142 |
    143 |
    144 |
    145 |
  • 146 |
147 |
148 |
149 |
    150 |
  • 151 |
    152 | 153 | 154 | 155 | StoreType 156 | 157 |
    158 |
    159 |
    160 |
    161 |
    162 |
    163 |

    A Store holds the state, mutate the state through actions / reducers and exposes the state via a Driver 164 | A Store is dedicated to a State Type

    165 | 166 | See more 167 |
    168 |
    169 |

    Declaration

    170 |
    171 |

    Swift

    172 |
    public protocol StoreType
    173 | 174 |
    175 |
    176 |
    177 |
    178 |
  • 179 |
180 |
181 |
182 |
183 | 187 |
188 |
189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/Protocols/Action.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Action Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Action

80 |
81 |
82 |
public protocol Action
83 | 84 |
85 |
86 |

Conform to an Action to mutate the State synchronously or asynchronously 87 | Arrays of Actions and Observable of Action are considered to be Actions as well

88 | 89 |
90 |
91 |
92 |
    93 |
  • 94 |
    95 | 96 | 97 | 98 | toAsync() 99 | 100 | 101 | Default implementation 102 | 103 |
    104 |
    105 |
    106 |
    107 |
    108 |
    109 |

    Convert the action into an Observable so it is compliant with asynchronous processing

    110 | 111 |
    112 |

    Default Implementation

    113 |
    114 | 115 |
    116 |
    117 |

    Declaration

    118 |
    119 |

    Swift

    120 |
    func toAsync() -> Observable<Action>
    121 | 122 |
    123 |
    124 |
    125 |

    Return Value

    126 |

    the asynchronous action

    127 |
    128 |
    129 |
    130 |
  • 131 |
132 |
133 |
134 |
135 | 139 |
140 |
141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Type Aliases

80 |

The following type aliases are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Reducer 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    A Reducer mutates an input state into an output state according to an action

    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    public typealias Reducer<StateType> = (_ state: StateType?, _ action: Action) -> StateType
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | Middleware 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 |

    A Middleware has not effect on the state, it us just triggered by a dispatch action

    127 | 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    public typealias Middleware<StateType> = (_ state: StateType?, _ action: Action) -> Void
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
141 |
142 |
143 |
144 | 148 |
149 |
150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 60px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; } 223 | .item .declaration-note { 224 | font-size: .85em; 225 | color: gray; 226 | font-style: italic; } 227 | 228 | .pointer-container { 229 | border-bottom: 1px solid #e2e2e2; 230 | left: -23px; 231 | padding-bottom: 13px; 232 | position: relative; 233 | width: 110%; } 234 | 235 | .pointer { 236 | background: #f9f9f9; 237 | border-left: 1px solid #e2e2e2; 238 | border-top: 1px solid #e2e2e2; 239 | height: 12px; 240 | left: 21px; 241 | top: -7px; 242 | -webkit-transform: rotate(45deg); 243 | -moz-transform: rotate(45deg); 244 | -o-transform: rotate(45deg); 245 | transform: rotate(45deg); 246 | position: absolute; 247 | width: 12px; } 248 | 249 | .height-container { 250 | display: none; 251 | left: -25px; 252 | padding: 0 25px; 253 | position: relative; 254 | width: 100%; 255 | overflow: hidden; } 256 | .height-container .section { 257 | background: #f9f9f9; 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -25px; 260 | position: relative; 261 | width: 100%; 262 | padding-top: 10px; 263 | padding-bottom: 5px; } 264 | 265 | .aside, .language { 266 | padding: 6px 12px; 267 | margin: 12px 0; 268 | border-left: 5px solid #dddddd; 269 | overflow-y: hidden; } 270 | .aside .aside-title, .language .aside-title { 271 | font-size: 9px; 272 | letter-spacing: 2px; 273 | text-transform: uppercase; 274 | padding-bottom: 0; 275 | margin: 0; 276 | color: #aaa; 277 | -webkit-user-select: none; } 278 | .aside p:last-child, .language p:last-child { 279 | margin-bottom: 0; } 280 | 281 | .language { 282 | border-left: 5px solid #cde9f4; } 283 | .language .aside-title { 284 | color: #4b8afb; } 285 | 286 | .aside-warning { 287 | border-left: 5px solid #ff6666; } 288 | .aside-warning .aside-title { 289 | color: #ff0000; } 290 | 291 | .graybox { 292 | border-collapse: collapse; 293 | width: 100%; } 294 | .graybox p { 295 | margin: 0; 296 | word-break: break-word; 297 | min-width: 50px; } 298 | .graybox td { 299 | border: 1px solid #e2e2e2; 300 | padding: 5px 25px 5px 10px; 301 | vertical-align: middle; } 302 | .graybox tr td:first-of-type { 303 | text-align: right; 304 | padding: 7px; 305 | vertical-align: top; 306 | word-break: normal; 307 | width: 40px; } 308 | 309 | .slightly-smaller { 310 | font-size: 0.9em; } 311 | 312 | #footer { 313 | position: absolute; 314 | bottom: 10px; 315 | margin-left: 25px; } 316 | #footer p { 317 | margin: 0; 318 | color: #aaa; 319 | font-size: 0.8em; } 320 | 321 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 322 | display: none; } 323 | html.dash .main-content { 324 | width: 980px; 325 | margin-left: 0; 326 | border: none; 327 | width: 100%; 328 | top: 0; 329 | padding-bottom: 0; } 330 | html.dash .height-container { 331 | display: block; } 332 | html.dash .item .token { 333 | margin-left: 0; } 334 | html.dash .content-wrapper { 335 | width: auto; } 336 | html.dash #footer { 337 | position: static; } 338 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.rxreduce 7 | CFBundleName 8 | RxReduce 9 | DocSetPlatformFamily 10 | rxreduce 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Classes

80 |

The following classes are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Store 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    A default store that will handle a specific kind of State

    100 | 101 | See more 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    public final class Store<StateType> : StoreType where StateType : State
    108 | 109 |
    110 |
    111 |
    112 |
    113 |
  • 114 |
115 |
116 |
117 |
118 | 122 |
123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Extensions

80 |

The following extensions are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Array 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 | 100 | See more 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    struct Array<Element> : _DestructorSafeContainer
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | Observable 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 | 127 | See more 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    class Observable<Element> : ObservableType
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
141 |
142 |
143 |
144 | 148 |
149 |
150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Extensions/Array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Array Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Array

80 |
81 |
82 |
struct Array<Element> : _DestructorSafeContainer
83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |
    91 |
  • 92 |
    93 | 94 | 95 | 96 | toAsync() 97 | 98 |
    99 |
    100 |
    101 |
    102 |
    103 |
    104 | 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public func toAsync() -> Observable<Action>
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Extensions/Observable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Observable Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Observable

80 |
81 |
82 |
class Observable<Element> : ObservableType
83 | 84 |
85 |
86 | 87 |
88 |
89 |
90 |
    91 |
  • 92 |
    93 | 94 | 95 | 96 | toAsync() 97 | 98 |
    99 |
    100 |
    101 |
    102 |
    103 |
    104 | 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public func toAsync() -> Observable<Action>
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Protocols

80 |

The following protocols are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Action 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    Conform to an Action to mutate the State synchronously or asynchronously 100 | Arrays of Actions and Observable of Action are considered to be Actions as well

    101 | 102 | See more 103 |
    104 |
    105 |

    Declaration

    106 |
    107 |

    Swift

    108 |
    public protocol Action
    109 | 110 |
    111 |
    112 |
    113 |
    114 |
  • 115 |
116 |
117 |
118 |
    119 |
  • 120 |
    121 | 122 | 123 | 124 | State 125 | 126 |
    127 |
    128 |
    129 |
    130 |
    131 |
    132 |

    Conform to the State protocol to be handled by a Store

    133 | 134 |
    135 |
    136 |

    Declaration

    137 |
    138 |

    Swift

    139 |
    public protocol State
    140 | 141 |
    142 |
    143 |
    144 |
    145 |
  • 146 |
147 |
148 |
149 |
    150 |
  • 151 |
    152 | 153 | 154 | 155 | StoreType 156 | 157 |
    158 |
    159 |
    160 |
    161 |
    162 |
    163 |

    A Store holds the state, mutate the state through actions / reducers and exposes the state via a Driver 164 | A Store is dedicated to a State Type

    165 | 166 | See more 167 |
    168 |
    169 |

    Declaration

    170 |
    171 |

    Swift

    172 |
    public protocol StoreType
    173 | 174 |
    175 |
    176 |
    177 |
    178 |
  • 179 |
180 |
181 |
182 |
183 | 187 |
188 |
189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Protocols/Action.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Action Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Action

80 |
81 |
82 |
public protocol Action
83 | 84 |
85 |
86 |

Conform to an Action to mutate the State synchronously or asynchronously 87 | Arrays of Actions and Observable of Action are considered to be Actions as well

88 | 89 |
90 |
91 |
92 |
    93 |
  • 94 |
    95 | 96 | 97 | 98 | toAsync() 99 | 100 | 101 | Default implementation 102 | 103 |
    104 |
    105 |
    106 |
    107 |
    108 |
    109 |

    Convert the action into an Observable so it is compliant with asynchronous processing

    110 | 111 |
    112 |

    Default Implementation

    113 |
    114 | 115 |
    116 |
    117 |

    Declaration

    118 |
    119 |

    Swift

    120 |
    func toAsync() -> Observable<Action>
    121 | 122 |
    123 |
    124 |
    125 |

    Return Value

    126 |

    the asynchronous action

    127 |
    128 |
    129 |
    130 |
  • 131 |
132 |
133 |
134 |
135 | 139 |
140 |
141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

RxReduce Docs (100% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 76 |
77 |
78 |
79 |

Type Aliases

80 |

The following type aliases are available globally.

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | Reducer 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    A Reducer mutates an input state into an output state according to an action

    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    public typealias Reducer<StateType> = (_ state: StateType?, _ action: Action) -> StateType
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | Middleware 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 |

    A Middleware has not effect on the state, it us just triggered by a dispatch action

    127 | 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    public typealias Middleware<StateType> = (_ state: StateType?, _ action: Action) -> Void
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
141 |
142 |
143 |
144 | 148 |
149 |
150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 60px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; } 223 | .item .declaration-note { 224 | font-size: .85em; 225 | color: gray; 226 | font-style: italic; } 227 | 228 | .pointer-container { 229 | border-bottom: 1px solid #e2e2e2; 230 | left: -23px; 231 | padding-bottom: 13px; 232 | position: relative; 233 | width: 110%; } 234 | 235 | .pointer { 236 | background: #f9f9f9; 237 | border-left: 1px solid #e2e2e2; 238 | border-top: 1px solid #e2e2e2; 239 | height: 12px; 240 | left: 21px; 241 | top: -7px; 242 | -webkit-transform: rotate(45deg); 243 | -moz-transform: rotate(45deg); 244 | -o-transform: rotate(45deg); 245 | transform: rotate(45deg); 246 | position: absolute; 247 | width: 12px; } 248 | 249 | .height-container { 250 | display: none; 251 | left: -25px; 252 | padding: 0 25px; 253 | position: relative; 254 | width: 100%; 255 | overflow: hidden; } 256 | .height-container .section { 257 | background: #f9f9f9; 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -25px; 260 | position: relative; 261 | width: 100%; 262 | padding-top: 10px; 263 | padding-bottom: 5px; } 264 | 265 | .aside, .language { 266 | padding: 6px 12px; 267 | margin: 12px 0; 268 | border-left: 5px solid #dddddd; 269 | overflow-y: hidden; } 270 | .aside .aside-title, .language .aside-title { 271 | font-size: 9px; 272 | letter-spacing: 2px; 273 | text-transform: uppercase; 274 | padding-bottom: 0; 275 | margin: 0; 276 | color: #aaa; 277 | -webkit-user-select: none; } 278 | .aside p:last-child, .language p:last-child { 279 | margin-bottom: 0; } 280 | 281 | .language { 282 | border-left: 5px solid #cde9f4; } 283 | .language .aside-title { 284 | color: #4b8afb; } 285 | 286 | .aside-warning { 287 | border-left: 5px solid #ff6666; } 288 | .aside-warning .aside-title { 289 | color: #ff0000; } 290 | 291 | .graybox { 292 | border-collapse: collapse; 293 | width: 100%; } 294 | .graybox p { 295 | margin: 0; 296 | word-break: break-word; 297 | min-width: 50px; } 298 | .graybox td { 299 | border: 1px solid #e2e2e2; 300 | padding: 5px 25px 5px 10px; 301 | vertical-align: middle; } 302 | .graybox tr td:first-of-type { 303 | text-align: right; 304 | padding: 7px; 305 | vertical-align: top; 306 | word-break: normal; 307 | width: 40px; } 308 | 309 | .slightly-smaller { 310 | font-size: 0.9em; } 311 | 312 | #footer { 313 | position: absolute; 314 | bottom: 10px; 315 | margin-left: 25px; } 316 | #footer p { 317 | margin: 0; 318 | color: #aaa; 319 | font-size: 0.8em; } 320 | 321 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 322 | display: none; } 323 | html.dash .main-content { 324 | width: 980px; 325 | margin-left: 0; 326 | border: none; 327 | width: 100%; 328 | top: 0; 329 | padding-bottom: 0; } 330 | html.dash .height-container { 331 | display: block; } 332 | html.dash .item .token { 333 | margin-left: 0; } 334 | html.dash .content-wrapper { 335 | width: auto; } 336 | html.dash #footer { 337 | position: static; } 338 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/docsets/RxReduce.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | var tokenOffset = "15px"; 27 | var original = link.css('marginLeft') == tokenOffset; 28 | link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); 29 | $content = link.parent().parent().next(); 30 | $content.slideToggle(animationDuration); 31 | 32 | // Keeps the document from jumping to the hash. 33 | var href = $(this).attr('href'); 34 | if (history.pushState) { 35 | history.pushState({}, '', href); 36 | } else { 37 | location.hash = href; 38 | } 39 | event.preventDefault(); 40 | }); 41 | 42 | // Dumb down quotes within code blocks that delimit strings instead of quotations 43 | // https://github.com/realm/jazzy/issues/714 44 | $("code q").replaceWith(function () { 45 | return ["\"", $(this).contents(), "\""]; 46 | }); 47 | -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:8RxReduce7Reducera":{"name":"Reducer","abstract":"

A Reducer mutates an input state into an output state according to an action

"},"Typealiases.html#/s:8RxReduce10Middlewarea":{"name":"Middleware","abstract":"

A Middleware has not effect on the state, it us just triggered by a dispatch action

"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP05StateD0":{"name":"StateType","abstract":"

A store is dedicated to the mutation/observation of this StateType

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_tlF":{"name":"state(from:)","abstract":"

The current State or SubState (UI compliant)

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_ts9EquatableRd__lF":{"name":"state(from:)","abstract":"

The current State or SubState (UI compliant), avoiding duplicate notifications in case of state equality

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypePxs15ContiguousArrayVy05StateD0QzAGSg_AA6Action_ptcG12withReducers_AEyyAH_AaI_ptcGSg0I11Middlewarestcfc":{"name":"init(withReducers:withMiddlewares:)","abstract":"

Inits the Store with its reducers stack

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP8dispatchyAA6Action_p6action_tF":{"name":"dispatch(action:)","abstract":"

Dispatch an action through the reducers to mutate the state

","parent_name":"StoreType"},"Protocols/Action.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","abstract":"

Convert the action into an Observable so it is compliant with asynchronous processing

","parent_name":"Action"},"Protocols/Action.html":{"name":"Action","abstract":"

Conform to an Action to mutate the State synchronously or asynchronously"},"Protocols.html#/s:8RxReduce5StateP":{"name":"State","abstract":"

Conform to the State protocol to be handled by a Store

"},"Protocols/StoreType.html":{"name":"StoreType","abstract":"

A Store holds the state, mutate the state through actions / reducers and exposes the state via a Driver"},"Extensions/Observable.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","parent_name":"Observable"},"Extensions/Array.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","parent_name":"Array"},"Extensions/Array.html":{"name":"Array"},"Extensions/Observable.html":{"name":"Observable"},"Classes/Store.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_tlF":{"name":"state(from:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_ts9EquatableRd__lF":{"name":"state(from:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypePxs15ContiguousArrayVy05StateD0QzAGSg_AA6Action_ptcG12withReducers_AEyyAH_AaI_ptcGSg0I11Middlewarestcfc":{"name":"init(withReducers:withMiddlewares:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypeP8dispatchyAA6Action_p6action_tF":{"name":"dispatch(action:)","parent_name":"Store"},"Classes/Store.html":{"name":"Store","abstract":"

A default store that will handle a specific kind of State

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/thibaultwittemberg/Development/RxReduce" 6 | } -------------------------------------------------------------------------------- /docs/docsets/RxReduce.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/docsets/RxReduce.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/RxReduce.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/docsets/RxReduce.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxReduce/e67b339885f17c9191706478926e96fc230de148/docs/img/gh.png -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | var tokenOffset = "15px"; 27 | var original = link.css('marginLeft') == tokenOffset; 28 | link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); 29 | $content = link.parent().parent().next(); 30 | $content.slideToggle(animationDuration); 31 | 32 | // Keeps the document from jumping to the hash. 33 | var href = $(this).attr('href'); 34 | if (history.pushState) { 35 | history.pushState({}, '', href); 36 | } else { 37 | location.hash = href; 38 | } 39 | event.preventDefault(); 40 | }); 41 | 42 | // Dumb down quotes within code blocks that delimit strings instead of quotations 43 | // https://github.com/realm/jazzy/issues/714 44 | $("code q").replaceWith(function () { 45 | return ["\"", $(this).contents(), "\""]; 46 | }); 47 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:8RxReduce7Reducera":{"name":"Reducer","abstract":"

A Reducer mutates an input state into an output state according to an action

"},"Typealiases.html#/s:8RxReduce10Middlewarea":{"name":"Middleware","abstract":"

A Middleware has not effect on the state, it us just triggered by a dispatch action

"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP05StateD0":{"name":"StateType","abstract":"

A store is dedicated to the mutation/observation of this StateType

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_tlF":{"name":"state(from:)","abstract":"

The current State or SubState (UI compliant)

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_ts9EquatableRd__lF":{"name":"state(from:)","abstract":"

The current State or SubState (UI compliant), avoiding duplicate notifications in case of state equality

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypePxs15ContiguousArrayVy05StateD0QzAGSg_AA6Action_ptcG12withReducers_AEyyAH_AaI_ptcGSg0I11Middlewarestcfc":{"name":"init(withReducers:withMiddlewares:)","abstract":"

Inits the Store with its reducers stack

","parent_name":"StoreType"},"Protocols/StoreType.html#/s:8RxReduce9StoreTypeP8dispatchyAA6Action_p6action_tF":{"name":"dispatch(action:)","abstract":"

Dispatch an action through the reducers to mutate the state

","parent_name":"StoreType"},"Protocols/Action.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","abstract":"

Convert the action into an Observable so it is compliant with asynchronous processing

","parent_name":"Action"},"Protocols/Action.html":{"name":"Action","abstract":"

Conform to an Action to mutate the State synchronously or asynchronously"},"Protocols.html#/s:8RxReduce5StateP":{"name":"State","abstract":"

Conform to the State protocol to be handled by a Store

"},"Protocols/StoreType.html":{"name":"StoreType","abstract":"

A Store holds the state, mutate the state through actions / reducers and exposes the state via a Driver"},"Extensions/Observable.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","parent_name":"Observable"},"Extensions/Array.html#/s:8RxReduce6ActionP7toAsync0A5Swift10ObservableCyAaB_pGyF":{"name":"toAsync()","parent_name":"Array"},"Extensions/Array.html":{"name":"Array"},"Extensions/Observable.html":{"name":"Observable"},"Classes/Store.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_tlF":{"name":"state(from:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypeP5state0A5Cocoa14SharedSequenceVyAE21DriverSharingStrategyVqd__Gqd__05StateD0Qzc4from_ts9EquatableRd__lF":{"name":"state(from:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypePxs15ContiguousArrayVy05StateD0QzAGSg_AA6Action_ptcG12withReducers_AEyyAH_AaI_ptcGSg0I11Middlewarestcfc":{"name":"init(withReducers:withMiddlewares:)","parent_name":"Store"},"Classes/Store.html#/s:8RxReduce9StoreTypeP8dispatchyAA6Action_p6action_tF":{"name":"dispatch(action:)","parent_name":"Store"},"Classes/Store.html":{"name":"Store","abstract":"

A default store that will handle a specific kind of State

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} --------------------------------------------------------------------------------