Shield配置通知点击后跳转至指定SwiftUI视图问题求助
问题:点击Shield通知无法跳转到指定SwiftUI视图
我已经完成了大部分功能开发:当预选中应用的Shield配置中主按钮被点击时,用户会收到通知。但目前点击通知仅会跳转到MainView(),我希望能跳转到指定的SwiftUIView()。我尝试修改应用的info.plist,但没有效果。
以下是当前代码:
ContentView.swift
import SwiftUI import FamilyControls import ManagedSettings import Combine struct ContentView: View { @State private var isAuthorized = UserDefaults.standard.bool(forKey: "HasRequestedScreenTimeAuthorization") @State private var navigateToSwiftUIView = false @State private var selectedApp: ApplicationToken? private let selectedAppKey = "SelectedAppToken" var body: some View { NavigationView { VStack { if isAuthorized { if navigateToSwiftUIView, let appToPause = selectedApp { SwiftUIView(appToPause: appToPause) } else { MainView() } } else { AuthorizationRequestView(isAuthorized: $isAuthorized) } } .onAppear { NotificationCenter.default.addObserver(forName: .navigateToSwiftUIView, object: nil, queue: .main) { _ in if let data = UserDefaults.standard.data(forKey: selectedAppKey), let appToken = try? JSONDecoder().decode(ApplicationToken.self, from: data) { selectedApp = appToken navigateToSwiftUIView = true } } } .onOpenURL { url in if url.absoluteString == "test://navigateToSwiftUIView" { if let data = UserDefaults.standard.data(forKey: selectedAppKey), let appToken = try? JSONDecoder().decode(ApplicationToken.self, from: data) { selectedApp = appToken navigateToSwiftUIView = true } } } } } }
App入口与AppDelegate
import SwiftUI import UserNotifications @main struct YourApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self return true } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // Trigger URL to navigate to SwiftUIView when notification is tapped if response.notification.request.content.userInfo["navigateTo"] as? String == "SwiftUIView" { UIApplication.shared.open(URL(string: "test://navigateToSwiftUIView")!) } completionHandler() } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { // Post notification for navigation in ContentView if url.absoluteString == "test://navigateToSwiftUIView" { NotificationCenter.default.post(name: .navigateToSwiftUIView, object: nil) return true } return false } } extension Notification.Name { static let navigateToSwiftUIView = Notification.Name("navigateToSwiftUIView") }
ShieldActionExtension.swift
import ManagedSettings import UserNotifications class ShieldActionExtension: ShieldActionDelegate { private let selectedAppKey = "SelectedAppToken" override func handle(action: ShieldAction, for application: ApplicationToken, completionHandler: @escaping (ShieldActionResponse) -> Void) { switch action { case .primaryButtonPressed: saveApplicationToken(application) sendShieldNotification() completionHandler(.defer) case .secondaryButtonPressed: completionHandler(.defer) @unknown default: fatalError("Unknown action") } } private func saveApplicationToken(_ application: ApplicationToken) { // Encode the application token and save to UserDefaults if let data = try? JSONEncoder().encode(application) { UserDefaults.standard.set(data, forKey: selectedAppKey) } } private func sendShieldNotification() { let content = UNMutableNotificationContent() content.title = "Shield Alert" content.body = "Access attempt detected." content.userInfo = ["navigateTo": "SwiftUIView"] let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) { error in if let error = error { print("Failed to add notification: \(error.localizedDescription)") } else { print("Notification scheduled successfully.") } } } }
解决方案
1. 确认URL Scheme配置无误
先检查info.plist的URL Scheme是否正确添加:
- 添加
URL types数组,新增一项 - 设置
URL Schemes为test(对应代码中的test://协议) - 填写
URL Identifier为唯一标识(例如com.yourcompany.yourapp)
2. 简化导航逻辑(去掉冗余的URL跳转)
当前流程绕了URL跳转+通知的弯路,直接在通知响应回调中处理导航更可靠:
修改AppDelegate的通知响应方法:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { if response.notification.request.content.userInfo["navigateTo"] as? String == "SwiftUIView" { let selectedAppKey = "SelectedAppToken" // 直接读取保存的AppToken并通过通知传递 if let data = UserDefaults.standard.data(forKey: selectedAppKey), let appToken = try? JSONDecoder().decode(ApplicationToken.self, from: data) { NotificationCenter.default.post(name: .navigateToSwiftUIView, object: appToken) } } completionHandler() }
更新ContentView的通知监听逻辑:
.onAppear { NotificationCenter.default.addObserver(forName: .navigateToSwiftUIView, object: nil, queue: .main) { notification in // 直接从通知对象获取AppToken if let appToken = notification.object as? ApplicationToken { selectedApp = appToken navigateToSwiftUIView = true } } }
之后可以移除AppDelegate中的application(_:open:options:)方法,以及ContentView中的onOpenURL回调,减少不必要的环节。
3. 确保ApplicationToken支持Codable
系统的ApplicationToken默认可能不遵循Codable,需要手动扩展:
extension ApplicationToken: Codable { enum CodingKeys: String, CodingKey { case bundleID = "bundleIdentifier" // 根据需求添加其他需要编码的属性 } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(bundleIdentifier, forKey: .bundleID) // 编码其他属性 } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let bundleID = try container.decode(String.self, forKey: .bundleID) self.init(bundleIdentifier: bundleID) // 初始化其他属性 } }
4. 测试验证
- 测试应用在后台/关闭状态下的通知点击
- 打印
UserDefaults中保存的ApplicationToken数据,确认编码解码正常 - 在通知响应回调中添加日志,确认代码执行路径
内容的提问来源于stack exchange,提问作者David Johansen
相关产品推荐
相关产品推荐

