{"id":1824,"url":"https://github.com/ReactorSwift/Reactor","last_synced_at":"2025-07-31T12:33:21.967Z","repository":{"id":144339385,"uuid":"65959430","full_name":"ReactorSwift/Reactor","owner":"ReactorSwift","description":"🔄 Unidirectional data flow in Swift.","archived":false,"fork":false,"pushed_at":"2020-06-13T17:02:01.000Z","size":83,"stargazers_count":176,"open_issues_count":3,"forks_count":16,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-04-11T15:06:22.598Z","etag":null,"topics":["architecture","elm-architecture","ios","mac","mac-app","redux","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/ReactorSwift.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}},"created_at":"2016-08-18T02:44:48.000Z","updated_at":"2023-07-25T07:20:46.000Z","dependencies_parsed_at":"2023-07-28T01:31:34.758Z","dependency_job_id":null,"html_url":"https://github.com/ReactorSwift/Reactor","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactorSwift%2FReactor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactorSwift%2FReactor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactorSwift%2FReactor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ReactorSwift%2FReactor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ReactorSwift","download_url":"https://codeload.github.com/ReactorSwift/Reactor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228248406,"owners_count":17891447,"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":["architecture","elm-architecture","ios","mac","mac-app","redux","swift","unidirectional-data-flow"],"created_at":"2024-01-05T20:15:56.679Z","updated_at":"2024-12-05T06:31:18.212Z","avatar_url":"https://github.com/ReactorSwift.png","language":"Swift","funding_links":[],"categories":["Reactive Programming"],"sub_categories":["Other free courses","Prototyping","Other Parsing"],"readme":"![](Logo/Logo.png)\n\n![bitrise](https://app.bitrise.io/app/e94000174c55ac03.svg?token=qFQSScqJqmNAUfE-1Sv0rA)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)\n![macOS](https://img.shields.io/badge/macOS-10.9-lightgrey.svg)\n\n# Reactor\n\nReactor is a framework for making more reactive applications inspired by [Elm](https://github.com/evancz/elm-architecture-tutorial), [Redux](http://redux.js.org/docs/basics/index.html), and recent work on [ReSwift](https://github.com/ReSwift/ReSwift). It's small and simple (just one file), so you can either use Carthage to stay up to date, or just drag and drop into your project and go.  Or you can look through it and roll your own.\n\nReactor encourages unidirectional data flow from a single source of truth—i.e., there is one global object responsible for managing application data, and all UI is derived and updated from it. This way your [UI is always in sync with your data](https://medium.com/swift-fox/react-native-native-28e37f7de1ae#.tbp9edrnn), and your data is sync with itself since there are not multiple copies of it floating around your app.\n\n\n## Architecture\n\n```\n                                                     ┌──────────────────┐\n                                                     │                  │\n                                                     │                  │\n                                                     │     Command      │\n                                                 ┌───│     (Async)      │\n                                                 │   │                  │\n                                                 │   │                  │\n                                                 │   └──────────────────┘\n                                                 │\n┌──────────────────┐                             │   ┌──────────────────┐\n│                  │                             │   │                  │\n│                  │         ┌───────────┐       │   │                  │\n│                  │◀────────┤   Event   ├────◀──┴───┤                  │\n│                  │         └───────────┘           │                  │\n│                  │                                 │                  │\n│       Core       │                                 │    Subscriber    │\n│                  │                                 │                  │\n│                  │                                 │                  │\n│    ┌───────┐     │         ┌───────────┐           │                  │\n│    │ State │     ├─────────┤   State   ├───────┬──▶│                  │\n│    └───────┘     │         └───────────┘       │   │                  │\n│                  │                             │   │                  │\n└──────────────────┘                             │   └──────────────────┘\n                                                 │\n                                                 │   ┌──────────────────┐\n                                                 │   │                  │\n                                                 │   │                  │\n                                                 └──▶│    Middleware    │\n                                                     │                  │\n                                                     │                  │\n                                                     └──────────────────┘\n```\n\nThere are six objects in the Reactor architecture:\n\n1. The `State` object - A struct with properties representing application data.\n1. The `Event` - Can trigger a state update.\n1. The `Core` - Holds the application state and responsible for firing events.\n1. The `Subscriber` - Often a view controller, listens for state updates.\n1. The `Command` - A task that can asynchronously fire events. Useful for networking, working with databases, or any other asynchronous task.\n1. `Middleware` - Receives every event and corresponding state. Useful for analytics, error handling, and other side effects.\n\n## State\n\nState is anything that conforms to `State`. Here is an example:\n\n```swift\nstruct Player: State {\n    var name: String\n    var level: Int\n\n    mutating func react(to event: Event) {\n        switch event {\n        case let _ as LevelUp:\n            level += 1\n        default:\n            break\n        }\n    }\n}\n```\n\nHere we have a simple `Player` model, which is state in our application. Obviously most application states are more complicated than this, but this is where composition comes into play: we can create state by composing states.\n\n```swift\nstruct RPGState: State {\n    var player: Player\n    var monsters: Monsters\n\n    mutating func react(to event: Event) {\n        player.react(to: event)\n        monsters.react(to: event)\n    }\n}\n```\n\nParent states can react to events however they wish, although this will in most cases involve delegating to substates default behavior.\n\nSide note: does the sight of `mutating` make you feel impure? Have no fear, [`mutating` semantics on value types](http://chris.eidhof.nl/post/structs-and-mutation-in-swift/) here are actualy very safe in Swift, and it gives us an imperative look and feel, with the safety of functional programming.\n\n## Events\n\nWe've seen that an `Event` can change state. What does an `Event` look like? In it's most basic form, an event might look like this:\n\n```swift\nstruct LevelUp: Event {}\n```\n\nIn other situations, you might want to pass some data along with the event. For example, in an application with more than one player we need to know which player is leveling up.\n\n```swift\nstruct LevelUp: Event {\n    var playerID: Int\n}\n```\n\nFor many events, generics work very nicely.\n\n```swift\nstruct Update\u003cT\u003e: Event {\n    var newValue: T\n}\n```\n\n## The Core\n\nSo, how does the state get events? Since the `Core` is responsible for all `State` changes, you can send events to the core which will in turn update the state by calling `react(to event: Event)` on the root state. You can create a shared global `Core` used by your entire application (my suggestion), or tediously pass the reference from object to object if you're a masochist.\n\nIn order to initialize your core, simply call the `Core`'s constructor and pass in your initial state and any middleware (discussed later in this readme). Personally, I like to make my core a shared instance and namespace it inside an enum.\n\n```swift\nenum App {\n  static let sharedCore = Core(state: RPGState(), middlewares: [\n        ReachabilityMiddleware(),\n        ErrorLogger(),\n        AnalyticsMiddleware(),\n        ])\n}\n```\n\nHere is an example of a simple view controller with a label displaying our intrepid character's level, and a \"Level Up\" button.\n\n```swift\nclass PlayerViewController: UIViewController {\n    var core = App.sharedCore\n    @IBOutlet weak var levelLabel: UILabel!\n\n    override func viewDidAppear(_ animated: Bool) {\n        super.viewDidAppear(animated)\n        core.add(subscriber: self)\n    }\n\n    override func viewDidDisappear(_ animated: Bool) {\n        super.viewDidDisappear(animated)\n        core.remove(subscriber: self)\n    }\n\n    @IBAction func didPressLevelUp() {\n        core.fire(event: LevelUp())\n    }\n}\n\nextension ViewController: Reactor.Subscriber {\n    func update(with state: RPGState) {\n        levelLabel?.text = String(state.level)\n    }\n}\n```\n\nBy subscribing and subscribing in `viewDidAppear`/`viewDidDisappear` respectively, we ensure that whenever this view controller is visible it is up to date with the latest application state. Upon initial subscription, the core will send the latest state to the subscriber's `update` function. Button presses forward events back to the core, which will then update the state and result in subsequent calls to `update`. (note: the `Core` always dispatches back to the main thread when it updates subscribers, so it is safe to perform UI updates in `update`.)\n\n## Commands\n\nSometimes you want to fire an `Event` at a later point, for example after a network request, database query, or other asynchronous operation. In these cases, `Command` helps you interact with the `Core` in a safe and consistent way.\n\n```swift\nstruct CreatePlayer: Command {\n    var session = URLSession.shared\n    var player: Player\n\n    func execute(state: RPGState, core: Core\u003cRPGState\u003e) {\n        let task = session.dataTask(with: player.createRequest()) { data, response, error in\n            // handle response appropriately\n            // then fire an update back to the Core\n            core.fire(event: AddPlayer(player: player))\n        }\n        task.resume()\n    }\n}\n\n// to fire a command\ncore.fire(command: CreatePlayer(player: myNewPlayer))\n```\n\nCommands get a copy of the current state, and a reference to the Core which allows them to fire Events as necessary.\n\n## Middleware\n\nSometimes you want to do something with an event besides just update application state. This is where `Middleware` comes into play. When you create a `Core`, along with the initial state, you may pass in an array of middleware. Each middleware gets called every time an event is fired. Middleware cannot mutate the state, but it does get a copy of the state along with the event. Middleware makes it easy to add things like logging, analytics, and error handling to an application. It is great for monitoring event and state and triggering side effects (for example: looking for HTTP 401 errors and then presenting a login screen).\n\n```swift\nstruct LoggingMiddleware: Middleware {\n    func process(event: Event, state: State) {\n        switch event {\n        case _ as LevelUp:\n            print(\"Leveled Up!\")\n        default:\n            break\n        }\n    }\n}\n```\n\n## Installation\n\nFollow the installation guides to integrate `Reactor`in your App.\n\n### Swift Package Manager\n\nTo integrate `Reactor` in your App using [Swift Package Manager](https://swift.org/package-manager/), specify it in your `Package.swift` file:\n\n```\nimport PackageDescription\n\nlet package = Package(\n    [...]\n    dependencies: [\n        .Package(url: \"https://github.com/ReactorSwift/Reactor.git\", majorVersion: XYZ)\n    ]\n)\n```\n\n### Carthage\n\nTo integrate `Reactor` in your App using [Carthage](https://github.com/Carthage/Carthage), specify it in your `Cartfile`:\n\n```\ngithub \"ReactorSwift/Reactor\" ~\u003e X.Y.Z\n```\n\nRun `carthage update` to build the framework and drag the built Reactor.framework into your Xcode project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FReactorSwift%2FReactor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FReactorSwift%2FReactor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FReactorSwift%2FReactor/lists"}