Architecture Pattern  ·  iOS & Android  ·  No Dependencies

FUSE

FFeature-scoped
UUnidirectional
SState
EEngine

A unidirectional architecture pattern for native iOS & Android development.

No libraries. No dependencies. Just a clear mental model.

What is FUSE?

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.

F
Feature-scoped
Every feature owns its state, actions, and reducer in complete isolation.
U
Unidirectional
Data flows in one direction only. Action → Reducer → State → UI. Always.
S
State
A single struct or data class is the only source of truth for a feature.
E
Engine
The Store and ViewModel act as the engine routing actions and effects.

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.

Pain Point 01
MVVM has no rules
ViewModels grow into thousands of lines. State mutates from multiple places. Side effects live everywhere. Two developers implement it differently in the same codebase.
Pain Point 02
TCA is iOS only and heavy
The Composable Architecture is brilliant but requires a third-party dependency, has a steep learning curve, and has no Android equivalent. A solo developer on both platforms is left without a symmetric answer.
Pain Point 03
VIPER buries you in files
Seven files per feature before writing a single line of business logic. Protocol explosions. Indirection that makes debugging harder, not easier. Designed for large teams, punishing for small ones.
Pain Point 04
No unified mental model
iOS teams use TCA or MVVM. Android teams use MVVM or MVI. Switching context between the two codebases means switching architecture paradigms entirely. Cognitive overhead compounds daily.
Pain Point 05
Side effects are an afterthought
Most architectures give no explicit answer for where async work lives, how one-time events like navigation are handled, or how race conditions are prevented. FUSE treats effects as first-class citizens.
Pain Point 06
Testing requires too much setup
When business logic is tangled with side effects and UI concerns, testing requires mocking half the system. FUSE's pure reducer means the most important tests are also the simplest: one function call, one assertion.
MR
Muhammad Riaz
Founder · applyfuse.com  ·  iOS & Android Developer
FUSE was conceived by Riaz, a developer building native iOS and Android applications simultaneously. Frustrated by the asymmetry between platform-specific architectures and the overhead of existing patterns, he distilled years of mobile development experience into a single, symmetric mental model.

The core insight: the reducer pattern, unidirectional data flow, and explicit effect separation are not iOS ideas or Android ideas — they are engineering ideas. FUSE makes them equally at home in Swift and Kotlin, giving a solo developer or a small team one architecture to learn, one pattern to enforce, and one codebase philosophy to maintain across both platforms.

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.

Platform parity referenceEvery concept in FUSE has a named, documented counterpart in both Swift and Kotlin. No platform is a second-class citizen.
Scale from 1 to 50+ featuresThe same FUSE pattern that works in a two-screen prototype scales to a 50-feature production app without architectural rewrites.
Cover every production concernFrom offline sync and conflict resolution to A/B testing and GDPR compliance — FUSE documentation covers the full surface area of a production mobile app.
Community of mobile engineersBuild an open knowledge base where mobile developers share FUSE implementations across domains — fintech, health, productivity, IoT, and beyond.
Official example apps per domainReference implementations for e-commerce, chat, health, fintech, and IoT — each a production-grade demonstration of FUSE at scale.
Optional tooling layerLong-term, lightweight optional Swift and Kotlin packages that provide the Store and BaseViewModel as importable utilities — without locking in any opinions beyond the core pattern.

How FUSE works

One direction. One source of truth. Every interaction follows the same predictable cycle — on both platforms, in every feature.

Action
User taps, types, or the system fires an event. Always a typed value.
Reducer
A pure function. Takes state + action. Returns new state. No async.
State
A single struct or data class. The only source of truth.
UI
Reads state and renders. Sends actions. Never writes state directly.
Effect
Async work lives here. Results feed back as new actions.
01
Reducer is always pure. No async code, no network calls, no side effects inside the reducer. Ever.
02
UI only reads state. Views never mutate state directly. All changes go through send(action).
03
Effects live outside the reducer. Async work is handled in the Store or ViewModel, after the reducer runs.
04
State is the single source of truth. No local mutable variables in views that duplicate state.
05
Navigation is an Effect, not a State value. One-time events use a dedicated channel, never a stored flag.
06
Repositories are protocol-bound. Every external dependency sits behind an interface. Always mockable.

iOS & Android — same mental model

The pattern is identical on both platforms. Switch between Swift and Kotlin — the architecture never changes, only the syntax does.

AuthState.swift · iOS
// 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
    }
}
AuthState.kt · Android
// 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
}

How FUSE compares

FUSE is not the most feature-rich — it is the most appropriate for a solo developer building native iOS and Android simultaneously.

ConcernMVVMVIPERClean ArchTCA FUSE ★
Learning curveLowVery highHighVery highMedium
BoilerplateLowVery highHighHighMedium
TestabilityGoodExcellentExcellentExcellentExcellent
UDF enforcedNoNoNoYesYes
Explicit effectsAd hocAd hocAd hocYesYes
iOS + Android parityPartialPartialPartialiOS onlyNative both
No dependenciesYesYesYesRequires TCAYes
Solo dev friendlyYesNoNoNoYes
Scales to large appsStrugglesYesYesYesYes

Get started with FUSE

No package manager. No install command. Read the pattern, apply it. That is all.

01
Define your State

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.

02
Model your Actions

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.

03
Write the Reducer

A single pure function: current State + Action → new State. Use switch / when to handle every case. No async, no network calls, no side effects.

04
Add Store or ViewModel

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.

05
Build the UI

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.

06
Test the Reducer first

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.