Skip to content

Instantly share code, notes, and snippets.

@magnuskahr
Last active October 23, 2023 20:42
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save magnuskahr/1c89ade86410577a2719020f01fcfdf5 to your computer and use it in GitHub Desktop.
Save magnuskahr/1c89ade86410577a2719020f01fcfdf5 to your computer and use it in GitHub Desktop.
Cascading Action system for SwiftUI. Read more about it here: http://www.magnuskahr.dk/posts/2021/12/cascading-environment-actions-in-swiftui/
import SwiftUI
extension View {
func cascadingAction<ActionInput>(path: CascadingActionNodeModifier<ActionInput>.Path, handler: @escaping (ActionInput) -> Void) -> some View {
self.modifier(CascadingActionNodeModifier(path: path, handler: handler))
}
func cascadingAction(path: CascadingActionNodeModifier<Void>.Path, handler: @escaping () -> Void) -> some View {
self.modifier(CascadingActionNodeModifier(path: path, handler: handler))
}
}
struct CascadingActionNodeModifier<ActionInput>: ViewModifier {
typealias Path = WritableKeyPath<EnvironmentValues, CascadingAction<ActionInput>>
@Environment private var parent: CascadingAction<ActionInput>
let handler: (ActionInput) -> Void
let path: Path
init(path: Path, handler: @escaping (ActionInput) -> Void) {
self._parent = Environment(path)
self.handler = handler
self.path = path
}
func body(content: Content) -> some View {
content.environment(path, node)
}
private var node: CascadingAction<ActionInput> {
.node(parent: parent, action: handler)
}
}
indirect enum CascadingAction<Input> {
typealias Action = (Input) -> Void
case root
case node(parent: Self, action: Action)
func callAsFunction(_ input: Input) {
if case let .node(parent, action) = self {
action(input)
parent(input)
}
}
}
extension CascadingAction where Input == Void {
func callAsFunction() {
self.callAsFunction(_: ())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment