The one architecture rule that keeps my solo iOS apps from rotting
I ship iOS apps solo. No code reviewer, no teammate catching me cut corners at 1am. The codebase only stays sane if I enforce structure on myself, because nobody else is going to.
The rule that saves me: views do not touch data.
My basic split:
- Views handle layout and taps. If a view is deciding how data gets saved, I moved too much into the view.
- ViewModels own screen state. I keep them @Observable and @MainActor, because SwiftUI already gives me enough ways to hurt myself.
- Services do the work: SwiftData, network calls, file access, anything that changes the outside world.
- Models stay boring. Data only.
I use it as a fence. Future me needs fewer places to look.
When a screen breaks, I know where to start. When I change storage, the UI does not need surgery. And when I let an AI agent work on a service, it has less room to wander into seven other files.
I learned this by doing the opposite. I let views query SwiftData directly in an early app and it turned into spaghetti. Worked fine until it didn't. Then every small change felt like pulling a wire out of a wall and hoping the lights stayed on.
Curious how other solo SwiftUI devs handle this. Do you keep the layers this strict, go heavier with something like TCA, or let small apps stay messy.