Home > Net >  SwiftUI: When to use @State vs @Published
SwiftUI: When to use @State vs @Published

Time:11-19

I feel like this is a question that would have already been asked somewhere, but I can't find much on it.

When using a variable for the purpose of updating the UI, when/why would we use @State within our view as opposed to using @Published within a ViewModel?

This is in the context of me trying to grasp MVVM architecture. I understand the difference generally, just not when it comes to something that both could easily accomplish the same way.

Below, I have 2 examples that do the same thing, but one uses @State while the other uses @Published and a ViewModel. Is one approach better than the other (for updating the UI purposes?)

@State example:

struct MyView: View {
    @State var backgroundIsRed = false

    var body: some View {
        ZStack {
            if backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { backgroundIsRed.toggle() }

    }
}

@Published example:

class ViewModel: ObservableObject {
    @Published var backgroundIsRed = false
}

struct MyView: View {

    @StateObject var viewModel = ViewModel()

    var body: some View {
        ZStack {
            if viewModel.backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { viewModel.backgroundIsRed.toggle() }
    }
}

CodePudding user response:

For example, I would say that the "Published" approach helps you create a test structure for your VM.

Taking your example, you could create a protocol:

protocol ViewModelProtocol {
   var backgroundIsRed: Bool { get }
   var date: Date { get } // Created for example purposes
}

And then:

class ViewModel: ViewModelProtocol, ObservableObject {
    @Published var backgroundIsRed = false
    @Published var date = Date()
}

class ViewModelMock: ViewModelProtocol, ObservableObject {
    @Published var backgroundIsRed = true
    @Published var date = Mock.Date
}

struct MyView: View {
    @StateObject var viewModel: ViewModelProtocol = ViewModel()
    //@StateObject var viewModel: ViewModelProtocol = ViewModelMock()

    var body: some View {
        ZStack {
            if viewModel.backgroundIsRed {
                Color.red
            } else {
                Color.green
            }
        }
        .onTapGesture { viewModel.backgroundIsRed.toggle() }
    }
}

On the other hand, the State approach presents a more straightforward way to implement the logic.

Apart from that, there isn't any particular reason to think that one is better than the other. Hope this helps you choose which one you should use in each situation.

CodePudding user response:

We don't need MVVM in SwiftUI because the View struct is already the view model, i.e. it holds the data that SwiftUI uses to create/update/remove the actual UIView objects on screen. If you use actual objects to do this job then you add unnecessary layer of indirection and will get the kind of consistency bugs that SwiftUI's clever use of value semantics was designed to eliminate.

It's best to think of @StateObject as @State but for when you need a reference type, e.g. you want to asynchronously load/save/sync data. Which is not very often nowadays given we have the much more powerful .task modifier.

If your aim is to group related vars and have testable logic simply use @State var with a custom struct which is much simpler than getting a @StateObject implementation correct. However, you must learn mutating func for your logic first. Also, if you want to access @Environment inside the custom struct you need to learn DynamicProperty.

  • Related