{"id":16209653,"url":"https://github.com/garume/unity-redux-middleware","last_synced_at":"2026-05-01T16:36:10.713Z","repository":{"id":232259746,"uuid":"783876894","full_name":"Garume/Unity-Redux-Middleware","owner":"Garume","description":"Unity Redux Middleware enhances AppUI Redux in Unity with domain-specific middleware and R3 for reactive extensions.","archived":false,"fork":false,"pushed_at":"2024-04-13T20:13:19.000Z","size":656,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-14T16:27:17.755Z","etag":null,"topics":["appui","redux","redux-middleware","redux-observable","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","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/Garume.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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}},"created_at":"2024-04-08T18:46:18.000Z","updated_at":"2024-04-16T09:50:53.622Z","dependencies_parsed_at":"2024-04-16T09:50:46.928Z","dependency_job_id":null,"html_url":"https://github.com/Garume/Unity-Redux-Middleware","commit_stats":null,"previous_names":["garume/unity-redux-middleware"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Garume/Unity-Redux-Middleware","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Garume%2FUnity-Redux-Middleware","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Garume%2FUnity-Redux-Middleware/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Garume%2FUnity-Redux-Middleware/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Garume%2FUnity-Redux-Middleware/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Garume","download_url":"https://codeload.github.com/Garume/Unity-Redux-Middleware/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Garume%2FUnity-Redux-Middleware/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32505109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["appui","redux","redux-middleware","redux-observable","unity"],"created_at":"2024-10-10T10:31:07.168Z","updated_at":"2026-05-01T16:36:10.699Z","avatar_url":"https://github.com/Garume.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unity Redux Middleware\n\nUnityReduxMiddleware provides middleware for adding logic processing to Unity.AppUI.Redux.\nIt can also be integrated with R3 to add processes that can be written in Rx.\n\n[![license](https://img.shields.io/badge/LICENSE-MIT-green.svg)](LICENSE)\n\n[日本語版 README](https://github.com/Garume/Unity-Redux-Middleware/blob/master/README_JA.md)\n\n## Overview\n\nUnity.AppUI.Redux is supposed to be based on a state management library developed in Javascript. However, AppUI.Redux does not have the middleware functionality of the original library. By using this middleware, it is possible to insert processing before and after dispatch.\n\nThis library adds this middleware to AppUI.Redux.\n\nNote: This is not an explanation of AppUI.Redux.\n\n## Table of Contents\n\n\u003cdetails\u003e\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Setup](#setup)\n  - [Requirements](#requirements)\n  - [Installation](#installation)\n- [Demonstration](#demonstration)\n- [Middleware](#middleware)\n  - [Concepts](#concepts)\n  - [How to create Middleware](#how-to-create-middleware)\n  - [Execution order](#execution-order)\n  - [Exception Handling](#exception-handling)\n  - [Asynchronous Processing](#asynchronous-processing)\n  - [Tests](#tests)\n- [Epic](#epic)\n  - [Concepts](#concepts-1)\n  - [Setup.](#setup)\n  - [Demo](#demo)\n  - [How to create Epic](#how-to-create-epic)\n  - [Operators](#operators)\n    - [OfAction](#ofaction)\n    - [Dispatch](#dispatch)\n  - [Combine](#combine)\n- [Support UniTask](#support-unitask)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c/details\u003e\n\n## Setup\n\n### Requirements\n\n-   Unity 2022.3 or higher\n\n### Installation\n\n1. select Window \u003e Package Manager\n2. Select the \"+\" button \u003e Add package from git URL\n3. Enter the following URL\n\n```\nhttps://github.com/Garume/Unity-Redux-Middleware.git?path=/Assets/UnityReducMiddleware\n```\n\nOr open Packages/manifest.json and add the following to the dependencies block\n\n```\n{\n    \"dependencies\": {\n        \"com.garume.unity-redux-middleware\": \"https://github.com/Garume/Unity-Redux-Middleware.git?path=/Assets/UnityReducMiddleware\"\n    }\n}\n```\n\n## Demonstration\n\nCreate LoggerMiddleware that outputs logs before and after dispatch.\n\n```cs\nusing UnityEngine;\nusing UnityReduxMiddleware;\n\npublic class LoggerMiddleware\n{\n    public static MiddlewareDelegate Create()\n    {\n        return store =\u003e next =\u003e async (action, token) =\u003e\n        {\n            var appName = action.type.Split('/')[0]; // get appName from action.\n            Debug.Log(store.GetState()[appName]); // log the current state.\n            await next(action, token); // call the next middleware. If there is no next middleware, dispatch is called.\n            Debug.Log(store.GetState()[appName]); // output the state after dispatch to the log.\n        };\n    }\n}\n```\n\nLet's apply the LoggerMiddleware we created.\n\n```cs\nusing Unity.AppUI.Redux;\nusing UnityReduxMiddleware;\n\npublic class Sample\n{\n    public void Main()\n    {\n        var store = new MiddlewareStore(); // Use MiddlewareStore instead of Store when using Middleware.\n        store.AddMiddleware(LoggerMiddleware.Create()); // Add LoggerMiddleware.\n        store.CreateSlice(Actions.AppName, new AppState(),.\n            builder =\u003e builder.Add(Actions.Increment, IncrementReducer)); // Create the slice.\n\n        store.Dispatch(Actions.IncrementAction.Invoke()); // dispatch the action.\n    }\n\n\n    public AppState IncrementReducer(AppState state, Action action)\n    {\n        return state with { Count = state.Count + 1 };\n    }\n\n    public record AppState\n    {\n        public int Count { get; set; }\n    }\n\n    public static class Actions\n    {\n        public const string AppName = \"app\";\n        public const string Increment = AppName + \"/Action1\";\n        public static readonly ActionCreator IncrementAction = Store.CreateAction(Increment);\n    }\n}\n```\n\nIf you run this, you will get the following results. (It is recommended to run this using TestRunner.)\n\n![alt text](docs/image.png)\n\n```\nDebug.Log()\n↓\ndispatch // State is updated\n↓\nDebug.Log()\n```\n\nAs a result, we were able to add processing before and after dispatch.\n\n## Middleware\n\n### Concepts\n\nThe Middleware design philosophy is based on the Redux philosophy.\nPlease check [Redux](https://www.infinijith.com/blog/redux/redux-middleware) for more information.\n\nThe following is a brief explanation.\n\nNormal dispatch processing only passes the Action to the Reducer.\n\n![alt text](docs/image-1.png)\nhttps://www.infinijith.com/blog/redux/redux-middleware\n\nAdding Middleware allows for side effects and asynchronous processing between dispatch and Reducer. This allows logic to be confined to the domain layer.\n\n![alt text](docs/image-2.png)\nhttps://www.infinijith.com/blog/redux/redux-middleware\n\nIn addition, the middleware is independent of each other and loosely coupled, making it easy to test.\n\n### How to create Middleware\n\nWhen creating Middleware, use `MiddlewareDelegate` as the return value.\nCall `await(action,token)` in the delegate to proceed to the next Middleware.\n\n```cs\npublic static MiddlewareDelegate Create()\n{\n    return store =\u003e next =\u003e async (action, token) =\u003e\n    {\n        await next(action, token); // Call the next middleware.\n    };\n}\n```\n\nIf there is no next middleware, it is dispatched using the current action and the State is updated.\nConditional branching can also be done by action.\n\nFor example, if you want to execute a process when the actionType is `\"App/Increment\"`, do the following.\n\n```cs\npublic static MiddlewareDelegate Create()\n{\n    return store =\u003e next =\u003e async (action, token) =\u003e\n    {\n        if (action.type == \"App/Increment\")\n        {\n            // Processing to be added\n        }\n        await next(action, token); // Call the next middleware.\n    };\n}\n```\n\nDo not forget to execute `await(action,token)` at this time.\n\n### Execution order\n\nYou can add multiple pieces of middleware.\n\nThey are executed in the order in which they are added.\n\n```cs\nvar store = new MiddlewareStore();\nstore.AddMiddleware(TestMiddleware1.Create());\nstore.AddMiddleware(TestMiddleware2.Create());\nstore.AddMiddleware(TestMiddleware3.Create());\n...\n```\n\nFor example, when added like this, the Action is passed as follows\n\n```\nTestMiddleware1\n↓\nTestMiddleware2\n↓\nTestMiddleware3\n```\n\n### Exception Handling\n\nMiddlewareStore does not include try/catch middleware.\n\nInstead, `ExceptionMiddleware`, a try/catch middleware, is included.\n\nBasically, it is recommended to add this middleware first.\n\n```cs\nvar store = new MiddlewareStore();\nstore.AddMiddleware(ExceptionMiddleware.Create());\nstore.AddMiddleware(TestMiddleware.Create());\n...\n```\n\nThe `ExceptionMiddleware` rethrows all exceptions except OperationCanceledException. If you prefer a different behavior, please create it accordingly.\n\n### Asynchronous Processing\n\nMiddleware has a basic design that allows the use of Tasks for asynchronous processing.\nAccordingly, an asynchronous method version of `Dispatch` is also provided.\n\n```cs\nvoid Dispatch(Action action)\nvoid Dispatch(string actionType)\nvoid Dispatch\u003cT\u003e(string actionType, T payload)\nasync Task DispatchAsync(Action action, CancellationToken token = default)\nasync Task DispatchAsync(string actionType, CancellationToken token = default)\nasync Task DispatchAsync\u003cT\u003e(string actionType, T payload, CancellationToken token = default)\n```\n\n### Tests\n\n`Unity-Redux-Middleware/Assets/UnityReduxMiddleware/Tests/Runtime`.\n\n## Epic\n\nEpic allows Observable to be handled.\n\nNote: R3 setup is required.\n\n### Concepts\n\nRefer to redux-observable.\n\nEpic is a function that takes an Observable\u003cAction\u003e and returns an Observable\u003cAction\u003e.\n\nIn redux-observable, this is called \"Actions in, actions out.\n\n![alt text](docs/image-3.png)\nhttps://makeitnew.io/epic-reactive-programming-with-redux-observable-eff4d3fb952f\n\nEpic's ability to handle Actions as streams allows for the use of a rich set of operators.\nThis makes it possible to easily write processes that would be difficult in middleware.\n\nAlso, unlike Middleware, Epic cannot insert processing before dispatch.\nInstead, Epic itself can issue new Actions.\n\n### Setup.\n\nR3 setup is required.\nPlease refer to the following for installation.\nhttps://github.com/Cysharp/R3?tab=readme-ov-file#unity\n\nAfter successful installation, Epic features will be activated.\n\n### Demo\n\nFirst, let's create Epic that outputs a State when an Action is sent.\n\nSince we also need a State to create Epic, we will also create an AppState.\nEpic can be created from `Epic.Create\u003cTState\u003e()`.\n\n```cs\npublic static Epic\u003cAppState\u003e CreateEpic()\n    {\n        return Epic.Create\u003cAppState\u003e((action, state) =\u003e\n        {\n            return action.Do(_ =\u003e Debug.Log($\"State: {state.CurrentValue}\")); // Outputs the current State\n        });\n    }\n\n    public record AppState\n    {\n        public int Count { get; set; }\n    }\n```\n\nNext, create a MiddlewareStore that will use Epic.\nAt the same time, create EpicMiddleware to run Epic.\nUnlike normal middleware, this middleware needs to be `Run` to be associated with Epic after it is added.\n\n```cs\npublic void Main()\n{\n    var store = new MiddlewareStore();\n    var epicMiddleware = EpicMiddleware.Default\u003cAppState\u003e(); // Create the epic middleware.\n    store.CreateSlice(\"app\", new AppState(), builder =\u003e\n    {\n        builder.Add(Actions.Increment, IncrementReducer); // Tie the action type to the reducer.\n    }); // Create a slice.\n    store.AddMiddleware(epicMiddleware.Create()); // Add epic middleware.\n    epicMiddleware.Run(CreateEpic()); // Run epic.\n    Store.Dispatch(Actions.IncrementAction.Invoke()); // Dispatch the action.\n}\n\npublic AppState IncrementReducer(AppState state, Action action)\n{\n    return state with { Count = state.Count + 1 };\n}\n\n\npublic static class Actions\n{\n    public const string AppName = \"app\";\n    public const string Increment = AppName + \"/Increment\";\n    public const string IncrementRequest = AppName + \"/IncrementRequest\";\n    public static readonly ActionCreator IncrementAction = Store.CreateAction(Increment);\n}\n```\n\nIf you run this, you should get the following results. (We recommend using TestRunner.)\n\n![alt text](docs/image-5.png)\n\nNext, let's create an Epic that converts a specific Action`IncrementActionRequest` into an Action`IncrementAction` when it is received.\nEpic can be combined, so use it as needed.\n\n```cs\n\npublic void Main()\n{\n    var store = new MiddlewareStore();\n    var epicMiddleware = EpicMiddleware.Default\u003cAppState\u003e(); // Create the epic middleware.\n    store.CreateSlice(\"app\", new AppState(), builder =\u003e\n    {\n        builder.Add(Actions.Increment, IncrementReducer); // Tie the action type to the reducer.\n    }); // Create a slice.\n    store.AddMiddleware(epicMiddleware.Create()); // Add epic middleware.\n    epicMiddleware.Run(CreateEpic()); // Run epic.\n\n    store.Dispatch(Actions.IncrementRequest); // Dispatch the action.\n    Debug.Log(store.GetState\u003cAppState\u003e(Actions.AppName)); // Log the result.\n}\n\n\npublic static Epic\u003cAppState\u003e CreateEpic()\n{\n    var epic1 = Epic.Create\u003cAppState\u003e((action, state) =\u003e\n    {\n        return action.Do(_ =\u003e Debug.Log($\"State: {state.CurrentValue}\"));\n    });\n\n    var epic2 = Epic.Create\u003cAppState\u003e((action, state) =\u003e\n    {\n        return action.Where(x =\u003e x.type == Actions.IncrementRequest)\n            .Select(_ =\u003e Actions.IncrementAction.Invoke());\n    });\n\n    return Epic.Combine(epic1, epic2);\n}\n```\n\nIf you run this, you will get the following results\n\n! [alt text](docs/image-6.png)\n\n### How to create Epic\n\nTo create Epic as usual, use the following.\n\n```cs\nEpic\u003cTState\u003e Epic.Create\u003cTState\u003e\nCreate\u003cTState,TDependency\u003e Epic.\n```\n\nExtension methods `string` and `ActionCreator` are provided for filtering by Action.\nFor example, you can write the following.\n\n```cs\nActionCreator actionCreator = Store.CreateAction(\"App/Increment\");\nactionCreator.CreateEpic\u003cAppState\u003e((action, state) =\u003e\n{\n    return action.Do(_ =\u003e Debug.Log($\"State: {state.CurrentValue}\")); // Execute only the Action of \"App/Increment\".\n});\n\nstring actionType = \"App/Increment\";\n\nactionType.CreateEpic\u003cAppState\u003e((action, state) =\u003e\n{\n    return action.Do(_ =\u003e Debug.Log($\"State: {state.CurrentValue}\"));　// Execute only the Action of \"App/Increment\".\n});\n```\n\n### Operators\n\nWe provide operators that are useful for adding processing.\n\n#### OfAction\n\nOnly the Action passed as an argument is passed through.\n\n#### Dispatch\n\nDispatch with the Action passed as argument.\n\n### Combine\n\nThe following two methods are provided to combine Epic.\n\n```cs\nEpic.Combine\u003cTState\u003e()\nEpicbuilder\n```\n\nEpicbuilder can be used as follows\n\n```cs\nprivate Epic\u003cApiMockState\u003e RootEpic()\n{\n    var builder = Epic.CreateBuilder\u003cApiMockState\u003e();\n\n    Actions.SendRequest.CreateEpic\u003cApiMockState\u003e((action, state) =\u003e\n        action.Do(Debug.Log).Dispatch(Actions.SendAction.Invoke(\"Requesting...\"))\n    ).AddTo(ref builder);\n\n    Actions.SendRequest.CreateEpic\u003cApiMockState\u003e((action, state) =\u003e\n        action.Delay(TimeSpan.FromSeconds(2))\n            .Dispatch(Actions.SendAction.Invoke(\"Hello, Unity!\"))\n    ).AddTo(ref builder);\n\n\n    return builder.Build();\n}\n```\n\n## Support UniTask\n\nUnityReduxMiddleware typically uses `Task` internally. However, there may be situations where you would prefer to use `UniTask`.\n\nIn such cases, simply incorporating UniTask into your project is sufficient. The middleware will automatically adjust its operations to return `UniTask`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgarume%2Funity-redux-middleware","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgarume%2Funity-redux-middleware","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgarume%2Funity-redux-middleware/lists"}