Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

swiftui - Does Combine have publishers that only publish when a value actually changes?

Is there a way to make a @Published variable that only publishes its value when the new value is different from the old one?

Right now if we have

@Published var test: Bool = false

and we do

test = false
test = false
test = false

the publisher is called 3 times. This is quite annoying as it sometimes causes my SwiftUI Views to be recreated because somewhere higher up in the hierarchy a publisher was set to the value it previously was set, trough another publisher that was triggered (and that destroys text field inputs because view models through the hierarchy are recreated when that happens).

is there a way of only publishing when it goes from false to true or vice versa?

An example scenario:

We create a user object in our app, and we want to add a car to the user. The app should immediately show the "add car" view if no car is found, otherwise show the main application view. For that we have a listener somewhere. On our top level view we have:


    @ObservedObject var viewModel = UserViewModel()


    var body: some View {
            if !viewModel.hasVehicles {
                return AnyView(AddVehicleView(viewModel:AddVehicleViewModel())
            } else {
                return AnyView(UserMainView(user: user))
            }
        }
    }

in our UserViewModel we have

class UserViewModel: ObservableObject {
    @Published var hasVehicles: Bool = false

  • some code that updates that boolean when certain listeners trigger.

Inside AddVehicleView we have a form that allows the user to fill out some text fields and save the vehicle.

Now imagine that for some reason the code that updates the hasVehicles property is triggered, but there still are no vehicles. What happens:

hasVehicles = false

and the top level view is re-evaluated, resulting in return AnyView(AddVehicleView(viewModel:AddVehicleViewModel()) being executed, and my form with text fields is emptied.

I suppose in this case I could solve it by putting AddVehicleViewModel() as a property inside the View struct, but that wouldn't solve it in the case when we want this to be executed multiple times, as that would mean next time the view gets built it will show the data of the last time we created that view, as we reuse the view model.

question from:https://stackoverflow.com/questions/65905731/does-combine-have-publishers-that-only-publish-when-a-value-actually-changes

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Try to use regular property with manual publisher activation, like

class UserViewModel: ObservableObject {
    var hasVehicles: Bool = false {
        willSet {
            if hasVehicles != newValue {
                objectWillChange.send()
            }
        }
    }
// ... other code
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...