In MVVM is it ok for your model to be an ObservableObject?

The way I'm used to to MVVM in Swift, would be to have a struct, value type model, so that it can be easily observed by the view model by just having a reference to the model, inside the view model, and annotating it with the @Published decorator, where the view model is a class.

However, what if the model is itself a class? Then @Published makes no sense anymore, unless the model itself is an observable object, that publishes changes.

Is it that an acceptable way to do MVVM? To have your model be an ObservableObject?
Because I feel the model publishing changes like that should not be, the model should just pe concerned with its logic and that's it.

Why your model is a class object? It looks odd to me.

In my experience, model should be a struct value type and has no state. If you want to maintain a state in your model, you should consider to turn your model into a viewModel.

1 Like

I'm writing an IRC Client, maybe I get things wrong, but for me, my model is an IRCSessions class, that does network stuff, connects to a channel, sends a message, etc.
I made it a class so I can share it across different views.
Repo:

ObservableObjects can form a tree/graph, that’s fine.

In original MVVM both Model and ViewModel are active objects with mutable state that notify about changes in state. The difference between them is that Model operates in terms of domain logic, while ViewModel operates in terms of presentation logic.

If you map this to SwiftUI, then your ObservableObject is a Model, your SwiftUI.View is a ViewModel. Despite being a struct it contains @State and other active fields that notify framework about changes.

And finally views are hidden inside the framework, you don’t access them directly.

1 Like

Thanks! I found a Stanford course on MVVM, CS193p, where they do have a separate VM, and the Views, are well, the struct that implement View protocol.

However, It's a bit odd, on the other hand I've heard people saying SwiftUI is newer, it should not use Microsoft's 2005 MVVM pattern, designed for WPF.

It does seem to make sense to use it however, I kind of wish Apple would step in and talk about these sort of things.

Another random note:

let session = try! IRCSession(using: createSession(to: user.selectedServer!.url))
ChatView(dispatcher: createDispatcher(using: session))

Instantiating new IRCSession is a change in state, and it should not happen in declarative code.

Declarative code be called multiple times. It is called by the framework and you have no control over when it is called. It can be triggered by state changes that you don't expect.

There are few places where change of state can happen in SwiftUI:

  • Initialiser of @StateObject (created once)
  • Initialiser of @State (created multiple times, only first instance preserved, rest are discarded)
  • Actions of Button, Menu, etc.
  • Actions of onReceive(_:, perform:), onChange(of:perform:), onPreferenceChange(_:perform:)

Better approach would be to have a custom initialiser in ChatView:

ChatView(url: user.selectedServer!.url)
...
struct ChatView: View {
    @StateObject var dispatcher: MessageDispatcher

    init(url: URL) {
        _ dispatcher = StateObject(wrappedValue: {
            let session = try! IRCSession(using: createSession(to: user.selectedServer!.url))
            return createDispatcher(using: session)
        }())
    }
...
1 Like