No artigo sobre TCA(The Composable Architecture), abordamos o fluxo de dados unidirecional. Na verdade, a cena iOS coreana já contava com um framework que havia tornado o unidirecional um padrão: o ReactorKit.
Publicado por Suyeol Jeon em 2017, esse framework tornou-se a arquitetura de fato para a combinação UIKit + RxSwift após ser adotado pela StyleShare e por serviços ligados à Kakao. Houve até uma época em que “experiência com ReactorKit é um diferencial” aparecia com frequência nas vagas.
Hoje, vamos revisar a estrutura do ReactorKit, suas diferenças em relação ao TCA e sua posição atual.
View e Reactor: são apenas dois papéis
A estrutura do ReactorKit é simples. Cada tela é dividida em View e Reactor.
- View: envia a entrada do usuário para
Actione assinaStatepara renderizar a tela. Não contém lógica. - Reactor: um bloco de lógica que recebe Actions e produz State. Ele não conhece a UI.
O ponto central é que o fluxo de dados dentro do Reactor fica fixo em uma única direção.
Action → mutate() → Mutation → reduce() → State → View
Quando um toque no botão entra como Action.refresh, mutate() trata efeitos colaterais, como requisições de rede, e emite Mutation.setItems([...]). reduce() recebe essa Mutation e calcula um novo State. A View assina esse State e atualiza a tela.
Por que a etapa intermediária Mutation é necessária?
Se você conhece MVVM(Model-View-ViewModel), pode pensar: “Por que não alterar o State diretamente a partir da Action?”. A Mutation existe para isolar o trabalho assíncrono.
mutate() é o único lugar onde operações assíncronas são permitidas. Já reduce() é uma função pura: com o mesmo State e Mutation, sempre produz o mesmo resultado. Como os efeitos colaterais ficam confinados a um só lugar, também há um único ponto para investigar ao rastrear por que o estado mudou.
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
}
}
Reactor é lógica pura que não conhece a UI, então os testes são simples. Envie uma Action e verifique o State.
Qual é a diferença em relação ao TCA?
Ambos são arquiteturas unidirecionais influenciadas pelo Redux, mas diferem na unidade de aplicação.
| ReactorKit | TCA | |
|---|---|---|
| Base | RxSwift | Combine·Swift Concurrency |
| Unidade de aplicação | Um Reactor por tela (View) | Composição de Reducers por funcionalidade em uma árvore |
| Forma de adoção | Introdução gradual, uma tela por vez | Estruturação do app inteiro como um único sistema |
| Principal contexto | UIKit | SwiftUI |
A praticidade do ReactorKit vem da forma de adoção. Em um projeto MVC(Model-View-Controller) existente, você pode escolher uma única tela e adicionar um Reactor. Não é preciso reescrever o app inteiro. Já o TCA gerencia também a composição de estado entre funcionalidades, mas exige adotar o sistema como um todo.
Também existiu uma biblioteca chamada ReSwift, uma adaptação direta do Redux para iOS, mas o modelo de store único em todo o app era difícil de combinar com UIKit e não se espalhou tanto quanto o ReactorKit.
O ReactorKit nos dias de hoje
Sendo sincero, menos projetos novos estão escolhendo o ReactorKit. Suas limitações ficaram mais evidentes.
Primeiro, ele é fortemente acoplado ao RxSwift. Com a migração do ecossistema da Apple para Combine e Swift Concurrency, a própria dependência do RxSwift virou um peso.
Segundo, ele não combina bem com SwiftUI. O protocolo View e o modelo de binding foram projetados com base nas atualizações imperativas do UIKit.
Ainda assim, o legado do ReactorKit é claro. Foi o primeiro framework a transmitir aos desenvolvedores iOS coreanos a noção de que “o estado flui em uma única direção” e “os efeitos colaterais devem ser isolados em um único lugar”. Para quem está aprendendo TCA agora, a estrutura Action·Mutation·State provavelmente não parece estranha; boa parte dessa intuição foi formada na era do ReactorKit.
Se você mantém uma base de código existente com UIKit + RxSwift, o ReactorKit continua sendo uma escolha comprovada. Como ele é integrado e removido por tela, migrar depois, retirando-o tela a tela, também é relativamente simples.
Resumo
- ReactorKit é uma arquitetura baseada em RxSwift que divide cada tela entre View e Reactor e impõe o fluxo unidirecional Action → Mutation → State.
- O trabalho assíncrono é isolado em
mutate(), e o cálculo puro do estado emreduce(). Por isso, rastrear e testar é fácil. - Os conceitos são iguais aos do TCA, mas seu diferencial é a adoção gradual e prática por tela.
- A adoção em projetos novos diminuiu por causa do acoplamento ao RxSwift e da incompatibilidade com SwiftUI, mas ele continua válido em código legado de UIKit.
No próximo artigo, vamos abordar o RIBs da Uber, que divide o app por unidades de lógica de negócio, e não por telas. Vamos analisar de verdade a arquitetura que foi mencionada em apenas um parágrafo no artigo sobre VIPER.

![Imagem de capa de [Arquitetura iOS #9] Resumo do ReactorKit: o padrão de arquitetura unidirecional](/assets/images/posts/317cfd0d-2a59-49f9-9bce-a85ec25ae593/reactorkit-unidirectional-flow-1.jpg)