Skip to content

Commit

Permalink
Add additional presentation modifier for SafariView
Browse files Browse the repository at this point in the history
  • Loading branch information
vsanthanam committed Mar 23, 2024
1 parent d05f53d commit bdd0ca4
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 67 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/nicklockwood/SwiftFormat",
"state" : {
"revision" : "507d63f6e890621f055397c9503bfc9468e8bcb6",
"version" : "0.51.15"
"revision" : "9e5d0d588ab6e271fe9887ec3dde21d544d4b080",
"version" : "0.53.5"
}
}
],
Expand Down
1 change: 1 addition & 0 deletions Sources/SafariUI/SafariUI.docc/SafariView.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ You can present a `SafariView` using the built-in presentation view modifiers:
- ``SwiftUI/View/safari(isPresented:url:onDismiss:)``
- ``SwiftUI/View/safari(item:onDismiss:safariView:)``
- ``SwiftUI/View/safari(item:id:onDismiss:safariView:)``
- ``SwiftUI/View/safari(url:onDismiss:)``

You can also use sheet presentation, or any other presentation mechanism of your choice.

Expand Down
1 change: 1 addition & 0 deletions Sources/SafariUI/SafariUI.docc/View.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ SwiftUI view modifiers used to configure a ``SafariView`` or a ``WebAuthenticati
- ``SwiftUI/View/safari(isPresented:url:onDismiss:)``
- ``SwiftUI/View/safari(item:onDismiss:safariView:)``
- ``SwiftUI/View/safari(item:id:onDismiss:safariView:)``
- ``SwiftUI/View/safari(url:onDismiss:)``

### SafarView Custom Activities

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,62 +86,6 @@ public extension View {
)
}

/// Presents a ``SafariView`` when a binding to a Boolean value that you provide is `true`.
///
/// Use this method when you want to present a ``SafariView`` to the user when a Boolean value you provide is true.
/// The example below displays a modal view of the mockup for a software license agreement when the user toggles the `isShowingSafari` variable by clicking or tapping on the “Show License Agreement” button:
///
/// ```swift
/// import Foundation
/// import SafariView
/// import SwiftUI
///
/// struct ShowLicenseAgreement: View {
///
/// let licenseAgreementURL: URL
///
/// @State private var isShowingSafari = false
///
/// var body: some View {
/// Button {
/// isShowingSafari.toggle()
/// } label: {
/// Text("Show License Agreement")
/// }
/// .safari(isPresented: $isShowingSafari,
/// url: licenseAgreementURL
/// onDismiss: didDismiss)
/// }
///
/// func didDismiss() {
/// // Handle the dismissing action.
/// }
///
/// }
/// ```
///
/// - Parameters:
/// - isPresented: A binding to a Boolean value that determines whether to present the ``SafariView`` that you create in the modifier’s content closure.
/// - url: The URL to load in the presented ``SafariView``
/// - onDismiss: The closure to execute when dismissing the ``SafariView``
/// - Returns: The modified view
func safari(
isPresented: Binding<Bool>,
url: URL,
onDismiss: (() -> Void)? = nil
) -> some View {
ModifiedContent(
content: self,
modifier: IsPresentedModifier(
isPresented: isPresented,
safariView: SafariView(
url: url
),
onDismiss: onDismiss
)
)
}

}

@available(iOS 14.0, macCatalyst 14.0, *)
Expand Down
118 changes: 118 additions & 0 deletions Sources/SafariView/Presentation/BoolURLPresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SafariUI
// BoolURLPresentation.swift
//
// MIT License
//
// Copyright (c) 2023 Varun Santhanam
// 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 Foundation
import SwiftUI

@available(iOS 14.0, macCatalyst 14.0, *)
public extension View {

/// Presents a ``SafariView`` when a binding to a Boolean value that you provide is `true`.
///
/// Use this method when you want to present a ``SafariView`` to the user when a Boolean value you provide is true.
/// The example below displays a modal view of the mockup for a software license agreement when the user toggles the `isShowingSafari` variable by clicking or tapping on the “Show License Agreement” button:
///
/// ```swift
/// import Foundation
/// import SafariView
/// import SwiftUI
///
/// struct ShowLicenseAgreement: View {
///
/// let licenseAgreementURL: URL
///
/// @State private var isShowingSafari = false
///
/// var body: some View {
/// Button {
/// isShowingSafari.toggle()
/// } label: {
/// Text("Show License Agreement")
/// }
/// .safari(isPresented: $isShowingSafari,
/// url: licenseAgreementURL
/// onDismiss: didDismiss)
/// }
///
/// func didDismiss() {
/// // Handle the dismissing action.
/// }
///
/// }
/// ```
///
/// - Parameters:
/// - isPresented: A binding to a Boolean value that determines whether to present the ``SafariView`` that you create in the modifier’s content closure.
/// - url: The URL to load in the presented ``SafariView``
/// - onDismiss: The closure to execute when dismissing the ``SafariView``
/// - Returns: The modified view
func safari(
isPresented: Binding<Bool>,
url: URL,
onDismiss: (() -> Void)? = nil
) -> some View {
ModifiedContent(
content: self,
modifier: BoolURLPresentation(
isPresented: isPresented,
onDismiss: onDismiss,
url: url
)
)
}

}

