How can I trigger an action when a swiftUI toggle() is toggled?

iOS 14+

If you're using iOS 14 and higher you can use onChange:

struct ContentView: View {
    @State private var isDisplayed = false
    
    var body: some View {
        Toggle("", isOn: $isDisplayed)
            .onChange(of: isDisplayed) { value in
                // action...
                print(value)
            }
    }
}

The cleanest approach in my opinion is to use a custom binding. With that you have full control when the toggle should actually switch

import SwiftUI

struct ToggleDemo: View {
    @State private var isToggled = false

    var body: some View {

        let binding = Binding(
            get: { self.isToggled },
            set: {
                potentialAsyncFunction($0)
            }
        )

        func potentialAsyncFunction(_ newState: Bool) {
            //something async
            self.isToggled = newState
        }

        return Toggle("My state", isOn: binding)
   }
}

iOS13+

Here is a more generic approach you can apply to any Binding for almost all built in Views like Pickers, Textfields, Toggle..

extension Binding {
    func didSet(execute: @escaping (Value) -> Void) -> Binding {
        return Binding(
            get: { self.wrappedValue },
            set: {
                self.wrappedValue = $0
                execute($0)
            }
        )
    }
}

And usage is simply;

@State var isOn: Bool = false
Toggle("Title", isOn: $isOn.didSet { (state) in
   print(state)
})

iOS14+

@State private var isOn = false

var body: some View {
    Toggle("Title", isOn: $isOn)
        .onChange(of: isOn) { _isOn in
            /// use _isOn here..
        }
}

Here is a version without using tapGesture.

@State private var isDisplayed = false
Toggle("", isOn: $isDisplayed)
   .onReceive([self.isDisplayed].publisher.first()) { (value) in
        print("New value is: \(value)")           
   }