{"id":13719610,"url":"https://github.com/RxSwiftCommunity/RxState","last_synced_at":"2025-05-07T11:32:20.968Z","repository":{"id":21368015,"uuid":"91064730","full_name":"RxSwiftCommunity/RxState","owner":"RxSwiftCommunity","description":"Redux implementation in Swift using RxSwift","archived":false,"fork":false,"pushed_at":"2023-04-12T01:24:43.000Z","size":540,"stargazers_count":155,"open_issues_count":3,"forks_count":12,"subscribers_count":8,"default_branch":"development","last_synced_at":"2025-04-29T06:32:18.580Z","etag":null,"topics":["redux","rxswift","state-container","state-management","swift","unidirectional-data-flow"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RxSwiftCommunity.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-05-12T07:37:59.000Z","updated_at":"2025-02-21T11:42:28.000Z","dependencies_parsed_at":"2024-01-29T22:08:30.938Z","dependency_job_id":"80304b85-be31-466a-8f41-53e0a79a7765","html_url":"https://github.com/RxSwiftCommunity/RxState","commit_stats":{"total_commits":30,"total_committers":5,"mean_commits":6.0,"dds":"0.33333333333333337","last_synced_commit":"366abc66ad7deeaeb60c9ec4bd7bb8841e15e9da"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxState","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxState/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxState/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RxSwiftCommunity%2FRxState/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RxSwiftCommunity","download_url":"https://codeload.github.com/RxSwiftCommunity/RxState/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252869113,"owners_count":21816979,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["redux","rxswift","state-container","state-management","swift","unidirectional-data-flow"],"created_at":"2024-08-03T01:00:52.749Z","updated_at":"2025-05-07T11:32:20.374Z","avatar_url":"https://github.com/RxSwiftCommunity.png","language":"Swift","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"\u003cimg src=\"assets/RxState_Logo.png\" alt=\"RxState\" width=\"50\" height=\"40\"\u003e RxState: Redux + RxSwift\n======================================\n\nRxState a predictable state container for Swift apps. It's a tiny library built on top of [RxSwift](https://github.com/ReactiveX/RxSwift) and inspired by [Redux](http://redux.js.org/) that facilitates building [Unidirectional Data Flow](http://redux.js.org/docs/basics/DataFlow.html) architecture.\n\n## Why Unidirectional Data Flow Architecture?\n\n1. Helps you manage state in a consistent and unified way that guaranty it’s always predictable (After all, state is the source of all evil and you wanna keep that evil in check).\n2. Limits the way app state can be mutated, which makes your app easier to understand.\n3. Makes your code easy to test.\n4. Enables faster debugging.\n5. It’s is entirely platform independent - you can easily use the same business logic and share it between apps for multiple platforms (iOS, tvOS, etc.).\n\n## Architecture Components\n\n- **App State**: A single **immutable** data structure. It includes the UI state, the navigation state and the state of your model layer. \n\n- **Store**:Contains the app state and notifies the `App State Observers` of the `App State` updates.\n\n- **Reducer**: A [pure](http://en.wikipedia.org/wiki/Pure_function) function that takes the current app state and an `Action` as input, creates a **new** `App State` that reflects the changes described by the `Action`, and returns the **new** `App State`.\n\n- **Action**: Actions describe a state change. The only way to modified the `App State` is by dispatching `Actions` to the `Store`.\n\n- **Action Creators and Dispatchers**: Creates `Action`s and dispatch them to the store.\n\n- **App State Observers**: Observers the `App State` in the `Store` to transform it to presentable data, write logs, etc.\n\n- **View**: Presents the presentable data that was deriver from the `App State` and delivers the user's interactions to the `Action Creators and Dispatchers`.\n\n## How it works?\n\n\u003cimg src=\"assets/RxState-Pattern.jpeg\" width=\"100%\" height=\"100%\"\u003e\n\n1. The `View/View Controller` sends events (The `View Model`'s inputs) to the `View Model`.\n\n2. The `View Model` creates an `Action` from the received inputs and dispatch them to the `Store`.\n\n- The  `View Model` can use a dedicated `Action Creator`s to create `Action`s.\n`Action Creator`s do can async work and, based on the results it gets, returns different `Action`s to the `View Model` to dispatch.\n\n3. The `Store` sends the `App State` and the received `Action` to the `Reducer`.\n\n4. The `Reducer` receives the current `App State` and the dispatched `Action`, computes and returns **new** `App State`.\n\n5. The `Store` sends the **new** `App State` to the subscribers.\n\n- One of the subscribers could be a `Middleware` that logs the `App State` resulted from dispatching an `Action`.\n\n6. The `View Model` receives the **new** `App State`, transform it presentable data, and send it to the `View/View Controller`.\n\n- The  `View Model` can use `Transformer`s to transform the `App State` to presentable data. This helps you reuse the transformation code in different `View Model`s.\n\n7. The `View/View Controller` render the UI to show the presentable data to the user.\n\n## How does RxState helps you build the Architecture?\n\nRxState defines the main component for you:\n\n1. `Store`: Contains the `App State` in the form of `Driver\u003c[SubstateType]\u003e`.\n\n2. `SubstateType`: A protocol that tags structs representing a substate.\nEx.\n\n```swift\nstruct TasksState: SubstateType {   \n    var tasks: [Task]\n    var addingTask: Bool\n}\n```\n\nYou can add a `Substate`s to the `App State` by dispatching `StoreAction.add(states: [SubstateType])`.\n\n```swift\nlet tasksState = TasksState()\nlet action = StoreAction.add(states: [tasksState])\nstore.dispatch(action: action)\n```\n\n3. `ActionType`: A protocol that tags an `Action`. The `Store` has the following `Action`s:\n\n```swift\npublic enum StoreAction: ActionType {\n    /// Adds substates to the application state.\n    case add(states: [SubstateType])\n\n    /// Removes all substates in the application state.\n    case reset\n}\n```\n\n4. `MainReducer`: A reducer used by the `Store`'s dispatch function to call the respective reducer based on the Action type.\n\n```swift\nlet mainReducer: MainReducer = { (state: [SubstateType], action: ActionType) -\u003e [SubstateType] in\n    // Copy the `App State`\n    var state: [SubstateType] = state\n    \n    // Cast to a specific `Action`.\n    switch action {\n    case let action as TasksAction:\n\n        // Extract the `Substate`.\n        guard var (tasksStateIndex, tasksState) = state\n            .enumerated()\n            .first(where: { (_, substate: SubstateType) -\u003e Bool in\n                return substate is Store.TasksState}\n            ) as? (Int, Store.TasksState)\n            else {\n                fatalError(\"You need to register `TasksState` first\")\n        }\n\n        // Reduce the `Substate` to get a new `Substate`.\n        tasksState = Store.reduce(state: tasksState, action: action)\n    \n        // Replace the `Substate` in the `App State` with the new `Substate`.\n        state[tasksStateIndex] = tasksState as SubstateType\n    \n    default:\n        fatalError(\"Unknown action type\")\n    }\n    \n    // Return the new `App State`\n    return state\n}\n```\n\n4. `MiddlewareType`: A protocol defining an object that can observe the `App State` and the last dispatched `Action` and does something with it like logging:\n\n```swift\nprotocol LoggingMiddlewareType: Middleware, HasDisposeBag {}\n\nfinal class LoggingMiddleware: LoggingMiddlewareType {\n    var disposeBag = DisposeBag()\n\n    func observe(currentStateLastAction: Driver\u003cCurrentStateLastAction\u003e) {\n        currentStateLastAction\n            .drive(\n                onNext: { (currentState: [SubstateType], lastAction: ActionType?) in\n                    print(currentState)\n                    print(lastAction)\n                }, onCompleted: nil, onDisposed: nil)\n            .disposed(by: disposeBag)\n        }\n    }\n}\n```\n\n## Dependencies\n\n- [RxSwift](https://github.com/ReactiveX/RxSwift) (\u003e= 5.1)\n- [RxCocoa](https://github.com/ReactiveX/RxSwift) (\u003e= 5.1)\n\n## Requirements\n\n- Swift 5\n\n## Installation\n\n- **Using [CocoaPods](https://cocoapods.org)**:\n\n```ruby\npod 'RxState'\n```\n- **Using [Swift Package Manager](https://developer.apple.com/documentation/swift_packages)**:\n\nCreate a ```Package.Swift``` file in your project's root folder.\n\nAdd following content into the ```Package.swift``` file\n\n```Swift\n// swift-tools-version:5.0\n\nimport PackageDescription\n\nlet package = Package(\n  name: \"YourProjectName\",\n  dependencies: [\n    .package(url: \"https://github.com/RxSwiftCommunity/RxState.git\", from: \"0.6.0\")\n  ],\n  targets: [\n    .target(name: \"YourProjectTarget\", dependencies: [\"RxState\"])\n  ]\n)\n```\n\n## Demo\n\nI have tried to make the [demo app](https://github.com/nazeehshoura/RxState/tree/development/RxStateExample) as comprehensive as possible. It currently runs on iOS and macOS.\nNotice how, because of the architecture, only the View/ View Controller layer needed to change in order to port the app from iOS to macOS.\n\n## Contributing\n\nWe would love to see you involved! Feedback and contribution are greatly appreciated :)  Checkout the [Contributing Guide](https://github.com/RxSwiftCommunity/RxState/blob/development/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/RxSwiftCommunity/RxState/blob/development/CODE_OF_CONDUCT.md).\n\n## Influences and credits\n\n* [RxSwift](https://github.com/ReactiveX/RxSwift): Reactive Programming in Swift.\n* [Redux](http://redux.js.org/): a predictable state container for JavaScript apps.\n\n## Author\n\nNazih Shoura, shoura.nazeeh@gmail.com\n\n## License\n\nThis library belongs to _RxSwiftCommunity_.\n\nRxState is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRxSwiftCommunity%2FRxState","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRxSwiftCommunity%2FRxState","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRxSwiftCommunity%2FRxState/lists"}