extension View { public func alert( _ title: String, isPresented: Binding, @AlertActionsBuilder actions: () -> [AlertAction] ) -> some View { AlertModifierView( child: self, title: title, isPresented: isPresented, actions: actions() ) } public func alert( _ title: Binding, @AlertActionsBuilder actions: () -> [AlertAction] ) -> some View { AlertModifierView( child: self, title: title.wrappedValue ?? "", isPresented: Binding { title.wrappedValue != nil } set: { newValue in if !newValue { title.wrappedValue = nil } }, actions: actions() ) } } struct AlertModifierView: TypeSafeView { typealias Children = AlertModifierViewChildren var body = EmptyView() var child: Child var title: String var isPresented: Binding var actions: [AlertAction] func children( backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) -> Children { AlertModifierViewChildren( childNode: AnyViewGraphNode( ViewGraphNode( for: child, backend: backend, environment: environment ) ), alert: nil ) } func asWidget( _ children: Children, backend: Backend ) -> Backend.Widget { children.childNode.widget.into() } func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { children.childNode.computeLayout( with: child, proposedSize: proposedSize, environment: environment ) } func commit( _: Backend.Widget, children: AlertModifierViewChildren, layout: ViewLayoutResult, environment: EnvironmentValues, backend: Backend ) { func castBackend_inner(_ backend: NewBackend) { _ = children.childNode.commit() if isPresented.wrappedValue && children.alert == nil { let alert = backend.createAlert() backend.updateAlert( alert, title: title, actionLabels: actions.map(\.label), environment: environment ) backend.showAlert( alert, window: .some(environment.window! as! NewBackend.Window) ) { responseId in children.alert = nil isPresented.wrappedValue = false actions[responseId].action() } children.alert = alert } else if isPresented.wrappedValue == false && children.alert != nil { backend.dismissAlert( children.alert as! NewBackend.Alert, window: .some(environment.window! as! NewBackend.Window) ) children.alert = nil } } guard let castedBackend = backend as? any (BaseAppBackend & BackendFeatures.Alerts) else { fatalError("Backend does not implement BackendFeatures.Alerts") } castBackend_inner(castedBackend) } } class AlertModifierViewChildren: ViewGraphNodeChildren { var childNode: AnyViewGraphNode var alert: Any? var widgets: [AnyWidget] { [childNode.widget] } var erasedNodes: [ErasedViewGraphNode] { [ErasedViewGraphNode(wrapping: childNode)] } init( childNode: AnyViewGraphNode, alert: Any? ) { self.childNode = childNode self.alert = alert } }