{"id":24527168,"url":"https://github.com/kazaimazai/puredux","last_synced_at":"2025-04-14T15:24:26.388Z","repository":{"id":149652749,"uuid":"307321669","full_name":"KazaiMazai/Puredux","owner":"KazaiMazai","description":"Puredux is a UDF architecture framework in Swift.","archived":false,"fork":false,"pushed_at":"2024-09-22T10:30:29.000Z","size":729,"stargazers_count":25,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T20:50:14.856Z","etag":null,"topics":["composable","mvi","mvi-architecture","mvi-clean-architecture","swift","swiftui","udf","udf-libraries","udf-library","uikit"],"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/KazaiMazai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-26T09:31:07.000Z","updated_at":"2025-01-28T17:22:43.000Z","dependencies_parsed_at":"2024-01-28T19:27:40.656Z","dependency_job_id":"c841bcc9-900a-47c8-8444-2c0c773d1af2","html_url":"https://github.com/KazaiMazai/Puredux","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazaiMazai%2FPuredux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazaiMazai%2FPuredux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazaiMazai%2FPuredux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KazaiMazai%2FPuredux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KazaiMazai","download_url":"https://codeload.github.com/KazaiMazai/Puredux/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248905020,"owners_count":21180906,"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":["composable","mvi","mvi-architecture","mvi-clean-architecture","swift","swiftui","udf","udf-libraries","udf-library","uikit"],"created_at":"2025-01-22T06:17:01.492Z","updated_at":"2025-04-14T15:24:26.350Z","avatar_url":"https://github.com/KazaiMazai.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/KazaiMazai/Puredux/blob/main/Sources/Puredux/Documentation.docc/Resources/Logo-dark.svg\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://github.com/KazaiMazai/Puredux/blob/main/Sources/Puredux/Documentation.docc/Resources/Logo.svg\"\u003e\n  \u003cimg src=\"https://github.com/KazaiMazai/Puredux/blob/main/Sources/Puredux/Documentation.docc/Resources/Logo.svg\"\u003e\n\u003c/picture\u003e\n\n\n[![CI](https://github.com/KazaiMazai/Puredux/workflows/Tests/badge.svg)](https://github.com/KazaiMazai/Puredux/actions?query=workflow%3ATests)\n\n\nPuredux is an architecture framework for SwiftUI and UIKit designed to\nstreamline state management with a focus on unidirectional data flow and separation of concerns.\n\n- **Unidirectional MVI Architecture**: Supports a unidirectional data flow to ensure predictable state management and consistency.\n- **SwiftUI and UIKit Compatibility**: Works seamlessly with both SwiftUI and UIKit, offering bindings for easy integration.\n- **Single Store/Multiple Stores Design**: Supports both single and multiple store setups for flexible state management of apps of any size and complexity.\n- **Separation of Concerns**: Emphasizes separating business logic and side effects from UI components.\n- **Performance Optimization**: Offers granular performance tuning with state deduplication, debouncing, and offloading heavy UI-related work to the background.\n\n## Why\n\nDevelop an application using Puredux, incorporating pure state, actions, and reducers, with a clean stateless UI, while managing side effects separately.\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n  * [Basics](#basics)\n  * [Store Definitions](#store-definitions)\n  * [UI Bindings](#ui-bindings)\n- [Hierarchical Stores Tree Architecture](#hierarchical-stores-tree-architecture)\n- [Side Effects](#side-effects)\n  * [Async Actions](#async-actions)\n  * [State-Driven Side Effects](#state-driven-side-effects)\n- [Dependency Injection](#dependency-injection)\n  * [Store Injection](#store-injection)\n  * [Dependency Injection](#dependency-injection-1)\n- [Performance](#performance)\n  * [Reducers Execution](#reducers-execution)\n  * [QoS Tuning](#qos-tuning)\n  * [Deduplicated State Updates](#deduplicated-state-updates)\n  * [UI Updates Debouncing](#ui-updates-debouncing)\n  * [Two-step UI Updates with Background Offloading](#two-step-ui-updates-with-background-offloading)\n  * [Granular UI Updates](#granular-ui-updates)\n- [Installation](#installation)\n  * [Swift Package Manager.](#swift-package-manager)\n- [Documentation](#documentation)\n- [Licensing](#licensing)\n\n## Getting Started\n\n### Basics\n\nAt its core, Puredux follows a predictable state management pattern that consists of the following key components:\n\n- State: A type that represents the entire application state or a portion of it.\n- Actions: Events that describe possible changes in the system, which lead to state mutations.\n- Reducer: A function that dictates how state changes in response to specific actions.\n- Store: The central hub where:\n    - Initial state and reducers are defined.\n    - Actions are dispatched to trigger state changes.\n    - New state values are propagated to any observers or views.\n  \n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\n\u003cp\u003e   \n \n```text\n \n                +-----------------------------------------+\n                |                  Store                  |\n                |                                         |\n                |  +-----------+   +-------------------+  |\n                |  |  Reducer  |\u003c--|   Current State   |  |\n      New State |  +-----------+   +-------------------+  |  Actions\n    \u003c-----------+      |                            A     |\u003c----------+\n    |           |      |                            |     |           A\n    |           |      V                            |     |           |\n    |           |  +-------------------+            |     |           |\n    |           |  |     New State     |------------+     |           |\n    |           |  +-------------------+                  |           |\n    |           |                                         |           |\n    |           +-----------------------------------------+           |\n    |                                                                 |\n    |                                                                 |\n    |                                                                 |\n    |           +----------------+                +---+----+          |\n    V Observer  |                |   Async Work   |        |          |\n    +----------\u003e|  Side Effects  |---------------\u003e| Action |---------\u003e|\n    |           |                |     Result     |        |          |\n    |           +----------------+                +----+---+          |\n    |                                                                 |\n    |           +----------------+                   +---+----+       |\n    V Observer  |                |       User        |        |       |\n    +----------\u003e|       UI       |------------------\u003e| Action |------\u003e+\n                |                |   Interactions    |        |\n                +----------------+                   +----+---+\n        \n```\n\u003c/p\u003e\n\u003c/details\u003e    \n    \n    \n### Store Definitions\n\n```swift\n// Let's cover actions with a protocol\n\nprotocol Action { }\n\nstruct IncrementCounter: Action {\n    // ...\n}\n\n// Define root AppState\n\nstruct AppState {\n    // ...\n    \n    mutating func reduce(_ action: Action) {\n        switch action {\n        case let action as IncrementCounter:\n            // ...\n        default:\n            break\n        }\n    }\n}\n\n// Inject root store\n\nextension SharedStores {\n    @StoreEntry var root = StateStore\u003cAppState, Action\u003e(AppState()) { state, action in\n        state.reduce(action)\n    }\n}\n```\n\n### UI Bindings\n\nPuredux can integrate with SwiftUI to manage both global app state and local states. \n\n```swift\n\n// View might need a local state that we don't need to share with the whole app \n\nstruct ViewState {\n    // ...\n    \n    mutating func reduce(_ action: Action) {\n        // ...\n    }\n}\n\n// In your SwiftUI View, you can combine the app's root store with local view-specific states.\n// This allows the view to respond dynamically to both app-level and view-level state changes.\n\nstruct ContentView: View  {\n    // We can take an injected root store,\n    // create a local state store and merge them together.\n    @State var store: StoreOf(\\.root).with(ViewState()) { state, action in \n        state.reduce(action) \n    }\n    \n    @State var viewState: (AppState, ViewState)?\n    \n    var body: some View {\n        MyView(viewState)\n            .subscribe(store) { viewState = $0 }\n            .onAppear {\n                dispatch(SomeAction())\n            }\n    }\n}\n\n```\n\u003cdetails\u003e\u003csummary\u003eClick to expand UIViewController example\u003c/summary\u003e\n\u003cp\u003e\n \n```swift\n\n// We can do the same thing almost with the same API for UIKit view controller:\n\nstruct MyScreenState {\n    // ...\n    \n    mutating func reduce(_ action: Action) {\n        // ...\n    }\n}\n\nfinal class MyViewController: ViewController {\n    var store: StoreOf(\\.root).with(MyScreenState()) { state, action in \n        state.reduce(action) \n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n         \n        subscribe(store) { [weak self] in\n            // Handle updates with the derived props\n            self?.updateUI(with: $0)\n        }\n     }\n     \n     private func updateUI(with state: (AppState, MyScreenState)) {\n         // Update UI elements with the new view state\n     }\n}\n\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Hierarchical Stores Tree Architecture\n\nPuredux allows you to build a hierarchical store tree, facilitating the development of applications where state can be either shared or isolated, while keeping state mutations predictable.\n\nActions propagate upstream from child stores to the root store, while state updates flow downstream from the root store to child stores, and ultimately to store observers.\n\nThe store tree hierarchy ensures that business logic is fully decoupled from the UI layer. This allows for a deep, isolated business logic tree, while maintaining a shallow UI layer focused solely on its responsibilities.\n\nMake your app driven by business logic, not by the view hierarchy.\n\nPuredux provides flexibility in structuring your app with the following options:\n\n- Single Store\n- Multiple Independent Stores\n- Multiple Store Tree\n\nThe multiple store tree is a flexible design that handles complex applications by organizing stores in a hierarchical structure. This approach offers several benefits:\n\n- **Feature scope isolation**: Each feature or module has its own dedicated store, preventing cross-contamination of state.\n- **Performance optimization**: Stores can be made pluggable and isolated, reducing the overhead of managing a large, monolithic state and improving performance.\n- **Scalable application growth**: You can start with a simple structure and add more stores as your application grows, making this approach sustainable for long-term development.\n\n\n```swift\n// Root Store Config\n\nlet root = StateStore\u003cAppState, Action\u003e(AppState()) { state, action in \n    state.reduce(action) \n} \n.effect(\\.effectState) { state, dispatch in\n    Effect {\n        // ...\n    }\n}\n\n// Feature One Config\n\nlet featureOne = root.with(FeatureOne()) { appState, action in \n    state.reduce(action) \n}\n.effect(\\.effectState) { state, dispatch in\n    Effect {\n        // ...\n    }\n}\n\n// Feature Two Config with 2 screen stores\n\nlet featureTwo = root.with(FeatureTwo()) { state, action in\n    state.reduce(action)\n}\n.effect(\\.effectState) { state, dispatch in\n    Effect {\n        // ...\n    }\n}\n\nlet screenOne = featureTwo.with(ScreenOne()) { state, action in \n    state.reduce(action) \n}\n.effect(\\.effectState) { state, dispatch in\n    Effect {\n        // ...\n    }\n}\n\nlet screenTwo = featureTwo.with(ScreenTwo()) { state, action in \n    state.reduce(action) \n}\n\n```\n\nWe can connect UI to any of the stores and will end up with the following hierarchy:\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand\u003c/summary\u003e\n\u003cp\u003e\n\n \n```text\n              +----------------+    +--------------+\n              | AppState Store | -- | Side Effects |\n              +----------------+    +--------------+\n                    |\n                    |\n       +------------+-------------------------+\n       |                                      |\n       |                                      |\n       |                                      |\n       |                                      |\n +------------------+                     +------------------+    +--------------+\n | FeatureOne Store |                     | FeatureTwo Store | -- | Side Effects |\n +------------------+                     +------------------+    +--------------+\n    |         |                               |\n +----+  +--------------+                     |\n | UI |  | Side Effects |        +------------+------------+\n +----+  +--------------+        |                         |\n                                 |                         |\n                                 |                         |\n                                 |                         |\n                          +-----------------+         +-----------------+\n                          | ScreenOne Store |         | ScreenTwo Store |\n                          +-----------------+         +-----------------+\n                             |         |                 |\n                          +----+  +--------------+    +----+\n                          | UI |  | Side Effects |    | UI |\n                          +----+  +--------------+    +----+\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Side Effects\n\nIn the context of UDF architecture, \"side effects\" refer to asynchronous operations that interact with external systems or services. These can include:\n\n- Network Requests: Fetching data from web services or APIs.\n- Database Fetches: Retrieving or updating information in a database.\n- I/O Operations: Reading from or writing to files, streams, or other I/O devices.\n- Timer Events: Handling delays, timeouts, or scheduled tasks.\n- Location Service Callbacks: Responding to changes in location data or retrieving location-specific information.\n\nIn the Puredux framework, managing these side effects is achieved through two main mechanisms:\n- Async Actions\n- State-Driven Side Effects\n\n### Async Actions\n\nAsync Actions are designed for straightforward, fire-and-forget scenarios. They allow you to initiate asynchronous operations and integrate them seamlessly into your application logic. These actions handle tasks such as network requests or database operations, enabling your application to respond to the outcomes of these operations.\nDefine actions that perform asynchronous work and then use them within your app.\n\n\n```swift\n\n// Define result and error actions:\n\nstruct FetchDataResult: Action {\n    // ...\n}\n\nstruct FetchDataError: Action {\n    // ...\n}\n\n// Define async action:\n\nstruct FetchDataAction: AsyncAction {\n\n    func execute(completeHandler: @escaping (Action) -\u003e Void) {  \n        APIClient.shared.fetchData {\n            switch $0 {\n            case .success(let result):\n                completeHandler(FetchDataResult(result))\n            case .success(let error):\n                completeHandler(FetchDataError(error))\n            }\n        }\n    }\n}\n\n// When async action is dispatched to the store, it will be executed:\n\nstore.dispatch(FetchDataAction())\n\n```\n\n### State-Driven Side Effects\n\nState-driven Side Effects offer more advanced capabilities for handling asynchronous operations. This mechanism is particularly useful when you need precise control over execution, including retry logic, cancellation, and synchronization with the UI or other parts of the application. Despite its advanced features, it is also suitable for simpler use cases due to its minimal boilerplate code.\n \n  \n ```swift\n// Add effect state to the state\nstruct AppState {\n    private(set) var theJob: Effect.State = .idle()\n}\n \n// Add related actions**\n \nenum Action {\n    case jobSuccess(Something)\n    case startJob\n    case cancelJob\n    case jobFailure(Error)\n}\n \n// Handle actions in the reducer\n  \nextension AppState {\n    mutating func reduce(_ action: Action) {\n        switch action {\n        case .jobSuccess:\n            theJob.succeed()\n        case .startJob:\n            theJob.run()\n        case .cancelJob:\n            theJob.cancel()\n        case .jobFailure(let error):\n            theJob.retryOrFailWith(error)\n        }\n    }\n}\n \n// Add SideEffect to the store:\n  \nlet store = StateStore\u003cAppState, Action\u003e(AppState()) { state, action in \n    state.reduce(action) \n}\n.effect(\\.theJob, on: .main) { appState, dispatch in\n    Effect {\n        do {\n            let result = try await apiService.fetch()\n            dispatch(.jobSuccess(result))\n        } catch {\n            dispatch(.jobFailure(error))\n        }\n    }\n}\n\n// Dispatch action to run the job:\n\nstore.dispatch(.startJob)\n\n```\n\nA powerful feature of State-driven Side Effects is that their scope and lifetime are defined by the store they are connected to. This makes them especially beneficial in complex store hierarchies, such as app-level stores, feature-scoped stores, and per-screen stores, as their side effects automatically align with the lifecycle of each store.\n\n## Dependency Injection\n\nPuredux splits dependencies into two categories:\n\n- Store Injection\n- Dependency Injection\n\nAlthough both are essentially dependencies, they are handled separately because they serve different purposes, and Puredux ensures they remain distinct.\n\n- **Store Injection** is used to conveniently obtain store instances in the UI layer of the application.\n- **Dependency Injection** is used inside the store's reducers to power the application's core logic.\n\n### Store Injection\n\nUse `@StoreEntry` in the `SharedStores` extension to inject the store instance:\n\n```swift\nextension SharedStores {\n   @StoreEntry var root = StateStore\u003cAppRootState, Action\u003e(....)  \n}\n \n\n// The `@StoreOf` property wrapper can be used to obtain the injected store instance:\n \nstruct MyView: View {\n    @State @StoreOf(\\.root)\n    var store: StateStore\u003cAppRootState, Action\u003e\n    \n    var body: some View {\n       // ...\n    }\n}\n```\n\n### Dependency Injection\n\nUse `@DependencyEntry` in the `Dependencies` extension to inject the dependency instance:\n\n```swift\nextension Dependencies {\n   @DependencyEntry var now = { Date() }  \n}\n \n\n// Then it can be used in the app reducer:\n \nstruct AppState {\n    private var currentTime: Date?\n\n    mutating func reduce(_ action: Action) {\n        switch action {\n        case let action as UpdateTime:\n            let now = Dependency[\\.now]\n            currentTime = now()\n        default:\n            break\n        }\n    }\n}\n\n```\n\n## Performance\n\nPuredux offers a robust strategy for addressing the performance challenges commonly faced in iOS applications. It provides several key optimizations to enhance app responsiveness and efficiency, including:\n\n- **Reducers background execution**: Offloads reducer logic to background threads to improve overall app performance.\n- **State updates deduplication**: Minimizes redundant state updates, reducing unnecessary re-renders and improving processing efficiency.\n- **Granular UI updates**: Ensures only the necessary parts of the UI are updated, enhancing responsiveness.\n- **UI updates debouncing**: Prevents excessive UI updates by intelligently controlling the frequency of updates.\n- **Two-step UI updates with background task offloading**: Heavy computational tasks are handled in the background, with UI updates executed in a structured two-step process to ensure smooth, lag-free interactions.\n\nAs your application and its features expand, you may encounter performance issues, such as reducers taking longer to execute or SwiftUI view\nbodies refreshing more frequently than anticipated. This article highlights several common challenges when building features in Puredux and provides solutions to address them.\n\n### Reducers Execution\n\nPuredux is designed in a way that allows you to implement state and reducers without any dependencies.\n\nThis is done intentionally to be able to offload all stores reducers' work to the background without worrying much about data races, access\nsynchronization with dependencies.\n\nAs the result reducers operate in background leaving the main thread exclusively for the UI.\n\n### QoS Tuning\n\nWhen creating the root store, you can choose the quality of service for the queue it will operate on.\n\n```swift\n let store = StateStore(\n     InitialState(),\n     qos: .userInitiated,\n     reducer: myReducer\n )\n```\n\n### Deduplicated State Updates\n\nPuredux supports state change deduplication to enable granular UI updates based on changes to specific parts of the state.\n\n```swift\nstruct MyView: View {\n    let store: Store\u003cViewState, Action\u003e\n    \n    @State var viewState: ViewState?\n\n    var body: some View {\n        ContentView(viewState)\n            .subscribe(\n                store: store,\n                removeStateDuplicates: .keyPath(\\.lastModified),\n                observe: {\n                    viewState = $0\n                }\n            )\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand UIViewController example\u003c/summary\u003e\n\u003cp\u003e\n\n ```swift\n final class MyViewController: ViewController  {\n    var store: StoreOf(\\.root).with(MyScreenState()) { state, action in \n        state.reduce(action) \n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n         \n        subscribe(\n            store,\n            removeStateDuplicates: .keyPath(\\.lastModified)) { [weak self] in\n            // Handle updates with the derived props\n            self?.updateUI(with: $0)\n        }\n     }\n     \n     private func updateUI(with state: (AppState, MyScreenState)) {\n         // Update UI elements with the new view state\n     }\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### UI Updates Debouncing\n\nThere are situations where frequent state updates are unavoidable, but triggering the UI for each update can be too resource-intensive. \nTo handle this, Puredux provides a debouncing option. \nWith debouncing, the UI update will only be triggered after a specified time interval has elapsed between state changes.\n\n```swift\nstruct MyView: View {\n    let store: Store\u003cViewState, Action\u003e\n    \n    @State var viewState: ViewState?\n\n    var body: some View {\n        ContentView(viewState)\n            .subscribe(\n                store: store,\n                debounceFor: 0.016, \n                observe: {\n                    viewState = $0\n                }\n            )\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand UIViewController example\u003c/summary\u003e\n\u003cp\u003e\n\n ```swift\n final class MyViewController: ViewController  {\n    var store: StoreOf(\\.root).with(MyScreenState()) { state, action in \n        state.reduce(action) \n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n         \n        subscribe(store, debounceFor: 0.016) { [weak self] in\n            self?.updateUI(with: $0)\n        }\n     }\n     \n     private func updateUI(with state: (AppState, MyScreenState)) {\n         // Update UI elements with the new view state\n     }\n}\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n### Two-step UI Updates with Background Offloading\n\nA very common use case involves converting raw model data into a more presentable format, \nwhich may include mapping `AttributedStrings`, using `DateFormatter`, and more. These operations can be resource-intensive.\n\nPuredux allows you to add a presentational layer in the state change processing pipeline between the store and the UI. This enables you to offload these computations to a background queue, keeping the UI responsive.\n\n```swift\nstruct MyView: View {\n    @State var store: StoreOf(\\.root).with(ViewState()) { state, action in \n        state.reduce(action) \n    }\n    \n    @State var viewModel: ViewModel?\n     \n    var body: some View {\n        ContentView(viewModel)\n            .subscribe(\n                store,\n                props: { state, dispatch in\n                    // ViewModel will be evaluated on the background presentation queue\n                    ViewModel(state, dispatch)\n                },\n                presentationQueue: .sharedPresentationQueue,\n                observe: {\n                    viewModel = $0\n                }\n            )\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand UIViewController example\u003c/summary\u003e\n\u003cp\u003e\n\n ```swift\n final class MyViewController: ViewController  {\n    var store: StoreOf(\\.root).with(MyScreenState()) { state, action in \n        state.reduce(action) \n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n         \n        subscribe(\n            store, \n            props: { state, dispatch in\n                // ViewModel will be evaluated on the background presentation queue\n                ViewModel(state, dispatch)\n            },\n            presentationQueue: .sharedPresentationQueue) { [weak self] in\n            \n            self?.updateUI(with: $0)\n        }\n     }\n     \n     private func updateUI(with viewModel: ViewModel) {\n         // Update UI elements with the new view state\n     }\n}\n```\n\u003c/p\u003e\n\u003c/details\u003e\n\n### Granular UI Updates\n\nPuredux provides a full control over UI updates. Any SwiftUI `View`, `UIViewController` or a `UIView` or can be individually subscribed to the `Store` with state deduplication, debouncing and 2-step UI updates with background offloading.\n\n## Installation\n\n### Swift Package Manager.\n\nPuredux is available through Swift Package Manager. \n\nTo install it, in Xcode 11.0 or later select File \u003e Swift Packages \u003e Add Package Dependency... and add Puredux repositoies URLs for the modules requried:\n\n```\nhttps://github.com/KazaiMazai/Puredux\n```\n\n## Documentation\n\n- [Documentation](https://swiftpackageindex.com/KazaiMazai/Puredux/main/documentation/puredux)\n- [Documentation v1.9.x](https://swiftpackageindex.com/KazaiMazai/Puredux/1.9.2/documentation/puredux)\n- [Documentation Archive](https://swiftpackageindex.com/kazaimazai/puredux/main/documentation/puredux/archive)\n- [Migration Guides](https://swiftpackageindex.com/kazaimazai/puredux/main/documentation/puredux/migrationguides)\n\n## Licensing\n\nPuredux and all its modules are licensed under MIT license.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazaimazai%2Fpuredux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkazaimazai%2Fpuredux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazaimazai%2Fpuredux/lists"}