diff --git a/CHANGELOG.md b/CHANGELOG.md index aeaed19..6bac38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,19 @@ All notable changes to this project will be documented in this file. --- -## [0.3.0](https://github.com/nazeehshoura/RxState/releases/tag/0.3.0) -#### Anomalies +## [0.5.0](https://github.com/nazeehshoura/RxState/releases/tag/0.5.0) -* Replaces `observe(Driver) to observe(StoreType) in `MiddlewareType`. +* Uses RxSwift 3.5.0 +* Uses RxCocoa 3.5.0 +* Renames `action` to `lastDispatchedaAtion`. +* Renames `action` to `lastDispatchedaAtion`. +* Defines `StoreState` as a type alias for `[SubstateType]` -## [0.2.1](https://github.com/nazeehshoura/RxState/releases/tag/0.2.1) +## [0.3.0](https://github.com/nazeehshoura/RxState/releases/tag/0.3.0) -#### Anomalies +* Replaces `observe(Driver)` with `observe(StoreType)` in `MiddlewareType`. + +## [0.2.1](https://github.com/nazeehshoura/RxState/releases/tag/0.2.1) * Renames `Middleware` to `MiddlewareType`. diff --git a/RxState.podspec b/RxState.podspec index 66c9963..8772ed2 100644 --- a/RxState.podspec +++ b/RxState.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "RxState" - s.version = "0.3.0" + s.version = "0.5.0" s.summary = "RxSwift + Redux" # This description is used to generate tags and improve search results. diff --git a/RxState.xcodeproj/project.pbxproj b/RxState.xcodeproj/project.pbxproj index 872ff76..e978654 100644 --- a/RxState.xcodeproj/project.pbxproj +++ b/RxState.xcodeproj/project.pbxproj @@ -364,7 +364,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -420,7 +420,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -450,10 +450,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = RxState/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -474,10 +474,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = RxState/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/RxState/Info.plist b/RxState/Info.plist index 961cfe7..090e5f5 100644 --- a/RxState/Info.plist +++ b/RxState/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.3.0 + 0.5.0 CFBundleSignature ???? CFBundleVersion - 3 + 4 NSPrincipalClass diff --git a/RxState/RxState.swift b/RxState/RxState.swift index 393634f..237b8ff 100644 --- a/RxState/RxState.swift +++ b/RxState/RxState.swift @@ -21,22 +21,27 @@ public protocol SubstateType {} 3. Allows access to the application state and last dipached action via `currentStateLastAction` Driver (Useful for creating middlewere). */ public protocol StoreType { - + /// Inisiate the store with a main reducer that the dispatch method will use to reduce incomming actions init(mainReducer: @escaping MainReducer) + + /** + The last dispatched action with the resulted state. + */ + var stateLastAction: Driver { get } /** - A Hot Observables of `StoreState`. + A Driver of `StoreState`. To add substates to `StoreState`, dispatch `Store.Action.register(subStates: [SubstateType])`. */ - var state: Observable { get } - + var state: Driver { get } + /** - A Hot Observables of the last dispatched action. + A Driver of the last dispatched action. If the value is `nil`, it means that no Action has been dispatched yet. */ - var lastDispatchedaAtion: Observable { get } - + var lastDispatchedaAtion: Driver { get } + /** Dispatches a action, causing the `state` to be updated. @@ -53,37 +58,40 @@ public protocol StoreType { */ func register(middlewares: [MiddlewareType]) - + } public class Store: StoreType { - + fileprivate let mainReducer: MainReducer - + required public init(mainReducer: @escaping MainReducer) { self.mainReducer = mainReducer } - - public var state: Observable<[SubstateType]> { - return _state - .asObservable() - .share(replay: 1, scope: SubjectLifetimeScope.forever) - .catchErrorJustReturn([]) + + public var stateLastAction: Driver { + return Driver.zip( + state + , lastDispatchedaAtion + ) { (state: [SubstateType], lastDispatchedaAtion: ActionType?) -> StateLastAction in + StateLastAction(state, lastDispatchedaAtion) + } + } + + public var state: Driver<[SubstateType]> { + return _state.asDriver() } public var middlewares: [MiddlewareType] = [] - + private let _state: Variable = Variable(StoreState()) - - public var lastDispatchedaAtion: Observable { - return _lastDispatchedaAtion - .asObservable() - .share(replay: 1, scope: SubjectLifetimeScope.forever) - .catchErrorJustReturn(nil) + + public var lastDispatchedaAtion: Driver { + return _lastDispatchedaAtion.asDriver() } private let _lastDispatchedaAtion: Variable = Variable(nil) - + public func dispatch(action: ActionType) { if let storeAction = action as? Store.Action { _state.value = Store.reduce(state: _state.value, sction: storeAction) @@ -95,7 +103,7 @@ public class Store: StoreType { public func register(middlewares: [MiddlewareType]) { self.middlewares.append(contentsOf: middlewares) - + for middleware in middlewares { middleware.observe(store: self) } @@ -110,7 +118,7 @@ extension Store { /// Removes all substates in the store's state. case reset } - + public static func reduce(state: StoreState, sction: Store.Action) -> StoreState { switch sction { case let .add(states): @@ -151,3 +159,6 @@ public typealias MainReducer = ((_ state: StoreState, _ action: ActionType) -> S This reducer is used by the store's dispatch function. It should call the respective reducer basied on the Action type. */ public typealias StoreState = [SubstateType] + +/// A tuple representing the current application state and the last dispatched action. +public typealias StateLastAction = (currentState: [SubstateType], lastAction: ActionType?) diff --git a/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/AddTaskTableViewCell.swift b/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/AddTaskTableViewCell.swift index 1e31039..20b13ae 100644 --- a/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/AddTaskTableViewCell.swift +++ b/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/AddTaskTableViewCell.swift @@ -33,7 +33,9 @@ final class AddTaskTableViewCell: TableViewCell { private func bindViewModelOutputs() { - viewModel.outputs.addTaskButtonActivityIndicatorIsAnimating + let outputs = viewModel.generateOutputs() + + outputs.addTaskButtonActivityIndicatorIsAnimating .drive(addTaskButton.rx.activityIndicatorIsAnimating) .disposed(by: disposeBag) } diff --git a/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/TaskTableViewCell.swift b/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/TaskTableViewCell.swift index 59f5afc..7933ac9 100644 --- a/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/TaskTableViewCell.swift +++ b/RxStateExample/RxStateExample/Flow/iOS/TasksScene/View/TaskTableViewCell.swift @@ -55,7 +55,7 @@ final class TaskTableViewCell: TableViewCell { .drive(toggleTaskStatusButton.rx.isEnabled) .disposed(by: disposeBag) - viewModel.outputs + outputs .toggleTaskStatusButtonActivityIndicatorIsAnimating .drive(toggleTaskStatusButton.rx.activityIndicatorIsAnimating) .disposed(by: disposeBag) diff --git a/RxStateExample/RxStateExample/Flow/macOS/TaskView.swift b/RxStateExample/RxStateExample/Flow/macOS/TaskView.swift index 803da4c..4ea4755 100644 --- a/RxStateExample/RxStateExample/Flow/macOS/TaskView.swift +++ b/RxStateExample/RxStateExample/Flow/macOS/TaskView.swift @@ -34,12 +34,14 @@ class TaskView: View { } private func bindViewModelOutputs(){ - viewModel.outputs + let outputs = viewModel.generateOutputs() + + outputs .summary .drive(SummaryTextField.rx.text) .disposed(by: disposeBag) - viewModel.outputs + outputs .toggleTaskStatusButtonIsSelected .drive(toggleTaskStatusButton.rx.state) .disposed(by: disposeBag) diff --git a/RxStateExample/RxStateExample/Flow/macOS/TaskViewViewModel.swift b/RxStateExample/RxStateExample/Flow/macOS/TaskViewViewModel.swift index 8b803b0..945407f 100644 --- a/RxStateExample/RxStateExample/Flow/macOS/TaskViewViewModel.swift +++ b/RxStateExample/RxStateExample/Flow/macOS/TaskViewViewModel.swift @@ -16,7 +16,7 @@ protocol TaskViewViewModelType: ViewModelType { // Going ☝️ to the store func set(inputs: TaskViewViewModel.Inputs) -> Disposable // Going 👇 from the store - var outputs: TaskViewViewModel.Outputs { get } + func generateOutputs() -> TaskViewViewModel.Outputs } struct TaskViewViewModel: TaskViewViewModelType { @@ -67,7 +67,7 @@ struct TaskViewViewModel: TaskViewViewModelType { let toggleTaskStatusButtonIsSelected: Driver } - var outputs: TaskViewViewModel.Outputs { + func generateOutputs() -> TaskViewViewModel.Outputs { let toggleTaskStatusTransformerInputs = ToggleTaskStatusTransformer.Inputs(store: self.store, taskId: taskId) let toggleTaskStatusTransformerOutputs = ToggleTaskStatusTransformer.transtorm(inputs: toggleTaskStatusTransformerInputs) diff --git a/RxStateExample/RxStateExample/Flow/macOS/TasksViewController.swift b/RxStateExample/RxStateExample/Flow/macOS/TasksViewController.swift index 16726dc..3a0aeaf 100644 --- a/RxStateExample/RxStateExample/Flow/macOS/TasksViewController.swift +++ b/RxStateExample/RxStateExample/Flow/macOS/TasksViewController.swift @@ -44,11 +44,13 @@ class TasksViewController: ViewController { } private func bindViewModelOutputs() { - viewModel.outputs.title + let outputs = viewModel.generateOutputs() + + outputs.title .drive(titleTextField.rx.text) .disposed(by: disposeBag) - viewModel.outputs.reloadTasksTableViewSignal + outputs.reloadTasksTableViewSignal .drive(onNext: { self.tasksTableView.reloadData() }, onCompleted: nil, onDisposed: nil) diff --git a/RxStateExample/RxStateExample/Flow/macOS/TasksViewControllerViewModel.swift b/RxStateExample/RxStateExample/Flow/macOS/TasksViewControllerViewModel.swift index e105bbf..d804e4b 100644 --- a/RxStateExample/RxStateExample/Flow/macOS/TasksViewControllerViewModel.swift +++ b/RxStateExample/RxStateExample/Flow/macOS/TasksViewControllerViewModel.swift @@ -17,7 +17,7 @@ protocol TasksViewControllerViewModelType: ViewModelType, NSTableViewDataSource, // Going ☝️ to the store func set(inputs: TasksViewControllerViewModel.Inputs) -> Disposable // Going 👇 from the store - var outputs: TasksViewControllerViewModel.Outputs { get } + func generateOutputs() -> TasksViewControllerViewModel.Outputs } class TasksViewControllerViewModel: NSObject, TasksViewControllerViewModelType { @@ -64,7 +64,7 @@ class TasksViewControllerViewModel: NSObject, TasksViewControllerViewModelType { let title: Driver } - var outputs: TasksViewControllerViewModel.Outputs { + func generateOutputs() -> TasksViewControllerViewModel.Outputs { let reloadTasksTableViewSignal = tasks .asDriver() .distinctUntilChanged({ (lhsTasks: [Task], rhsTasks: [Task]) -> Bool in diff --git a/RxStateExample/RxStateExample/Middleware/LoggingMiddleware.swift b/RxStateExample/RxStateExample/Middleware/LoggingMiddleware.swift index ea17368..e040986 100644 --- a/RxStateExample/RxStateExample/Middleware/LoggingMiddleware.swift +++ b/RxStateExample/RxStateExample/Middleware/LoggingMiddleware.swift @@ -17,7 +17,7 @@ final class LoggingMiddleware: LoggingMiddlewareType { var disposeBag = DisposeBag() func observe(store: StoreType) { - store.currentStateLastAction + store.stateLastAction .drive( onNext: { (currentState: [SubstateType], lastAction: ActionType?) in print("\n---------------------------------------------") diff --git a/RxStateExample/RxStateExample/StateManagment/ErrorStateManagment.swift b/RxStateExample/RxStateExample/StateManagment/ErrorStateManagment.swift index 4f92aae..2f14d0c 100644 --- a/RxStateExample/RxStateExample/StateManagment/ErrorStateManagment.swift +++ b/RxStateExample/RxStateExample/StateManagment/ErrorStateManagment.swift @@ -59,9 +59,9 @@ extension Store { extension StoreType { /// A convenience variable to extract `Store.TasksState` from the application state - var presentableError: Observable { + var presentableError: Driver { let presentableError = store.state - .flatMap { (states: [SubstateType]) -> Observable in + .flatMap { (states: [SubstateType]) -> Driver in guard let errorStateManagerState = states .first(where: { (state: SubstateType) -> Bool in @@ -73,10 +73,10 @@ extension StoreType { guard let presentableError = errorStateManagerState.presentableError else { - return Observable.never() + return Driver.never() } - return Observable.of(presentableError) + return Driver.of(presentableError) } return presentableError diff --git a/RxStateExample/RxStateExample/StateManagment/FlowStateManagement.swift b/RxStateExample/RxStateExample/StateManagment/FlowStateManagement.swift index dc6b48d..db97571 100644 --- a/RxStateExample/RxStateExample/StateManagment/FlowStateManagement.swift +++ b/RxStateExample/RxStateExample/StateManagment/FlowStateManagement.swift @@ -80,12 +80,12 @@ extension Store { extension StoreType { /// A convenience computed variable to extract `Store.FlowState` from the application state - var flowState: Observable { + var flowState: Driver { let flowState = store.state - .flatMap { (states: [SubstateType]) -> Observable in + .flatMap { (states: [SubstateType]) -> Driver in for state in states { guard let value = state as? Store.FlowState else { continue } - return Observable.just(value) + return Driver.just(value) } fatalError("You need to register `Store.FlowState` first") } @@ -95,8 +95,8 @@ extension StoreType { } /// A convenience computed variable to extract `Store.FlowState.navigatableController` from the application state - var navigatableController: Observable { - let navigatableController: Observable = store.flowState + var navigatableController: Driver { + let navigatableController: Driver = store.flowState .map { (state: Store.FlowState) -> NavigatableController in return state.currentRouteNavigatableController } @@ -104,8 +104,8 @@ extension StoreType { } /// A convenience computed variable to extract `Store.FlowState.originRoute` from the application state - var originRoute: Observable { - let originRoute: Observable = store.flowState + var originRoute: Driver { + let originRoute: Driver = store.flowState .map { (state: Store.FlowState) -> Route? in return state.currentRoute } @@ -113,8 +113,8 @@ extension StoreType { } /// A convenience computed variable to extract `Store.FlowState.originRoute` from the application state - var currentRoute: Observable { - let currentRoute: Observable = store.flowState + var currentRoute: Driver { + let currentRoute: Driver = store.flowState .map { (state: Store.FlowState) -> Route? in return state.currentRoute } diff --git a/RxStateExample/RxStateExample/StateManagment/TasksStateManagement.swift b/RxStateExample/RxStateExample/StateManagment/TasksStateManagement.swift index 49b9ece..a041132 100644 --- a/RxStateExample/RxStateExample/StateManagment/TasksStateManagement.swift +++ b/RxStateExample/RxStateExample/StateManagment/TasksStateManagement.swift @@ -108,15 +108,15 @@ extension Store { // MARK: - Shortcuts extension StoreType { /// A convenience variable to extract a specific `Task` from the application state - func task(withId id: TaskId) -> Observable { + func task(withId id: TaskId) -> Driver { let task = store.tasksState - .flatMap { (state: Store.TasksState) -> Observable in + .flatMap { (state: Store.TasksState) -> Driver in guard let task = state.tasks.first(where: { (task: Task) -> Bool in task.id == id }) else { - return Observable.empty() + return Driver.empty() } - return Observable.of(task) + return Driver.of(task) } .distinctUntilChanged() @@ -124,12 +124,12 @@ extension StoreType { } /// A convenience variable to extract `Store.TasksState` from the application state - var tasksState: Observable { + var tasksState: Driver { let tasksState = store.state - .flatMap { (states: [SubstateType]) -> Observable in + .flatMap { (states: [SubstateType]) -> Driver in for state in states { guard let value = state as? Store.TasksState else { continue } - return Observable.just(value) + return Driver.just(value) } fatalError("You need to register `Store.TasksState` first") } diff --git a/RxStateTests/Info.plist b/RxStateTests/Info.plist index a2eeaf7..645ee0c 100644 --- a/RxStateTests/Info.plist +++ b/RxStateTests/Info.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.3.0 + 0.5.0 CFBundleVersion - 3 + 4