{"id":15055394,"url":"https://github.com/denizcoskun/rxstore","last_synced_at":"2025-04-10T03:41:03.417Z","repository":{"id":54515708,"uuid":"337835386","full_name":"denizcoskun/RxStore","owner":"denizcoskun","description":"Reactive State Management Tool for Swift","archived":false,"fork":false,"pushed_at":"2021-04-05T22:39:29.000Z","size":47,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-03-14T16:43:48.768Z","etag":null,"topics":["combine","ngrx","ngrx-store","redux","rxswift","selector","state-management","swift","swiftui"],"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/denizcoskun.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":"2021-02-10T19:46:30.000Z","updated_at":"2023-03-14T23:13:13.000Z","dependencies_parsed_at":"2022-08-13T18:20:45.809Z","dependency_job_id":null,"html_url":"https://github.com/denizcoskun/RxStore","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizcoskun%2FRxStore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizcoskun%2FRxStore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizcoskun%2FRxStore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizcoskun%2FRxStore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denizcoskun","download_url":"https://codeload.github.com/denizcoskun/RxStore/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248154963,"owners_count":21056541,"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":["combine","ngrx","ngrx-store","redux","rxswift","selector","state-management","swift","swiftui"],"created_at":"2024-09-24T21:41:45.523Z","updated_at":"2025-04-10T03:41:03.391Z","avatar_url":"https://github.com/denizcoskun.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RxStore\n\nRxStore is a fully reactive state management tool built on top of Apple's [Combine](https://developer.apple.com/documentation/combine) framework. \nIt is a naive implementation of [Redux](https://redux.js.org/) inspired by [@ngrx/store](https://ngrx.io/guide/store).\n\n## Demo App\n[Spotify-Playlist-Sorter](https://github.com/denizcoskun/Spotify-Playlist-Sorter)\n\n## Basic Usage\n\n\n```swift\n\n// Define your app store, it can have multiple sub states\nclass AppStore: RxStore {\n    var counterState = RxStore.State(0)\n}\n\n// Define actions\nenum CounterAction {\n    struct Increment: RxStoreAction {}\n    struct Decrement: RxStoreAction {}\n}\n\n// Create a reducer\nlet reducer : RxStore.Reducer\u003cInt\u003e = {state, action in\n    switch action {\n    case _ as CounterAction.Increment:\n        return state + 1\n    case _ as CounterAction.Decrement:\n        return state - 1\n    default:\n        return state\n    }\n}\n\n// Register the reducer and initialize the app store\n\nlet appStore = AppStore()\n    .registerReducer(for: \\.counterState, reducer)\n    .initialize()\n\n// You are ready to go\n\nlet cancellable = appStore\n    .counterState\n    .sink(receiveValue: {print($0)}) // 0, 1\n\nappStore.dispatch(action: CounterAction.Increment)\n\n```\n\n\n## App store with multiple states\n\n\n```swift\n\n// Define your app store, it can have multiple sub states\nclass AppStore: RxStore {\n    var counterState = RxStore.State(0)\n    var loadingState = RxStore.State(false)\n}\n\n// Define actions\nenum CounterAction {\n    struct Increment: RxStoreAction {}\n    struct Decrement: RxStoreAction {}\n}\n\nenum LoadingAction {\n    struct Loading: RxStoreAction {}\n    struct Loaded: RxStoreAction {}\n}\n\n\n// Reducer for counter state\nlet counterReducer : RxStore.Reducer\u003cInt\u003e = {state, action in\n    switch action {\n    case _ as CounterAction.Increment:\n        return state + 1\n    case _ as CounterAction.Decrement:\n        return state - 1\n    default:\n        return state\n    }\n}\n\n// Reducer for loading state\nlet loadingReducer: RxStore.Reducer\u003cBool\u003e = {state, action in\n    switch action {\n    case _ as LoadingAction.Loading:\n        return true\n    case _ as LoadingAction.Loaded:\n        return false\n    default:\n        return state\n    }\n}\n\n// Register the reducer and initialize the app store\n\nlet appStore = AppStore()\n    .registerReducer(for: \\.counterState, counterReducer)\n    .registerReducer(for: \\.loadingState, loadingReducer)\n    .initialize()\n\n// You are ready to go\n\nlet cancellable = appStore\n    .counterState\n    .sink(receiveValue: {print($0)}) // 0, 1\n\nlet cancellable2 = appStore\n    .loadingState\n    .sink(receiveValue: {print($0)}) // false, true\n\nappStore.dispatch(action: CounterAction.Increment())\nappStore.dispatch(action: LoadingAction.Loaded())\n\n```\n\n## Usage with side effects\n\n\n```swift\n\nstruct Todo {\n let id: Int\n let text: String\n}\n\ntypealias TodosState = Dictionary\u003cInt, Todo\u003e\n\nclass AppStore: RxStore {\n    var todosState = RxStore.State\u003cTodosState\u003e([:])\n    var loadingState = RxStore.State(false)\n}\n\nenum Action {\n    struct LoadTodos: RxStoreAction {}\n    struct LoadTodosSuccess: RxStoreAction {\n        let payload: [Todo]\n    }\n    struct LoadTodosFailure: RxStoreAction {\n        let error: Error\n    }\n}\n\nlet todoReducer: RxStore.Reducer = {state, action -\u003e TodosState in\n    switch action {\n    case let action as Action.LoadTodosSuccess:\n        var newState = state\n        action.payload.forEach {\n            newState[$0.id] = $0\n        }\n        return newState\n    default:\n        return state\n    }\n}\n\nlet loadTodosEffect = AppStore.createEffect(Action.LoadTodos.self) { store, action in\n    mockGetTodosFromServer()\n        .map { Action.LoadTodosSuccess($0) }\n        .catch {Just(Action.LoadTodosFailure(error: $0))}\n        .eraseToAnyPublisher()\n}\n\n\n\nlet store = AppStore()\n    .registerReducer(for: \\.todosState, reducer: todoReducer)\n    .registerReducer(for: \\.loadingState, reducer: loadingReducer)\n    .registerEffects([loadTodosEffect])\n    .initialize()\n\nlet cancellable = store.todosState.sink(receiveValue: {state in\n            print(state) // [], [\"mock-todo-id\": MockTodo]\n})\n\nstore.dispatch(Action.LoadTodos) // This will fetch the todos from the server \n\n```\n\n\n## Selectors\n\nSelectors allow you to combine sub states and convert them into expected result.\n\nBelow is an example of how a selector can be used:\n\n```swift\nlet todoList = [mockTodo, mockTodo2]\nlet userTodoIds: Dictionary\u003cInt, [Int]\u003e = [userId:[mockTodo.id], userId2: [mockTodo2.id]]\n\nclass AppStore: RxStore {\n    var todos = RxStore.State(todoList)\n    var userTodos = RxStore.State(userTodoIds)\n}\n\nlet store = AppStore().initialize()\n\nfunc getTodosForSelectedUser(_ userId: Int) -\u003e AppStore.Selector\u003c[Todo]\u003e {\n    AppStore.createSelector(path: \\.todos, path2: \\.userTodoIds) { todos, userTodoIds -\u003e [Todo] in\n        let todoIds = userTodoIds[userId] ?? []\n        let userTodos = todos.filter { todo in  todoIds.contains(todo.id) }\n        return userTodos\n    }\n}\n\nlet _ = store.select(getTodosForSelectedUser(userId2)).sink { userTodos in\n    print(userTodos) // [mockTodo2]\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizcoskun%2Frxstore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenizcoskun%2Frxstore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizcoskun%2Frxstore/lists"}