00:16:28
MVVM (Model-View-ViewModel) has been a dominant architecture pattern for iOS apps over the past decade, praised for its ability to separate concerns in UIKit-based projects. However, with the advent of SwiftUI, developers are increasingly questioning its relevance. This article explores whether MVVM still holds up in the modern SwiftUI ecosystem or if simpler alternatives are more effective.
In the UIKit era, MVVM gained popularity due to the inherent complexity of building user interfaces. Tasks like setting up table views, handling cell dequeuing, or managing button states required substantial code within view controllers. This often led to massive, unwieldy classes dubbed "Massive View Controllers."
MVVM addressed this by decoupling the business logic (handled by the ViewModel) from the UI rendering (managed by the ViewController). This separation made code more testable and maintainable. Additionally, splitting a large view controller into smaller child view controllers in UIKit was cumbersome and error-prone, making MVVM a pragmatic choice.
SwiftUI fundamentally changes how developers build UIs. Its declarative syntax reduces the boilerplate code needed for basic operations. For instance, displaying a list with dynamic rows is as simple as using a List
view with a closure. The body
property treats views as pure functions of state, enabling elegant state management within the view itself—like using a switch
statement to handle loading, error, and loaded states.
SwiftUI’s property wrappers (@State
, @Environment
) are designed to work within views, making it natural to handle UI logic directly. Modifiers like onChange
or task(id:)
allow views to react to state changes without extra layers. This reduces the need for a ViewModel to orchestrate these interactions.
A recent article highlights several reasons to reconsider MVVM in SwiftUI:
@State
and @Environment
enable views to manage their state efficiently, reducing the ViewModel’s role.@Query
) that work best within views. Forcing a ViewModel intermediary adds unnecessary complexity.Architecture should serve your project’s goals, not dogma. For a small team or startup iterating quickly, a simple approach—where views handle UI logic and services manage business logic—may be optimal. Conversely, large teams with complex coordination needs might benefit from more structured patterns.
The key is to avoid "fighting the framework." If using MVVM in SwiftUI requires workarounds for basic tasks (e.g., adapting SwiftData queries), it introduces friction. Prioritize simplicity: only add complexity when it solves a tangible problem, such as improving testability or enabling team scalability.
SwiftUI is still evolving. Hypothetical features, like a macro to bind an observable class to a view, could reshape the debate by making ViewModels more seamless. Until then, flexibility is crucial.
MVVM remains a valid choice for SwiftUI if it aligns with your project’s needs—for example, when leveraging existing expertise or handling complex state transformations. However, for many SwiftUI apps, a simpler architecture where views directly consume models and services can be equally effective. The best approach is to stay pragmatic, regularly reassess your choices, and let your goals dictate the architecture—not vice versa.