如何在SFSafariViewWrapper中获取用户操作SafariView时变更的URL
解决方案
你当前使用的SFSafariViewWrapper是基础封装,没有配置URL变化的监听回调,我们需要针对场景改造实现,同时注意系统SFSafariViewController的公开API没有直接提供URL变更回调,下面是两种常用实现方案:
方案1:适用于OAuth授权等需获取重定向URL的场景(官方推荐)
如果你的需求是监听用户完成特定操作后的回调URL(比如登录授权后的重定向),优先使用系统提供的ASWebAuthenticationSession,不需要自行封装SafariView,系统会自动处理回调逻辑,示例代码:
import AuthenticationServices // 替换为你后台配置的重定向URL专属scheme let callbackScheme = "yourapp" struct AuthView: View { @State var session: ASWebAuthenticationSession? var body: some View { Button("打开授权页") { guard let authURL = URL(string: "https://你的授权页面地址") else { return } session = ASWebAuthenticationSession(url: authURL, callbackURLScheme: callbackScheme) { callbackURL, error in guard error == nil, let callbackURL = callbackURL else { // 可自行添加错误处理逻辑 return } // 此处直接获取到变更后的回调URL print("拿到回调URL:\(callbackURL)") } session?.presentationContextProvider = self session?.start() } } } extension AuthView: ASWebAuthenticationPresentationContextProviding { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { ASPresentationAnchor() } }
方案2:需要实时监听SafariView所有URL变化的场景
如果你确实需要监听用户在Safari弹窗内的所有页面跳转URL,可以通过KVO监听SFSafariViewController内部webView的URL属性,先改造你的SFSafariViewWrapper封装:
import SwiftUI import SafariServices struct SFSafariViewWrapper: UIViewControllerRepresentable { let url: URL // 定义URL变化的回调闭包 var onURLChange: (URL) -> Void func makeUIViewController(context: Context) -> SFSafariViewController { let safariVC = SFSafariViewController(url: url) safariVC.delegate = context.coordinator context.coordinator.safariVC = safariVC return safariVC } func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, SFSafariViewControllerDelegate { let parent: SFSafariViewWrapper var safariVC: SFSafariViewController? var urlObservation: NSKeyValueObservation? init(_ parent: SFSafariViewWrapper) { self.parent = parent super.init() // 添加KVO监听URL变更 urlObservation = safariVC?.observe(\.webView.url, options: [.new]) { [weak self] _, change in guard let newURL = change.newValue as? URL else { return } self?.parent.onURLChange(newURL) } } deinit { // 移除监听避免内存泄漏 urlObservation?.invalidate() } } }
改造后你的原有调用代码可修改为如下形式,即可拿到每次变更的URL:
Button(action: {self.showLogin = true}) { Text("OpenURL") } .sheet(isPresented: self.$showLogin) { SFSafariViewWrapper(url: URL(string: "https://google.com")!) { newURL in // 此处可拿到每次页面跳转后的新URL print("页面URL已变更:\(newURL)") } }
注意:该方案用到了SFSafariViewController的私有属性webView,若后续苹果调整私有API结构可能会失效,提交App Store审核有极小概率被拒
内容的提问来源于stack exchange,提问作者Martin Poláček
相关产品推荐
相关产品推荐

