Revolve is a light-weight library for building applications with a robust and composable architecture. It is a Redux-based and leverages the power of the Combine framework to streamline your app's state management and UI development. Designed specifically for SwiftUI, Revolve makes creating efficient, maintainable, and enjoyable applications easier than ever.
- What is Revolve?
- Installation
- Examples
- [Infrastructure] (#infrastructure)
- Basic usage
- Documentation
Revolve is a modern architecture library for SwiftUI apps, which leverages Redux and Combine to manage your app's state, actions, and side effects. The library enables you to build clean and maintainable applications, making it easier to test, scale, and reason about your code.
- ๐ฆ Swift Package Manager support
- ๐ Composable reducers and state
- ๐ช Type-safe actions using enums
- ๐งช Easily testable with CombineSchedulers
Add the following dependency to your Package.swift
:
.package(url: "https://github.com/Handasatic/Revolve.git", from: "1.0.0")
We provide a simple example of how to use Revolve:
- Basic Example - A simple example demonstrating the core concepts of Revolve.
- Advanced Example - Coming soon
A Reducer
in Revolve is a pure function that takes an inout state and an action and applies the action to the state. It has the following signature:
public typealias Reducer<State: Revolve.State, Action: Revolve.Action> = (inout State, Action) -> Void
Revolve provides a combine
function that allows you to combine multiple reducers into a single reducer. This ensures you can easily compose reducers that handle different parts of the state:
public func combine<State, Action>(
reducers: Reducer<State, Action>...
) -> Reducer<State, Action> where State: Revolve.State, Action: Revolve.Action {
return { state, action in
for reducer in reducers {
reducer(&state, action)
}
}
}
Here's a quick example to get you started with Revolve:
Create a struct conforming to Revolve.State
to define your app's state. This struct should contain all the necessary properties to represent your app's current state.
struct AppState: Revolve.State {
var user: UserState
var settings: SettingsState
}
struct UserState: Revolve.State {
var name: String
var age: Int
}
struct SettingsState: Revolve.State {
var notificationsEnabled: Bool
var darkModeEnabled: Bool
}
Define an enum conforming to Revolve.Action to represent the different actions that can be performed in your app. For a more organized codebase, you can group actions by the state they affect.
eenum AppAction: Revolve.Action {
case user(UserAction)
case settings(SettingsAction)
}
enum UserAction: Revolve.Action {
case setName(String)
case setAge(Int)
}
enum SettingsAction: Revolve.Action {
case setNotificationsEnabled(Bool)
case setDarkModeEnabled(Bool)
}
Create a reducer function that takes the current state and an action as its arguments and returns an updated state. To handle state composition, you can create separate reducers for each state struct and use a top-level reducer to delegate actions to their respective reducers.
let appReducer: Reducer<AppState, AppAction> = { state, action in
switch action {
case let .user(userAction):
userReducer(state: &state.user, action: userAction)
case let .settings(settingsAction):
settingsReducer(state: &state.settings, action: settingsAction)
}
}
let userReducer: Reducer<UserState, UserAction> = { state, action in
switch action {
case let .setName(name):
state.name = name
case let .setAge(age):
state.age = age
}
}
let settingsReducer: Reducer<SettingsState, SettingsAction> = { state, action in
switch action {
case let .setNotificationsEnabled(enabled):
state.notificationsEnabled = enabled
case let .setDarkModeEnabled(enabled):
state.darkModeEnabled = enabled
}
}
Instantiate a StateStore with the initial state, reducer, and a scheduler.
let store = StateStore(initialState: AppState(user: UserState(name: "", age: 0),
settings: SettingsState(notificationsEnabled: true,
darkModeEnabled: false)),
reducer: appReducer,
scheduler: DispatchQueue.main.eraseToAnyScheduler())
Then, inject the StateStore into your SwiftUI views.