Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stop using MVVM for SwiftUI

Stop using MVVM for SwiftUI

Don’t over-engineering! No suggested architecture for SwiftUI, just MVC without the C. On SwiftUI you get extra (or wrong) work and complexity for no benefits. Don’t fight the system.

Gustavo Arranhado

January 25, 2022
Tweet

Other Decks in Programming

Transcript

  1. From Apple No suggested architecture for SwiftUI, just MVC without

    the C Related to Flux architecture but can applies to Redux, MVVM, … Do we really need MVVM? No ViewControllers means no ViewModels too Evolution MVC (imperative) MVVM (binding, pre-reactive) MV (declarative, post-reactive)
  2. Demystify Model Data & Logic -> Objects, OOP / POP

    In the Apple system this is the model layer In the Apple system this is the UI layer UIKit (UIViews, UIViewControllers), SwiftUI (Views) C in MVC is just a bridge VM in MVVM is just a bridge Presentation logic M in MVC / MVVM represent the objects from software modelling and design Model design patterns across Apple platforms: Objective-C (Table Data Gateway) Swift (Active Record)
  3. Product Shopping Cart ProductList ProductView Model-View (MV) Data & Logic

    Natural way in SwiftUI, a big improvement over the other reactive / declarative platforms Note: On the other platforms (e.g. Microsoft) you can’t do that, a bridge object (VM) is required ObservableObject ✓ In app, you store and process data by using a data model that is separate from its UI ✓ Adopt the ObservableObject protocol for model classes ✓ Use ObservableObject where you need to manage the life cycle of your data ✓ Typically, the ObservableObject is part of your model
  4. import SwiftUI // Model (Data & Logic), OOP / POP

    struct Product { func purchase() async { } static var all: [Product] { get async } } // Views (UI) struct ProductList: View { @State private var products: [Product] = [] @State private var search: String = "" var body: some View { VStack { // Product list // User can search products and open product detail } .task { products = await Product.all } } } struct ProductView: View { let product: Product var body: some View { VStack { // Product detail // User can purchase product } } }
  5. Product Shopping Cart ProductList ProductView ProductView VM ProductList VM Model-View-ViewModel

    (MVVM) legacy Data & Logic Bridge This is how the data binding works on Microsoft (WPF/Silverlight) platforms Can't use GestureState, FocusState, Environment, FetchRequest, ... Changing one property will invalidate the VM at all MVVM was invented by Microsoft architects Ken Cooper and Ted Peters speci f ically to simplify event-driven programming of user interfaces. The pattern was incorporated into Windows Presentation Foundation (WPF) (Microsoft's .NET graphics system) and Silverlight (WPF's Internet application derivative). John Gossman, one of Microsoft's WPF and Silverlight architects, announced MVVM on his blog in 2005. On SwiftUI you get extra work and complexity for no bene f its
  6. import SwiftUI // Model (Data & Logic), OOP / POP

    struct Product { func purchase() async { } static var all: [Product] { get async } } // ViewModels (UI) class ProductListVM: ObservableObject { @Published var products: [Product] = [] @Published var search: String = "" // Can't use GestureState, FocusState, Environment, FetchRequest, ... // Changing one property will invalidate the object at all func loadProducts() async { products = await Product.all } } class ProductViewVM: ObservableObject { let product: Product func purchaseProduct() { } } // Views (UI) struct ProductList: View { @StateObject private var viewModel = ProductListVM() var body: some View { VStack { // Product list // User can search products and open product detail } .task { async viewModel.loadProducts() } } } struct ProductView: View { @StateObject var viewModel: ProductViewVM var body: some View { VStack { // Product detail // User can purchase product } } }
  7. Product Shopping CartItem ProductList ProductView ProductDetail VM Products VM Model-View-ViewModel

    (MVVM) other Data (POJOs) Logic & Bridge ShoppingCart VM You can’t do that on Microsoft (WPF/Silverlight) platforms but it’s seen on other platforms (e.g. Android) Incorrect MVVM architecture In this case VM is really needed because the Model is just POJOs (wrong approach and anti-OOP) ObservableObject is part of your model - Use if / where needed - Not a ViewModel (from MVVM) - Not part of another layer
  8. import SwiftUI // Model (Data), POJOs struct Product { }

    // ViewModels (UI) class ProductsVM: ObservableObject { @Published var products: [Product] = [] func loadProducts() async { } } class ProductDetailVM: ObservableObject { let product: Product func purchaseProduct() { } } // Views (UI) struct ProductList: View { @StateObject private var productsVM = ProductsVM() @State private var search: String = "" var body: some View { VStack { // Product list // User can search products and open product detail } .task { async productsVM.loadProducts() } } } struct ProductView: View { @StateObject var viewModel: ProductDetailVM var body: some View { VStack { // Product detail // User can purchase product } } }
  9. import SwiftUI // Views (UI) struct ProductList: View { @State

    private var products: [Product] = [] @State private var search: String = "" var body: some View { VStack { // Product list // User can search products and open product detail } .task { products = await Product.all } } } import SwiftUI // ViewModels (UI) class ProductListVM: ObservableObject { @Published var products: [Product] = [] @Published var search: String = "" // Can't use GestureState, FocusState, Environment, FetchRequest, ... // Changing one property will invalidate the object at all func loadProducts() async { products = await Product.all } } // Views (UI) struct ProductList: View { @StateObject private var viewModel = ProductListVM() var body: some View { VStack { // Product list // User can search products and open product detail } .task { async viewModel.loadProducts() } } } MVVM MV SwiftUI is modern, simple and high composable / componentable. No need to split View-State; no middle man bene f its Keep It Simple! Don’t f ight the system Don’t ignore the potential of a platform Don’t over-engineering