How can I run an action when a state changes?

Related searches
enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @State private var selection: Int = 0

    var body: some View {
        SegmentedControl(selection: $selection) {
            ForEach(SectionType.allCases.identified(by: \.self)) { type in
                Text(type.rawValue).tag(type)
            }
        }
    }
}

How do I run code (e.g print("Selection changed to \(selection)") when the $selection state changes? I looked through the docs and I couldn't find anything.

You can't use didSet observer on @State but you can on an ObservableObject property.

import SwiftUI
import Combine

final class SelectionStore: ObservableObject {
    var selection: SectionType = .top {
        didSet {
            print("Selection changed to \(selection)")
        }
    }

    // @Published var items = ["Jane Doe", "John Doe", "Bob"]
}

Then use it like this:

import SwiftUI

enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @ObservedObject var store = SelectionStore()

    var body: some View {
        List {
            Picker("Selection", selection: $store.selection) {
                ForEach(FeedType.allCases, id: \.self) { type in
                    Text(type.rawValue).tag(type)
                }
            }.pickerStyle(SegmentedPickerStyle())

            // ForEach(store.items) { item in
            //     Text(item)
            // }
        }
    }
}

3 Ways to React to @State Changes in SwiftUI, I quickly faced the challenge of updating an @State variable based on another @ State variable's changes. And, yes, the property observers that� Use a combination of ViewModels, the onSaveInstanceState () method, and/or persistent local storage to preserve an activity’s UI state across configuration changes. Deciding how to combine these options depends on the complexity of your UI data, use cases for your app, and consideration of speed of retrieval versus memory usage.

Here is another option if you have a component that updates a @Binding. Rather than doing this:

Component(selectedValue: self.$item, ...)

you can do this and have a little greater control:

Component(selectedValue: Binding(
    get: { self.item },
    set: { (newValue) in
              self.item = newValue
              // now do whatever you need to do once this has changed
    }), ... )

This way you get the benefits of the binding along with the detection of when the Component has changed the value.

How to run some code when state changes using onChange(), SwiftUI lets us attach an onChange() modifier to any view, which will run code of our choosing when some state changes in our program. This is� Change state​​ Instructs a parent State machine action which state it should run when the current state has finished. This action can be used within the action State Machine only. To prevent repeated loops through states in a state machine, ensure that each state branch in the state machine has this action.

In iOS 14 there is now a onChange modifier you can use like so:

SegmentedControl(selection: $selection) {
    ForEach(SectionType.allCases.identified(by: \.self)) { type in
        Text(type.rawValue).tag(type)
    }
}
.onChange(of: selection) { value in
    print("Selection changed to \(selection)")
}

Three ways to react to @State event changes in SwiftUI, In short time, I faced the challenge to update a @State variable based on another @State variable changes. And yes, the property observers� Change Management allows the Emergency and Normal change types to be reverted to the new state which is the first approval state using the Revert to New action from the Context Menu. This action is performed if the approval was requested and the submitter recognizes that not all configuration item in the scope of the change is included before submitting for approval.

iOS 13.0+

The following as an extension of Binding, so you can execute a closure whenever the value changes.

extension Binding {
    
    /// When the `Binding`'s `wrappedValue` changes, the given closure is executed.
    /// - Parameter closure: Chunk of code to execute whenever the value changes.
    /// - Returns: New `Binding`.
    func onUpdate(_ closure: @escaping () -> Void) -> Binding<Value> {
        Binding(get: {
            wrappedValue
        }, set: { newValue in
            wrappedValue = newValue
            closure()
        })
    }
}

Used like so for example:

struct ContentView: View {
    
    @State private var isLightOn = false
    
    var body: some View {
        Toggle("Light", isOn: $isLightOn.onUpdate(printInfo))
    }
    
    private func printInfo() {
        if isLightOn {
            print("Light is now on!")
        } else {
            print("Light is now off.")
        }
    }
}

This example doesn't need to use a separate function. You only need a closure.

Actions and reducers: updating state � Human Redux, In Redux all changes to the application state happen via an "action. this reducer, again based on its key const previousStateForKey = state[key] // actually run� The change to the state value is inside a closure. So although the state change is defined inside the view body, it is not actually executed while the body is computed, but when the button is pressed. So you are completely safe there and Xcode will not complain.

Not really answering your question, but here's the right way to set up SegmentedControl (didn't want to post that code as a comment, because it looks ugly). Replace your ForEach version with the following code:

ForEach(0..<SectionType.allCases.count) { index in 
    Text(SectionType.allCases[index].rawValue).tag(index)
}

Tagging views with enumeration cases or even strings makes it behave inadequately – selection doesn't work.

You might also want to add the following after the SegmentedControl declaration to ensure that selection works:

Text("Value: \(SectionType.allCases[self.selection].rawValue)")

Full version of body:

var body: some View {
    VStack {
        SegmentedControl(selection: self.selection) {
            ForEach(0..<SectionType.allCases.count) { index in
                Text(SectionType.allCases[index].rawValue).tag(index)
                }
            }

        Text("Value: \(SectionType.allCases[self.selection].rawValue)")
    }
}

Regarding your question – I tried adding didSet observer to selection, but it crashes Xcode editor and generates "Segmentation fault: 11" error when trying to build.

Actors: How to dispatch actions after Redux state changes, a change in your Redux store's state by dispatching another action? that any action dispatched by actors do not result in a new // actor run,� Select a Change State workflow action and place on the Request Approval- declined branch. In the Next State field, select Graphics Team. Select another Change State action and place on the Request Approval – approved branch. In the Next State field, select CEO Approval.

I tried your suggestion and it worked to an extent in that the relay and mute switching would alternate but only every time I held the momentary button down or released it, so as far as I can see the problem is finding a way for the statement to run once by using a variable of the switch state without repeating the statement due to the stored

In many instances, a macro should run only when a certain number of cells have values entered into them (referred to as the "key cells" in this document). In order to prevent a large macro from running every time a value is entered into a cell of a sheet, you must check to see if the ActiveCell is one of the key cells.

I want to run code whenever the user updates the state of some widgets. In my case, I have many Entry, Checkbutton, Spinbox and Radiobutton widgets. Whenever any one of these changes, I want to run my code (in this case, update a text box on the other panel).

Comments
  • Sorry for my answer before, it was incorrect. I have deleted it for now but I am looking into this and will answer as soon as I find out :D
  • Awesome! Thanks so much! :)
  • I haven't worked with this enough to say for sure - and will happily delete this comment if way off - but I tried executing a func with a print in it, even set a breakpoint, and no luck. Yet, when I put SwiftUI code (like creating a Text("got here") it worked. Keeping in mid it's all beta 1, I'm thinking this will be corrected (or at least better documented) in a later beta.
  • You might want to try using @Published decorator and subscribe to it.
  • Its the combine framework, check the videos from WWDC on Combine
  • You can also use onReceive
  • And what if I want to change another @State property in the ContentView on didSet? With your code I stuck in the instance of the SelectionStore
  • This throws runtime errors: "Modifying state during view update, this will cause undefined behavior."
  • Love this answer. This pattern has saved my butt a few times.
  • To those who want an extension of Binding for this, see below. I find it much neater to call.
  • Works like a charm.