{"id":17101295,"url":"https://github.com/keitaoouchi/fluxxkit","last_synced_at":"2026-03-08T08:33:34.487Z","repository":{"id":56911525,"uuid":"90921810","full_name":"keitaoouchi/FluxxKit","owner":"keitaoouchi","description":"Unidirectional data flow for reactive programming in iOS.","archived":false,"fork":false,"pushed_at":"2019-03-31T13:24:01.000Z","size":42,"stargazers_count":42,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-13T00:02:06.858Z","etag":null,"topics":["flux","ios","reactive-programming","rxswift","swift","unidirectional-data-flow"],"latest_commit_sha":null,"homepage":null,"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/keitaoouchi.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}},"created_at":"2017-05-11T01:00:03.000Z","updated_at":"2024-06-25T01:41:37.000Z","dependencies_parsed_at":"2022-08-20T20:20:41.507Z","dependency_job_id":null,"html_url":"https://github.com/keitaoouchi/FluxxKit","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keitaoouchi%2FFluxxKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keitaoouchi%2FFluxxKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keitaoouchi%2FFluxxKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keitaoouchi%2FFluxxKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/keitaoouchi","download_url":"https://codeload.github.com/keitaoouchi/FluxxKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647224,"owners_count":21139084,"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":["flux","ios","reactive-programming","rxswift","swift","unidirectional-data-flow"],"created_at":"2024-10-14T15:24:40.369Z","updated_at":"2026-03-08T08:33:34.467Z","avatar_url":"https://github.com/keitaoouchi.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FluxxKit\n\nLightweight Flux-style state management for SwiftUI, built on `@Observable` and Swift Concurrency.\n\n- Zero external dependencies\n- Swift 6 strict concurrency safe\n- Reducer as a composable function value\n\n## Concepts\n\n### Swift is already powerful enough\n\n`enum`, `struct`, `@Observable`, `async/await` — Swift already has everything needed for state management. FluxxKit does not replace these primitives. It simply provides a methodology to organize them.\n\n### Algebraic Data Types as the source of truth\n\nSwift's `enum` functions as an algebraic data type (sum type). By defining Actions as ADTs, the complete set of operations a component can perform is fixed at compile time. The `switch` exhaustiveness check turns unhandled Actions into compiler errors, and when a new Action is added, every Reducer that fails to handle it is flagged immediately.\n\n### Reducer as a pure function\n\nAll state transition logic is consolidated into a pure function: `(State, Action) -\u003e (State, Effect)`. No \"spooky action at a distance\" — state only changes in one place. Testing is just calling a function. Code review is self-contained: reading the Reducer tells you everything that can happen on a given screen.\n\n### Effects as first-class values\n\nSide effects (API calls, timers, global actions) are expressed as return values from the Reducer. Reducer purity is preserved, and the presence of side effects is traceable during code review.\n\n## Requirements\n\n| Target | Version |\n|---|---|\n| iOS | 17.0+ |\n| macOS | 14.0+ |\n| Swift | 6.0+ |\n\n## Installation\n\n### Swift Package Manager\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/nicoryo/FluxxKit.git\", from: \"2.0.0\")\n]\n```\n\n## Quick Start\n\n### 1. Define State and Action\n\n```swift\nimport FluxxKit\n\nstruct CounterState: StateType {\n    var count: Int = 0\n}\n\nenum CounterAction: ActionType {\n    case increment\n    case decrement\n}\n```\n\n### 2. Define a Reducer\n\nReducers are pure functions — no protocol conformance needed.\n\n```swift\nlet counterReducer = Reducer\u003cCounterState, CounterAction\u003e { state, action in\n    switch action {\n    case .increment:\n        return (CounterState(count: state.count + 1), .none)\n    case .decrement:\n        return (CounterState(count: state.count - 1), .none)\n    }\n}\n```\n\n### 3. Use in SwiftUI\n\n```swift\nstruct CounterView: View {\n    @State private var store = Store(\n        initialState: CounterState(),\n        reducer: counterReducer\n    )\n\n    var body: some View {\n        VStack {\n            Text(\"\\(store.state.count)\")\n            Button(\"+\") { store.dispatch(.increment) }\n            Button(\"-\") { store.dispatch(.decrement) }\n        }\n    }\n}\n```\n\n## Side Effects\n\nUse `Effect.run` for async operations. The dispatch function is injected so actions flow back through the store.\n\n```swift\nenum SearchAction: ActionType {\n    case search(query: String)\n    case loaded([Result])\n}\n\nlet searchReducer = Reducer\u003cSearchState, SearchAction\u003e { state, action in\n    switch action {\n    case .search(let query):\n        let effect = Effect\u003cSearchAction\u003e.run { dispatch in\n            let results = await API.search(query)\n            await dispatch(.loaded(results))\n        }\n        return (state, effect)\n    case .loaded(let results):\n        return (SearchState(results: results), .none)\n    }\n}\n```\n\nUse `.many` to combine multiple effects:\n\n```swift\nreturn (newState, .many([effect1, effect2]))\n```\n\n## Example App\n\n[FluxxKitExample](https://github.com/keitaoouchi/FluxxKitExample) — A real-world sample app built with FluxxKit.\n\n## Architecture\n\n```\nView → Action → Store.dispatch → Reducer(State, Action) → (NewState, Effect)\n                                                                ↓\n                                                          Effect.run → dispatch(Action)\n```\n\n## License\n\nFluxxKit 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%2Fkeitaoouchi%2Ffluxxkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeitaoouchi%2Ffluxxkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeitaoouchi%2Ffluxxkit/lists"}