From 0020e5b1947cc0361afedab1b97b5ac822e69034 Mon Sep 17 00:00:00 2001 From: Jason Morley Date: Wed, 11 Feb 2026 11:33:33 -1000 Subject: [PATCH 1/2] feat: Add `.prompt` like view modifier for presenting alerts --- Sources/Interact/Model/Prompt.swift | 46 ++++++++++++++++++ .../Interact/Modifiers/PromptModifier.swift | 48 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 Sources/Interact/Model/Prompt.swift create mode 100644 Sources/Interact/Modifiers/PromptModifier.swift diff --git a/Sources/Interact/Model/Prompt.swift b/Sources/Interact/Model/Prompt.swift new file mode 100644 index 00000000000..b684c3ce303 --- /dev/null +++ b/Sources/Interact/Model/Prompt.swift @@ -0,0 +1,46 @@ +// Copyright (c) 2018-2026 Jason Morley +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SwiftUI + +public protocol PromptProtocol { + + associatedtype Actions: View + associatedtype Message: View + + var title: String { get } + var actions: Actions { get } + var message: Message { get } + +} + +public struct Prompt: PromptProtocol { + + public let title: String + public let actions: Actions + public let message: Message + + public init(_ title: String, @ViewBuilder actions: () -> Actions, @ViewBuilder message: () -> Message) { + self.title = title + self.actions = actions() + self.message = message() + } + +} diff --git a/Sources/Interact/Modifiers/PromptModifier.swift b/Sources/Interact/Modifiers/PromptModifier.swift new file mode 100644 index 00000000000..a5dfa0d8c5b --- /dev/null +++ b/Sources/Interact/Modifiers/PromptModifier.swift @@ -0,0 +1,48 @@ +// Copyright (c) 2018-2026 Jason Morley +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SwiftUI + +struct PromptModifier: ViewModifier { + + @Binding var item: T? + + let content: (T) -> A + + func body(content: Content) -> some View { + let alertContent = item.map(self.content) + return content.alert(alertContent?.title ?? "", + isPresented: $item.bool(), + presenting: alertContent) { alertContent in + alertContent.actions + } message: { alertContent in + alertContent.message + } + } +} + +public extension View { + + func prompt(item: Binding, + content: @escaping (T) -> A) -> some View { + self.modifier(PromptModifier(item: item, content: content)) + } + +} From 32c7bfda512cca3fef1a07a2718311db1ff2260a Mon Sep 17 00:00:00 2001 From: Jason Morley Date: Wed, 11 Feb 2026 11:35:54 -1000 Subject: [PATCH 2/2] Rename `PromptProtocol` to `Promptable` --- Sources/Interact/Model/Prompt.swift | 16 ++++++++-------- Sources/Interact/Modifiers/PromptModifier.swift | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Sources/Interact/Model/Prompt.swift b/Sources/Interact/Model/Prompt.swift index b684c3ce303..3c4d09d3d38 100644 --- a/Sources/Interact/Model/Prompt.swift +++ b/Sources/Interact/Model/Prompt.swift @@ -20,27 +20,27 @@ import SwiftUI -public protocol PromptProtocol { - +public protocol Promptable { + associatedtype Actions: View associatedtype Message: View - + var title: String { get } var actions: Actions { get } var message: Message { get } - + } -public struct Prompt: PromptProtocol { - +public struct Prompt: Promptable { + public let title: String public let actions: Actions public let message: Message - + public init(_ title: String, @ViewBuilder actions: () -> Actions, @ViewBuilder message: () -> Message) { self.title = title self.actions = actions() self.message = message() } - + } diff --git a/Sources/Interact/Modifiers/PromptModifier.swift b/Sources/Interact/Modifiers/PromptModifier.swift index a5dfa0d8c5b..896b0cc35d2 100644 --- a/Sources/Interact/Modifiers/PromptModifier.swift +++ b/Sources/Interact/Modifiers/PromptModifier.swift @@ -20,7 +20,7 @@ import SwiftUI -struct PromptModifier: ViewModifier { +struct PromptModifier: ViewModifier { @Binding var item: T? @@ -40,8 +40,8 @@ struct PromptModifier: ViewModifier { public extension View { - func prompt(item: Binding, - content: @escaping (T) -> A) -> some View { + func prompt(item: Binding, + content: @escaping (T) -> A) -> some View { self.modifier(PromptModifier(item: item, content: content)) }