version Swift: 5.1+ iOS: 13.0+ macOS: 10.15+ watchOS: 6.2+
Build on Xcode SwiftPM: compatible license contact: @stleamist

BetterSafariView

一种在 SwiftUI 中呈现 SFSafariViewController 或启动 ASWebAuthenticationSession 的更好方式。

目录

动机

SwiftUI 是一种强大且直观的用户界面构建方式,但在发布时缺少一些现有元素。其中一个缺失的元素是 SFSafariViewController

幸运的是,Apple 提供了一种将 UIKit 元素包装到 SwiftUI 视图中的方法。将 SFSafariViewController 放入 SwiftUI 中的常用方法是创建一个代表 SFSafariViewController 的简单视图,然后使用 sheet(isPresented:onDismiss:content:) 修饰符或 NavigationLink 按钮来呈现它(请参阅演示项目中的 RootView.swift)。

然而,这种方法存在一个问题:它无法以默认的呈现样式(全屏推送过渡)呈现 SFSafariViewController。sheet 修饰符只能以模态 sheet 呈现视图,而 navigation link 会在顶部显示两个导航栏,因此我们必须处理它们。这归结为以下结论:除非使用 present(_:animated:completion:) 方法的 UIViewController 实例,否则无法以正确的方式呈现它,但是直接从 SwiftUI 视图访问 UIHostingController 是被禁止的,也不是一个好的设计。

BetterSafariView 通过托管一个简单的 UIViewController 来作为视图的背景呈现 SFSafariViewController,从而清晰地实现了这个目标。 通过这种方式,ASWebAuthenticationSession 也能够在 SwiftUI 中无任何问题地启动。

要求

SafariView

WebAuthenticationSession

用法

使用以下修饰符,您可以像呈现 sheet 一样使用它。

SafariView

示例

import SwiftUI
import BetterSafariView

struct ContentView: View {
    
    @State private var presentingSafariView = false
    
    var body: some View {
        Button(action: {
            self.presentingSafariView = true
        }) {
            Text("Present SafariView")
        }
        .safariView(isPresented: $presentingSafariView) {
            SafariView(
                url: URL(string: "https://github.com/")!,
                configuration: SafariView.Configuration(
                    entersReaderIfAvailable: false,
                    barCollapsingEnabled: true
                )
            )
            .preferredBarAccentColor(.clear)
            .preferredControlAccentColor(.accentColor)
            .dismissButtonStyle(.done)
        }
    }
}

View 修饰符

safariView(isPresented:onDismiss:content:)
/// Presents a Safari view when a given condition is true.
func safariView(
    isPresented: Binding<Bool>,
    onDismiss: (() -> Void)? = nil,
    content: @escaping () -> SafariView
) -> some View
safariView(item:onDismiss:content:)
/// Presents a Safari view using the given item as a data source for the `SafariView` to present.
func safariView<Item: Identifiable>(
    item: Binding<Item?>,
    onDismiss: (() -> Void)? = nil,
    content: @escaping (Item) -> SafariView
) -> some View

SafariView 初始化器

init(url:)
/// Creates a Safari view that loads the specified URL.
init(url: URL)
init(url:configuration:)
/// Creates and configures a Safari view that loads the specified URL.
init(url: URL, configuration: SafariView.Configuration)

SafariView 修饰符

preferredBarAccentColor(_:)
/// Sets the accent color for the background of the navigation bar and the toolbar.
func preferredBarAccentColor(_ color: Color?) -> SafariView
preferredControlAccentColor(_:)
/// Sets the accent color for the control buttons on the navigation bar and the toolbar.
func preferredControlAccentColor(_ color: Color?) -> SafariView
dismissButtonStyle(_:)
/// Sets the style of dismiss button to use in the navigation bar to close `SafariView`.
func dismissButtonStyle(_ style: SafariView.DismissButtonStyle) -> SafariView

WebAuthenticationSession

示例

import SwiftUI
import BetterSafariView

struct ContentView: View {
    
    @State private var startingWebAuthenticationSession = false
    
    var body: some View {
        Button(action: {
            self.startingWebAuthenticationSession = true
        }) {
            Text("Start WebAuthenticationSession")
        }
        .webAuthenticationSession(isPresented: $startingWebAuthenticationSession) {
            WebAuthenticationSession(
                url: URL(string: "https://github.com/login/oauth/authorize")!,
                callbackURLScheme: "github"
            ) { callbackURL, error in
                print(callbackURL, error)
            }
            .prefersEphemeralWebBrowserSession(false)
        }
    }
}

View 修饰符

webAuthenticationSession(isPresented:content:)
/// Starts a web authentication session when a given condition is true.
func webAuthenticationSession(
    isPresented: Binding<Bool>,
    content: @escaping () -> WebAuthenticationSession
) -> some View
webAuthenticationSession(item:content:)
/// Starts a web authentication session using the given item as a data source for the `WebAuthenticationSession` to start.
func webAuthenticationSession<Item: Identifiable>(
    item: Binding<Item?>,
    content: @escaping (Item) -> WebAuthenticationSession
) -> some View

WebAuthenticationSession 初始化器

init(url:callbackURLScheme:completionHandler:)
/// Creates a web authentication session instance.
init(
    url: URL,
    callbackURLScheme: String?,
    completionHandler: @escaping (URL?, Error?) -> Void
)
init(url:callbackURLScheme:onCompletion:)
/// Creates a web authentication session instance.
init(
    url: URL,
    callbackURLScheme: String?,
    onCompletion: @escaping (Result<URL, Error>) -> Void
)

WebAuthenticationSession 修饰符

prefersEphemeralWebBrowserSession(_:)
/// Configures whether the session should ask the browser for a private authentication session.
func prefersEphemeralWebBrowserSession(_ prefersEphemeralWebBrowserSession: Bool) -> WebAuthenticationSession

已知问题

安装

Swift Package Manager

将以下行添加到您的 Package.swift 文件中的 dependencies

.package(url: "https://github.com/stleamist/BetterSafariView.git", .upToNextMajor(from: "2.4.2"))

接下来,将 BetterSafariView 添加为您的目标的依赖项

.target(name: "MyTarget", dependencies: ["BetterSafariView"])

您完成的描述可能如下所示

// swift-tools-version:5.1

import PackageDescription

let package = Package(
    name: "MyPackage",
    dependencies: [
        .package(url: "https://github.com/stleamist/BetterSafariView.git", .upToNextMajor(from: "2.4.2"))
    ],
    targets: [
        .target(name: "MyTarget", dependencies: ["BetterSafariView"])
    ]
)

Xcode

选择 File > Swift Packages > Add Package Dependency,然后输入以下 URL

https://github.com/stleamist/BetterSafariView.git

有关更多详细信息,请参阅 Adding Package Dependencies to Your App

演示

您可以在演示项目中查看它在每个平台上的工作方式,并将其与其他朴素的实现进行比较。通过打开 BetterSafariView.xcworkspace 查看演示应用程序。

注意: 此演示项目适用于 iOS 14.0+、macOS 11.0+ 和 watchOS 7.0+,而该软件包与 iOS 13.0+、macOS 10.15+ 和 watchOS 6.2+ 兼容。

许可证

BetterSafariView 在 MIT 许可证下发布。 有关详细信息,请参阅 LICENSE