# ReactorKit: unidirectional state for Swift views, with or without SwiftUI

> ReactorKit splits a screen into a passive View and a testable Reactor, joined by two RxSwift streams. It is a good fit for existing UIKit codebases that want to adopt the pattern one screen at a time.

**ReactorKit/ReactorKit** — A library for reactive and unidirectional Swift applications

- Repository: https://github.com/ReactorKit/ReactorKit
- Website: http://reactorkit.io
- Stars: 2,785 · Forks: 272
- Language: Swift
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/reactorkit-reactorkit

## The problem ReactorKit solves: logic trapped in view controllers

Most iOS screens start simple and end up as a view controller that owns networking, formatting, and button state at the same time. That class is hard to test because instantiating it pulls in a window, a nib, and a live network stack. ReactorKit's stated first design goal is testability through separating business logic from a view. A reactor has no dependency on a view, so the README's testing guidance is to test reactors and test view bindings separately rather than driving the UI.

The second goal is adoption cost. ReactorKit does not require the whole application to follow one architecture. The README says it can be adopted partially, for one or more specific views, and that you do not need to rewrite everything to use it on an existing project. That matters more than it sounds: most architecture libraries are sold as all-or-nothing migrations, and a team that cannot migrate incrementally usually does not migrate at all.

The third goal is code volume. The README frames it as focusing on avoiding complicated code for simple things, and claims less code than other architectures. Whether that holds depends on how much ceremony your screen already has, but the shape of a reactor is small: three nested types and an initial state.

## View, Action, Mutation, State: the data flow through a Reactor

The framework combines Flux with reactive programming. User actions and view states travel on observable streams, and the README describes those streams as unidirectional: the view can only emit actions, and the reactor can only emit states. There is no path for a view to mutate state directly.

A Reactor protocol conformance requires three types and one property. Action represents a user interaction, Mutation is described in the README as a bridge between Action and State, and State represents the current view state. The reactor converts the action stream to the state stream in two steps, mutate() and reduce(), and initialState supplies the starting State value.

On the view side, conforming an existing class to ReactorView gives it a reactor property, typically assigned from outside the view. When that property changes, bind(reactor:) is called, and that method is where you map inputs to reactor.action and outputs from reactor.state back to UI components. The README's example binds a refresh button's tap to a refresh action and maps state.isFollowing to a button's isSelected. The split is strict enough to be useful: the view decides where things go, the reactor decides what they mean.

One detail worth knowing before you pick a protocol: if a view controller comes from a storyboard, DeferredReactorView defers binding until the view is loaded. Assigning reactor then does not run bind(reactor:) immediately; it runs after viewDidLoad.

## Installing ReactorKit and writing a first reactor

The README's installation section points at the usual Swift distribution channels, and the repository carries both a ReactorKit.podspec for CocoaPods and a Package.swift for Swift Package Manager. The README's own installation material is where the concrete steps live, and the repository's Package.swift is the authoritative source for the supported Swift tools version and platform minimums, so check it against your toolchain before upgrading an older app.

Once the module is linked, a reactor is three nested types plus the initial state. This is the README's ProfileViewReactor example:

```swift
class ProfileViewReactor: Reactor {
  // represent user actions
  enum Action {
    case refreshFollowingStatus(Int)
    case follow(Int)
  }

  // represent state changes
  enum Mutation {
    case setFollowing(Bool)
  }

  // represents the current view state
  struct State {
    var isFollowing: Bool = false
  }

  let initialState: State = State()
}
```

At this point the code compiles but nothing happens, because mutate() and reduce() are not implemented yet. That is the first thing to verify after wiring the module in: an empty reactor conforms but produces no state changes, so a screen that looks frozen is usually a reactor with unimplemented steps rather than a binding bug.

## Where ReactorKit gets awkward: threading, Pulse, and global state

The README devotes separate advanced sections to global states, view communication, threading, and Pulse, which is a signal that these are the parts people ask about rather than the parts that are automatic. Threading in particular is not something the framework hides: because everything is an RxSwift stream, the scheduler you observe on is your decision, and a state emission delivered off the main queue will reach UI bindings off the main queue.

Pulse exists because State is a value that is re-emitted, so a boolean like isFollowing cannot express an event that should fire once. The README treats it as an advanced topic with its own section, and that is the honest framing: if your screen needs one-shot effects such as navigation or an alert, plain State equality will not give you that for free, and you have to reach for the Pulse mechanism or handle it yourself.

Global state is the other pressure point. The README covers it, but a shared store that several reactors read from reintroduces the coupling that per-view reactors were meant to remove. ReactorKit's own design goal is start small, so the framework's advice and its architecture point the same way: keep reactors local until you have a concrete reason not to.

The clearest case where ReactorKit is the wrong tool is a codebase with no RxSwift and no appetite for it. Every binding in the README is an RxSwift binding, and the dependencies section lists RxSwift. If your team has standardized on Combine, adding RxSwift alongside it means two reactive stacks in one app, which is a cost the framework does not offset.

