En el artículo sobre TCA (The Composable Architecture) tratamos el flujo de datos unidireccional. Sin embargo, en la escena iOS de Corea ya existía un framework que lo había convertido en un estándar: ReactorKit.
Publicado por Suyeol Jeon en 2017, este framework se consolidó como la arquitectura de facto para UIKit + RxSwift cuando lo adoptaron StyleShare y servicios afiliados a Kakao. Incluso hubo una época en la que “se valora experiencia con ReactorKit” aparecía con frecuencia en las ofertas de empleo.
Hoy veremos la estructura de ReactorKit, sus diferencias con TCA y su posición actual.
View y Reactor: solo hay dos roles
La estructura de ReactorKit es sencilla. Cada pantalla se divide en View y Reactor.
- View: envía la entrada del usuario a
Actiony se suscribe aStatepara dibujar la pantalla. No contiene lógica. - Reactor: bloque de lógica que recibe Actions y genera State. No conoce la UI en absoluto.
La clave es que el flujo de datos dentro del Reactor queda fijado en una sola dirección.
Action → mutate() → Mutation → reduce() → State → View
Cuando un toque en un botón entra en Action.refresh, mutate() gestiona efectos secundarios como solicitudes de red y emite Mutation.setItems([...]). reduce() recibe esa Mutation y calcula un nuevo State. View se suscribe a State y actualiza la pantalla.
¿Por qué se necesita Mutation como paso intermedio?
Si conoces MVVM (Model-View-ViewModel), quizá te preguntes: “¿Por qué no cambiar State directamente desde Action?”. Mutation existe para aislar el trabajo asíncrono.
mutate() es el único lugar donde se permiten tareas asíncronas. En cambio, reduce() es una función pura: con el mismo State y Mutation siempre produce el mismo resultado. Como los efectos secundarios quedan confinados a un solo lugar, también hay un único lugar que revisar para rastrear por qué cambió el estado.
final class SearchReactor: Reactor {
enum Action { case updateQuery(String) }
enum Mutation { case setResults([Repo]) }
struct State { var results: [Repo] = [] }
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .updateQuery(let query):
return service.search(query)
.map { Mutation.setResults($0) }
}
}
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .setResults(let repos): newState.results = repos
}
return newState
}
}
Como Reactor es lógica pura y no conoce la UI, probarlo es sencillo. Envías un Action y compruebas el State.
¿En qué se diferencia de TCA?
Ambos son arquitecturas unidireccionales influenciadas por Redux, pero difieren en su unidad de aplicación.
| ReactorKit | TCA | |
|---|---|---|
| Base | RxSwift | Combine·Swift Concurrency |
| Unidad de aplicación | Un Reactor por pantalla (View) | Componer Reducer de funcionalidades en un árbol |
| Forma de adopción | Se puede introducir gradualmente, pantalla por pantalla | Organizar toda la app como un único sistema |
| Ámbito principal | UIKit | SwiftUI |
La practicidad de ReactorKit proviene de su forma de adopción. En un proyecto MVC (Model-View-Controller) existente, basta con añadir un Reactor a una pantalla. No hace falta rehacer toda la app. TCA, en cambio, gestiona la composición del estado entre funcionalidades, pero exige adoptar el sistema completo.
También existió una biblioteca llamada ReSwift, una adaptación directa de Redux para iOS. Sin embargo, su modelo de almacén único global era difícil de combinar con UIKit y no se extendió tanto como ReactorKit.
ReactorKit en la actualidad
Siendo honestos, cada vez menos proyectos nuevos eligen ReactorKit porque sus debilidades se han vuelto evidentes.
Primero, está fuertemente acoplado a RxSwift. Al migrar el ecosistema de Apple hacia Combine y Swift Concurrency, la dependencia de RxSwift se convirtió en una carga.
Segundo, no encaja con SwiftUI. Su protocolo View y su modelo de binding se diseñaron partiendo de las actualizaciones imperativas de UIKit.
Aun así, el legado de ReactorKit es evidente. Fue el primer framework que inculcó a los desarrolladores iOS coreanos la idea de que “el estado fluye en una sola dirección” y “los efectos secundarios se aíslan en un único lugar”. Si hoy estás aprendiendo TCA, la estructura Action–Mutation–State probablemente te resulte familiar; gran parte de esa intuición se formó durante la era de ReactorKit.
Si mantienes una base de código existente con UIKit + RxSwift, ReactorKit sigue siendo una opción probada. Como se integra y se retira por pantalla, migrar más adelante eliminándolo pantalla por pantalla resulta relativamente sencillo.
Resumen
- ReactorKit es una arquitectura basada en RxSwift que divide cada pantalla en View y Reactor y fuerza el flujo unidireccional Action → Mutation → State.
- El trabajo asíncrono se aísla en
mutate()y el cálculo puro del estado enreduce(). Por eso el rastreo y las pruebas son sencillos. - Sus conceptos son los mismos que los de TCA, pero se diferencia por permitir una adopción práctica y gradual, pantalla por pantalla.
- La adopción en proyectos nuevos ha disminuido por su dependencia de RxSwift y su incompatibilidad con SwiftUI, pero sigue siendo válida en código heredado de UIKit.
En el próximo artículo trataremos RIBs de Uber, que divide la app por unidades de lógica de negocio en lugar de por pantallas. Analizaremos a fondo la arquitectura mencionada solo en un párrafo del artículo sobre VIPER.

![Imagen de portada de [Arquitectura iOS #9] ReactorKit: estándar unidireccional](/assets/images/posts/317cfd0d-2a59-49f9-9bce-a85ec25ae593/reactorkit-unidirectional-flow-1.jpg)