skip to Main Content

I’m attempting to separate my model and view by adding a view model as a bridge. When I do this in the following code, clicking on the ‘Increase Score’ button no longer triggers a view update. It works if I connect the view directly to the model. Any suggestions on how to get this to work?

import SwiftUI

@main
struct MVVM_Test_Watch_AppApp: App {
    @StateObject var viewModel = ViewModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: viewModel)
        }
    }
}

class Model {
    var score = 0
}

class ViewModel: ObservableObject {
    @Published private var model: Model
    
    init() {
        model = Model()
    }
    var score: Int {
        model.score
    }
    func incScore() {
        model.score += 1
    }
}

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        VStack {
            Text("Your score is (viewModel.score)")
            Button("Increase Score") {
                viewModel.incScore()
            }
        }
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    According to this migration page, there's a new way to handle observation. I updated and tested my code.

    import SwiftUI
    
    @main
    struct MVVM_Test_Watch_AppApp: App {
        @State var viewModel = ViewModel()
        
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .environment(viewModel)
            }
        }
    }
    
    @Observable
    class Model {
        var score = 0
    }
    
    @Observable
    class ViewModel {
        private var model: Model
        
        init() {
            model = Model()
        }
        var score: Int {
            model.score
        }
        func incScore() {
            model.score += 1
        }
    
    }
    
    struct ContentView: View {
        @Environment(ViewModel.self) private var viewModel
    
        
        var body: some View {
            VStack {
                Text("Your score is (viewModel.score)")
                Button("Increase Score") {
                    viewModel.incScore()
                }
            }
        }
    }
    

  2. Typically, in models within SwiftUI, struct is used, and while this might not be the exact cause of the issue you’re encountering.

    In your code, a minimal change to detect updates and refresh the view would be to add objectWillChange.send() to the function. Here’s a sample code for reference:

    import SwiftUI
    
    @main
    struct MVVM_Test_Watch_AppApp: App {
        @StateObject var viewModel = ViewModel()
        
        var body: some Scene {
            WindowGroup {
                ContentView(viewModel: viewModel)
            }
        }
    }
    
    class Model {
        var score = 0
    }
    
    class ViewModel: ObservableObject {
        @Published private var model: Model
        
        init() {
            model = Model()
        }
        var score: Int {
            model.score
        }
        func incScore() {
            model.score += 1
    
            objectWillChange.send()
    
        }
    }
    
    struct ContentView: View {
        @ObservedObject var viewModel: ViewModel
        
        var body: some View {
            VStack {
                Text("Your score is (viewModel.score)")
                Button("Increase Score") {
                    viewModel.incScore()
                }
            }
        }
    }
    

    Or you can consider using @Published in the following way:

     class ViewModel: ObservableObject {
        private var model: Model
        @Published var score: Int
    
        init() {
            model = Model()
            score = model.score
        }
    
        func incScore() {
            model.score += 1
            score = model.score
            
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search