## SwiftUI support and how it differs from the UIKit path

ReactorKit ships a SwiftUI layer, and the README gives it its own top-level section rather than treating it as a footnote. ObservableState, ObservedReactor, and ReactorObserving are the pieces named there, along with a ReactorBindable property wrapper and a binding(get:send:) helper for two-way controls. There is also ObservableStateIgnored, which exists so that a field in State does not trigger a view update.

The difference from the UIKit path is the direction of the binding. In UIKit you implement bind(reactor:) and push state into controls imperatively. In SwiftUI the state is observed and the view body re-renders, so the interesting decisions move to what is exposed as observable state and what is excluded from it. ObservableStateIgnored is the tell: without it, every field in State participates in view identity and re-render decisions, and a large State struct becomes a performance question rather than a modelling one.

The README also has a testing subsection for SwiftUI, separate from the reactor and view testing described for UIKit. If you plan to run both UI frameworks in one app during a migration, expect to maintain two binding styles against the same reactors, which is workable but is real work.

## ReactorKit compared with RxFeedback and Point-Free approaches

RxFeedback is the closest neighbour in the Swift reactive space, and the difference is in where the state machine lives. ReactorKit gives you named types: Action, Mutation, and State are explicit declarations, and mutate() and reduce() are methods you implement. RxFeedback expresses the same loop as a system of feedback functions composed from streams, with the state type and its reducer passed as arguments. ReactorKit's version is more prescriptive and more legible to someone reading the file for the first time; RxFeedback's version is more composable and keeps the loop in one expression rather than spread across protocol requirements.

The Point-Free ecosystem takes a different route again, building state management out of composable reducers and, in newer work, Swift's own Observation facilities. The practical distinction for a team choosing today is dependency gravity. ReactorKit pulls in RxSwift and asks you to write bindings in RxSwift. The Point-Free approach is designed to stand on its own rather than on an existing reactive framework. If you are starting a new app with no RxSwift history, ReactorKit's main advantage, incremental adoption inside an RxSwift codebase, does not apply to you.

## Maintenance, licensing, and the cost of staying on ReactorKit

ReactorKit is MIT licensed, which permits commercial and closed-source use; the repository's LICENSE file is the text to read, and this is a description of the licence identifier rather than legal advice. The practical implication for a shipped app is that there is no copyleft obligation attached to the framework itself.

The maintenance picture is more nuanced than a version number suggests. The most recent release listed is 3.2.0 from 2022-01-13, while the last push to the repository was on 2026-05-07. So the codebase has seen activity since the last tagged release, and the README badge advertises Swift 6.0 support, but consumers who depend on released versions are working from a tag that is several years old. The repository is not archived.

Upgrade cost is concentrated in two places. First, the RxSwift dependency: a major RxSwift release is a ReactorKit migration whether or not ReactorKit itself changes, because the bindings in your views are RxSwift code. Second, the Swift toolchain: the README badge and Package.swift both speak to the supported Swift version, and a project pinned to an older Xcode will need to confirm compatibility before pulling a newer commit. The Makefile shows the documentation pipeline (swift build, sourcekitten doc, jazzy, published to gh-pages), which is a signal about how the project is maintained but tells you nothing about release cadence.

## Conclusion

Adopt ReactorKit when you already ship RxSwift and want business logic out of view controllers without rewriting the app; the README states the architecture can be adopted partially, for one or more specific views. Skip it if your app is built on Combine or on Swift's Observation, since the core binding is an RxSwift stream. Before committing, check that your Swift toolchain satisfies the Package.swift requirement, and read the Pulse and threading sections, because those are where the framework's behaviour is easiest to get wrong.

## FAQ

### Does ReactorKit work with SwiftUI?

Yes. The README has a dedicated SwiftUI section covering ObservableState, ObservedReactor, ReactorObserving, the ReactorBindable property wrapper, and binding(get:send:), plus a separate testing subsection for SwiftUI.

### Can I use ReactorKit in only part of an existing app?

The README states that ReactorKit does not require the whole application to follow a single architecture and can be adopted partially, for one or more specific views, without rewriting everything.

### What is the difference between Action, Mutation and State in ReactorKit?

Action represents a user interaction, State represents a view state, and Mutation is described in the README as a bridge between the two. A reactor converts the action stream to the state stream through mutate() and then reduce().

### Does ReactorKit depend on RxSwift?

Yes. The README lists RxSwift under dependencies, and the documented view bindings map inputs and outputs through RxSwift streams, so the framework is not usable without it.

## Sources

- [License: MIT](https://github.com/ReactorKit/ReactorKit/blob/main/LICENSE)
- [Project website](http://reactorkit.io)
- [ReactorKit/ReactorKit on GitHub](https://github.com/ReactorKit/ReactorKit)
- [README](https://github.com/ReactorKit/ReactorKit/blob/main/README.md)
- [Releases](https://github.com/ReactorKit/ReactorKit/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/reactorkit-reactorkit
