Skip to content

Commit

Permalink
Add preliminary web authentication support (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
vsanthanam authored Aug 19, 2023
1 parent ab10291 commit 8a40fc3
Show file tree
Hide file tree
Showing 10 changed files with 459 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--enable isEmpty
--disable blankLinesAtEndOfScope, blankLinesAtStartOfScope, redundantNilInit, unusedArguments, redundantParens, wrapMultilineStatementBraces, trailingCommas, braces
--disable blankLinesAtEndOfScope, blankLinesAtStartOfScope, redundantNilInit, unusedArguments, redundantParens, wrapMultilineStatementBraces, trailingCommas, braces, opaqueGenericParameters
--swiftversion 5.8
--header "SafariUI\n{file}\n\nMIT License\n\nCopyright (c) 2021 Varun Santhanam\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\n\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
2 changes: 0 additions & 2 deletions Sources/SafariUI/DismissButtonStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,5 @@ public extension SafariView {
case .cancel: return .cancel
}
}

}

}
13 changes: 13 additions & 0 deletions Sources/SafariUI/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ extension EnvironmentValues {
set { self[SafariViewDismissButtonStyleEnvironmentKey.self] = newValue }
}

var webAuthenticationPrefersEphemeralWebBrowserSession: Bool {
get { self[WebAuthenticationPrefersEphemeralWebBrowserSessionEnvironmentKey.self] }
set { self[WebAuthenticationPrefersEphemeralWebBrowserSessionEnvironmentKey.self] = newValue }
}

}

private struct SafariViewEntersReaderIfAvailableEnvironmentKey: EnvironmentKey {
Expand Down Expand Up @@ -162,3 +167,11 @@ private struct SafariViewExcludedActivityTypesEnvironmentKey: EnvironmentKey {
static let defaultValue: Value = .default

}

private struct WebAuthenticationPrefersEphemeralWebBrowserSessionEnvironmentKey: EnvironmentKey {

typealias Value = Bool

static let defaultValue: Bool = false

}
28 changes: 27 additions & 1 deletion Sources/SafariUI/Modifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public extension View {
return ModifiedContent(content: self, modifier: modifier)
}

func webAuthenticationPrefersEphemeralWebBrowserSession(_ prefersEphemeralWebBrowserSession: Bool = true) -> some View {
let modifer = WebAuthenticationPrefersEphemeralWebBrowserSessionModifier(prefersEphemeralWebBrowserSession: prefersEphemeralWebBrowserSession)
return ModifiedContent(content: self, modifier: modifer)
}

}