@available(iOS 14.0, macCatalyst 14.0, *)
private struct BoolURLPresentation: ViewModifier {

init(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)?,
url: URL
) {
_isPresented = isPresented
self.onDismiss = onDismiss
self.url = url
}

@MainActor
@ViewBuilder
func body(content: Content) -> some View {
content
.safari(
isPresented: $isPresented,
onDismiss: onDismiss
) {
SafariView(url: url)
}
}

@Binding
private var isPresented: Bool
private let onDismiss: (() -> Void)?
private let url: URL

}
File renamed without changes.
109 changes: 109 additions & 0 deletions Sources/SafariView/Presentation/URLPresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SafariUI
// URLPresentation.swift
//
// MIT License
//
// Copyright (c) 2023 Varun Santhanam
// 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 Foundation
import SwiftUI

@available(iOS 14.0, macCatalyst 14.0, *)
public extension View {

/// Presents a ``SafariView`` using the given URL.
///
/// Use this method when you need to present a ``SafariView`` with content from URL.
/// The example below shows a list of buttons, each of which provide a different URL to the view.
///
/// ```swift
/// struct InformationView: View {
///
/// @State var url: URL?
///
/// var body: some View {
/// VStack {
/// Button("Terms of service") {
/// url = URL(string: "https://www.myservice.com/terms-of-service")!
/// }
/// Button("Warranty") {
/// url = URL(string: "https://www.myservice.com/warranty")!
/// }
/// Button("Support") {
/// ` url = URL(string: "https://www.myservice.com/help")!
/// }
/// }
/// .safari(url: $url, onDismiss: dismissAction)
/// }
///
/// func didDismiss() {
/// // Handle the dismissing action.
/// }
/// }
/// ```
/// - Parameters:
/// - url: The URL used to load the view
/// - onDismiss: The closure to execute when dismissing the ``SafariView``
/// - Returns: The modified view
func safari(
url: Binding<URL?>,
onDismiss: (() -> Void)? = nil
) -> some View {
ModifiedContent(
content: self,
modifier: URLPresentation(
url: url,
onDismiss: onDismiss
)
)
}

}

@available(iOS 14.0, macCatalyst 14.0, *)
private struct URLPresentation: ViewModifier {

init(
url: Binding<URL?>,
onDismiss: (() -> Void)?
) {
_url = url
self.onDismiss = onDismiss
}

@MainActor
@ViewBuilder
func body(content: Content) -> some View {
content
.safari(
item: $url,
id: \.hashValue,
onDismiss: onDismiss
) { url in
SafariView(url: url)
}
}

@Binding
private var url: URL?
private let onDismiss: (() -> Void)?

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ public extension View {
/// import SafariView
/// import SwiftUI
///
/// struct InventoryItem: Identifiable {
/// let id: Int
/// struct InventoryItem {
/// let title: String
/// let url: URL
/// }
Expand All @@ -56,14 +55,15 @@ public extension View {
/// @State private var selectedItem: InventoryItem?
///
/// var body: some View {
/// List(inventory) { inventoryItem in
/// List(inventory, id: \.title) { inventoryItem in
/// Button(action: {
/// self.selectedItem = inventoryItem
/// }) {
/// Text(inventoryItem.title)
/// }
/// }
/// .safari(item: $selectedItem,
/// id: \.title
/// onDismiss: dismissAction) { item in
/// SafariView(url: item.url)
/// }
Expand Down Expand Up @@ -138,7 +138,7 @@ private struct WrappedItemPresentation<Item, ID>: ViewModifier where ID: Hashabl
private let onDismiss: (() -> Void)?
private let safariView: (Item) -> SafariView

private var wrappedItem: Binding<WrappedIdentifiable<Item, ID>?> {
private var wrappedItem: Binding<WrappedIdentifiable?> {
.init {
if let item {
.init(value: item, path: id)
Expand All @@ -150,18 +150,18 @@ private struct WrappedItemPresentation<Item, ID>: ViewModifier where ID: Hashabl
}
}

struct WrappedIdentifiable<Wrapped, ID>: Identifiable where ID: Hashable {
struct WrappedIdentifiable: Identifiable {

init(
value: Wrapped,
path: KeyPath<Wrapped, ID>
value: Item,
path: KeyPath<Item, ID>
) {
self.value = value
self.path = path
}

let value: Wrapped
let path: KeyPath<Wrapped, ID>
let value: Item
let path: KeyPath<Item, ID>

var id: ID {
value[keyPath: path]
Expand Down

0 comments on commit bdd0ca4

Please sign in to comment.