TinyRedux is a small-footprint library, strongly inspired by ReduxJS, written in pure Swift.
TinyRedux offers a significant improvement over traditional MVC and MVVM architectures.
Store centralizes global state management and removes the need to pass data across multiple ViewModels, which can become a heavy task when evolving consolidated logic.
TinyRedux adopts a Supervised Redux Model where middleware, resolver, and reducer cooperate in the same dispatch flow with distinct responsibilities:
-
Middlewareorchestrates async operations and side effects across the app. -
Resolversupervises errors raised during that flow and applies remediation strategies before the action continues. -
Reducerapplies deterministic state transitions.
This separation keeps the architecture clean and aligns with SOLID principles thanks to composable abstractions for middleware, resolver, and store processing.
Example of declaration
import Observation
import TinyRedux
@MainActor
@Observable
final class AppState: ReduxState {
@MainActor
final class ReadOnlyAppState: ReduxReadOnlyState {
private unowned let state: AppState
init(_ state: AppState) {
self.state = state
}
var counter: Int { state.counter }
}
var counter: Int
@ObservationIgnored
lazy var readOnly = ReadOnlyAppState(self)
init(
counter: Int
) {
self.counter = counter
}
convenience init() {
self.init(
counter: 0
)
}
}Example of declaration
import TinyRedux
@CaseID
enum AppActions: ReduxAction {
case inc(Int)
case dec(Int)
var description: String {
switch self {
case .inc: ".inc"
case .dec: ".dec"
}
}
var debugDescription: String {
switch self {
case .inc(let value): ".inc by \(value) step."
default:
description
}
}
}Example of declaration
import TinyRedux
let testMiddleware = AnyMiddleware<AppState, AppActions>(id: "testMiddleware") { context in
let (_, dispatch, action) = context.args
switch action {
case .inc(let value):
return .task { state in
try await Task.sleep(nanoseconds: 60 * 60 * 1_000_000_000)
dispatch(0, .dec(value))
}
case .dec:
return .next
default:
return .defaultNext
}
}Example of declaration
import TinyRedux
let testResolver = AnyResolver<AppState, AppActions>(id: "testResolver") { context in
let (state, dispatch, error, origin, action) = context.args
switch (error, action) {
case (_, .inc):
return .next
case (_, .dec):
return .next
default:
return .defaultNext
}
}Example of declaration
import TinyRedux
let testReducer = AnyReducer<AppState, AppActions>(id: "testReducer") { context in
let (state, action) = context.args
switch action {
case .inc(let value):
state.counter += value
return .next
case .dec(let value):
state.counter -= value
return .next
}
}Example of declaration
import TinyRedux
let store = Store.main
extension Store where State == AppState, Action == AppActions {
static let main = Store<AppState, AppActions>(
initialState: AppState(),
middlewares: [testMiddleware],
resolvers: [testResolver],
reducers: [testReducer]
)
}