private struct SafariViewEntersReaderIfAvailableModifier: ViewModifier {
Expand Down Expand Up @@ -343,7 +348,7 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier {
self.activityTypes = activityTypes
}

// MARK: - ViewBuilder
// MARK: - ViewModifier

@ViewBuilder
func body(content: Content) -> some View {
Expand All @@ -356,3 +361,24 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier {
private let activityTypes: SafariView.ExcludedActivityTypes

}

private struct WebAuthenticationPrefersEphemeralWebBrowserSessionModifier: ViewModifier {

// MARK: - Initializers

init(prefersEphemeralWebBrowserSession: Bool) {
self.prefersEphemeralWebBrowserSession = prefersEphemeralWebBrowserSession
}

// MARK: - ViewModifier

@ViewBuilder
func body(content: Content) -> some View {
content
.environment(\.webAuthenticationPrefersEphemeralWebBrowserSession, prefersEphemeralWebBrowserSession)
}

// MARK: - Private

private let prefersEphemeralWebBrowserSession: Bool
}
37 changes: 33 additions & 4 deletions Sources/SafariUI/Presentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,17 @@ public extension View {
/// - onDismiss: The closure to execute when dismissing the ``SafariView``
/// - safariView: A closure that returns the ``SafariView`` to present
/// - Returns: The modified view
func safari<Item>(
func safari<Item, Identifier>(
item: Binding<Item?>,
id: KeyPath<Item, some Hashable>,
id: KeyPath<Item, Identifier>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder safariView: @escaping (Item) -> SafariView
) -> some View {
) -> some View where Identifier: Hashable {
let modifier = SafariView.ItemModifier(
item: item,
id: id,
onDismiss: onDismiss,
safariView: safariView
build: safariView
)
return ModifiedContent(content: self, modifier: modifier)
}
Expand Down Expand Up @@ -274,3 +274,32 @@ public extension View {
)
}
}

public extension View {

func webAuthentication(
_ isPresented: Binding<Bool>,
webAuthentication: @escaping () -> WebAuthentication
) -> some View {
let modifier = WebAuthentication.BoolModifier(isPresented: isPresented, build: webAuthentication)
return ModifiedContent(content: self, modifier: modifier)
}

func webAuthentication<Item>(
_ item: Binding<Item?>,
webAuthentication: @escaping (Item) -> WebAuthentication
) -> some View where Item: Identifiable {
let modifier = WebAuthentication.IdentifiableItemModitifer(item: item, build: webAuthentication)
return ModifiedContent(content: self, modifier: modifier)
}

func webAuthentication<Item, Identifier>(
_ item: Binding<Item?>,
id: KeyPath<Item, Identifier>,
webAuthentication: @escaping (Item) -> WebAuthentication
) -> some View where Identifier: Hashable {
let modifier = WebAuthentication.ItemModifier(item: item, id: id, build: webAuthentication)
return ModifiedContent(content: self, modifier: modifier)
}

}
8 changes: 8 additions & 0 deletions Sources/SafariUI/SafariUI.docc/SafariUI.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ SafariServices in SwiftUI
### Views

- ``SafariView``

### Structures

- ``WebAuthentication``

### View Modifiers

- ``SwiftUI/View``
4 changes: 0 additions & 4 deletions Sources/SafariUI/SafariUI.docc/SafariView.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ You can also use sheet presentation, or any other presentation mechanism of your
- ``init(url:activityButton:onInitialLoad:onInitialRedirect:onOpenInBrowser:)``
- ``init(url:activityButton:eventAttribution:onInitialLoad:onInitialRedirect:onOpenInBrowser:)``

### View Modifiers

- ``SwiftUI/View``

### Appearance

- ``DismissButtonStyle``
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
# ``SwiftUI/View``

SwiftUI view modifiers used to configure a ``SafariView``
SwiftUI view modifiers used to configure a ``SafariView`` or a ``WebAuthentication``

## Topics

### Configuration
### SafariView Configuration

- ``SwiftUI/View/safariEntersReaderIfAvailable(_:)``
- ``SwiftUI/View/safariBarCollapsingEnabled(_:)``

### Appearance
### SafariView Appearance

- ``SwiftUI/View/safariBarTintColor(_:)``
- ``SwiftUI/View/safariControlTintColor(_:)``
- ``SwiftUI/View/safariDismissButtonStyle(_:)``

### Presentation
### SafariView Presentation

- ``SwiftUI/View/safari(isPresented:onDismiss:safariView:)``
- ``SwiftUI/View/safari(url:onDismiss:safariView:)``
- ``SwiftUI/View/safari(item:onDismiss:safariView:)``
- ``SwiftUI/View/safari(item:id:onDismiss:safariView:)``

### Custom Activities
### SafarView Custom Activities

- ``SwiftUI/View/includedSafariActivities(_:)-1yaml``
- ``SwiftUI/View/includedSafariActivities(_:)-7buso``
- ``SwiftUI/View/excludedSafariActivityTypes(_:)-5sf78``
- ``SwiftUI/View/excludedSafariActivityTypes(_:)-4omxw``

### WebAuthentication Configuration

- ``SwiftUI/View/webAuthenticationPrefersEphemeralWebBrowserSession(_:)``

### WebAuthentication Presentation

- ``SwiftUI/View/webAuthentication(_:webAuthentication:)-5m8qc``
- ``SwiftUI/View/webAuthentication(_:webAuthentication:)-9e7q7``
- ``SwiftUI/View/webAuthentication(_:id:webAuthentication:)``
59 changes: 19 additions & 40 deletions Sources/SafariUI/SafariView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,69 +637,48 @@ public struct SafariView: View {

struct ItemModifier<Item, Identifier>: ViewModifier where Identifier: Hashable {

// MARK: - Initializers
@Binding
var item: Item?

init(
item: Binding<Item?>,
id: KeyPath<Item, Identifier>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder safariView: @escaping (Item) -> SafariView
) {
self.item = item
self.id = id
self.onDismiss = onDismiss
self.safariView = safariView
}
let id: KeyPath<Item, Identifier>
let onDismiss: (() -> Void)?
let build: (Item) -> SafariView

// MARK: - ViewModifier

@ViewBuilder
func body(content: Content) -> some View {
content
.safari(item: binding, onDismiss: onDismiss) { wrappedItem in
safariView(wrappedItem.item)
.safari(item: wrapped, onDismiss: onDismiss) { item in
build(item.wrapped)
}
}

// MARK: - Private

private let item: Binding<Item?>
private let id: KeyPath<Item, Identifier>
private let onDismiss: (() -> Void)?
private let safariView: (Item) -> SafariView
private struct WrappedItem: Identifiable {
let wrapped: Item
let path: KeyPath<Item, Identifier>
var id: Identifier { wrapped[keyPath: path] }
}

private var binding: Binding<WrappedItem?> {
private var wrapped: Binding<WrappedItem?> {
Binding<WrappedItem?> {
guard let item = item.wrappedValue else {
return nil
}
return WrappedItem(item, id)
item.map(wrap)
} set: { newValue in
item.wrappedValue = newValue?.item
item = newValue?.wrapped
}
}

private struct WrappedItem: Identifiable {
init(_ item: Item,
_ keyPath: KeyPath<Item, Identifier>) {
self.item = item
self.keyPath = keyPath
}

let item: Item
private let keyPath: KeyPath<Item, Identifier>

typealias ID = Identifier

var id: ID {
item[keyPath: keyPath]
}
private func wrap(_ item: Item) -> WrappedItem {
.init(wrapped: item, path: id)
}

}

}

private extension UIView {
extension UIView {

var controller: UIViewController? {
if let nextResponder = next as? UIViewController {
Expand Down
Loading

0 comments on commit 8a40fc3

Please sign in to comment.