A trigger value is an equatable value a modifier observes; when the value changes, the modifier
runs its action. It is SwiftUI's declarative way to fire an imperative side effect, used by
sensoryFeedback, scrollIndicatorsFlash, and mapCameraKeyframeAnimator.
The Pattern in the Framework
The action never runs on appearance, only on change. Toggling a boolean is a common way to fire it on demand.
List(messages, id: \.self) { message in
Text(verbatim: message)
}
.sensoryFeedback(.impact, trigger: messages)
.scrollIndicatorsFlash(trigger: messages)
Building Your Own
A custom trigger modifier is a ViewModifier with a generic Trigger: Equatable property and an
onChange in its body. Wrap it in a View extension for call-site ergonomics.
struct PlaySoundViewModifier<Trigger: Equatable>: ViewModifier {
let sound: URL
let trigger: Trigger
func body(content: Content) -> some View {
content
.onChange(of: trigger) {
if let player = try? AVAudioPlayer(contentsOf: sound) {
player.play()
}
}
}
}
extension View {
func playSound(_ sound: URL, trigger: some Equatable) -> some View {
self.modifier(PlaySoundViewModifier(sound: sound, trigger: trigger))
}
}
List(messages, id: \.self) { message in
Text(verbatim: message)
}
.playSound(
Bundle.main.url(forResource: "sound", withExtension: "wav")!,
trigger: messages
)
Triggering Cancellation
The same shape carries commands into a component. An AsyncButton can take a cancellation
trigger and cancel its stored task when the value changes.
.disabled(isRunning)
.onChange(of: cancellation) {
task?.cancel()
}
The caller just toggles a boolean state to cancel. Any equatable type works as the trigger; the value itself carries no meaning, only its change does.