A unidirectional architecture pattern for native iOS & Android development.
No libraries. No dependencies. Just a clear mental model.
FUSE stands for Feature-scoped Unidirectional State Engine — a lightweight architecture pattern that brings predictable, testable, and scalable state management to native iOS and Android simultaneously.
FUSE was born from a specific frustration: building native iOS and Android apps simultaneously as a solo developer, while existing architectures were either too heavy, platform-specific, or left too many decisions unanswered.
FUSE aims to be the definitive architecture reference for native mobile teams who want scalable, testable, and maintainable codebases without committing to a third-party framework.
One direction. One source of truth. Every interaction follows the same predictable cycle — on both platforms, in every feature.
send(action).The pattern is identical on both platforms. Switch between Swift and Kotlin — the architecture never changes, only the syntax does.
// State — value type, Equatable struct AuthState: Equatable { var email: String = "" var password: String = "" var user: User? = nil var isLoading: Bool = false var errorMessage: String? = nil // Derived — computed, not stored var isLoggedIn: Bool { user != nil } var canSubmit: Bool { !email.isEmpty && !password.isEmpty && !isLoading } }
// State — data class, immutable copy data class AuthState( val email: String = "", val password: String = "", val user: User? = null, val isLoading: Boolean = false, val errorMessage: String? = null ) { // Derived — computed, not stored val isLoggedIn get() = user != null val canSubmit get() = email.isNotBlank() && password.isNotBlank() && !isLoading }
FUSE is not the most feature-rich — it is the most appropriate for a solo developer building native iOS and Android simultaneously.
| Concern | MVVM | VIPER | Clean Arch | TCA | FUSE ★ |
|---|---|---|---|---|---|
| Learning curve | Low | Very high | High | Very high | Medium |
| Boilerplate | Low | Very high | High | High | Medium |
| Testability | Good | Excellent | Excellent | Excellent | Excellent |
| UDF enforced | No | No | No | Yes | Yes |
| Explicit effects | Ad hoc | Ad hoc | Ad hoc | Yes | Yes |
| iOS + Android parity | Partial | Partial | Partial | iOS only | Native both |
| No dependencies | Yes | Yes | Yes | Requires TCA | Yes |
| Solo dev friendly | Yes | No | No | No | Yes |
| Scales to large apps | Struggles | Yes | Yes | Yes | Yes |
No package manager. No install command. Read the pattern, apply it. That is all.
Create a struct (iOS) or data class (Android) that holds all data your feature needs. Keep it flat. Add computed derived properties. Every field should have a default so the initial state requires zero setup.
Write an enum (iOS) or sealed class (Android) listing every possible event in the feature — user intents and system responses. Actions are the complete vocabulary of your feature.
A single pure function: current State + Action → new State. Use switch / when to handle every case. No async, no network calls, no side effects.
Copy the generic Store (iOS) or BaseViewModel (Android) once into your project. This is the only shared infrastructure FUSE needs. It owns state, calls the reducer, and handles async effects.
Your SwiftUI View or Composable reads state and calls store.send() / vm.send() on interactions. Never write to state directly. The view renders state and reports actions — nothing else.
Write unit tests for the reducer before anything else. Pure function in, pure function out. No mocks, no async needed. These are the fastest, most valuable tests in your entire codebase.