swiftswiftui

How to let SwiftUI View know that ViewModel's computed property has changed?


Imagine you have ViewModel which computed property may change from time to time:

import Foundation

class ViewModel: ObservableObject {
    private var val = 42;
    
    var compProp: Int {
        return val
    }

    func maybeChange() {
        if (Int.random(in: 0..<2) == 1) {
            val += 1
            ???
            //heyViewThePropertyIsChanged(nameof(compProp))
        }
    }
}

How can I force the View:

import SwiftUI

struct ContentView: View {
    @StateObject var viewModel: ViewModel
    var body: some View {
        VStack {
            Text("\(viewModel.compProp)").font(.title)
        }
        .frame(minWidth: 320)
        .toolbar {
            Button(action: viewModel.maybeChange) {
                Label("Maybe", systemImage: "gear.badge.questionmark")
            }
        }.padding()
    }
}

to reflect such a change?


Solution

  • Each ObservableObject offers a publisher via objectWillChange for this purpose.

    class ViewModel: ObservableObject {
        private var val = 42;
        
        var compProp: Int {
            return val
        }
    
        func maybeChange() {
            if (Int.random(in: 0..<2) == 1) {
                objectWillChange.send()
                val += 1
            }
        }
    }
    

    This recreates the same behavior that you get with a Published property wrapper.

    I'll anticipate the question of why “willChange” and not “didChange”: Data Essentials in SwiftUI