Folders named Views, Models, and Services work until each contains dozens of unrelated files. Understanding one feature then requires searching the entire project.
I organize SwiftUI code around product capabilities:
Features/Workout/
Store/
Views/
Components/
Domain/
Data/
Store owns state transitions and effect coordination. Views render state and send actions. Domain contains pure decisions. Data implements network, persistence, and cache boundaries.
Shared code must earn its place in Core. Networking primitives and typed errors are cross-cutting. A workout formatter still belongs to Workout even if another screen happens to reuse it.
The dependency direction matters more than folder names:
View → Feature → Client boundary → API / SwiftData / cache
Views never perform storage or network work. Reducers do not construct concrete services. Feature clients hide implementation choices while keeping available operations explicit.
I also avoid creating every directory for a small feature. A simple screen may need one feature file and one view. Structure should reveal real complexity, not predict imaginary complexity.
Feature-first organization improves deletion and ownership. Removing a product capability has a visible boundary, and opening its folder shows the complete flow. SwiftUI supplies the rendering system; a growing product still needs a map for where decisions belong.