如何在 SwiftUI 中从通知深层链接到屏幕?

Posted

技术标签:

【中文标题】如何在 SwiftUI 中从通知深层链接到屏幕?【英文标题】:How can I deep link from a notification to a screen in SwiftUI? 【发布时间】:2021-06-26 17:21:32 【问题描述】:

我正在尝试在我的应用中设置深层链接。我已经设置了应用程序以及推送通知。但是,我无法完成 AppDelegate 接收用户点击通知的最终链接,该链接指向我想要深度链接到的屏幕。我基本上想从 AppDelegate 调用 viewRouter.goToFred() 。我在这里设置了一个 git repo (https://github.com/cameronhenige/SwiftUITestDeepLink),其中有一个应用程序,除了最后一块之外,所有的东西都设置好了。有人可以帮我解决这个问题吗?以下是相关的代码片段。谢谢!



import SwiftUI

struct MainView: View 
    
    @EnvironmentObject var viewRouter: ViewRouter

    var body: some View 
        NavigationView 

            List 

            ForEach(viewRouter.pets)  pet in
                NavigationLink(
                    destination: PetView(),
                    tag: pet,
                    selection: $viewRouter.selectedPet,
                    label: 
                        Text(pet.name)
                    
                )
            
                Button("Send Notification to Fred") 
                    viewRouter.sendNotification()
                
                
                Button("Manually go to Fred") 
                    viewRouter.goToFred()
                
            
            

        
    


struct MainView_Previews: PreviewProvider 
    static var previews: some View 
        MainView()
    




import SwiftUI

@main
struct ContentView: App 
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene 
        WindowGroup 
            MainView().environmentObject(ViewRouter())

        


    



import Foundation
import UserNotifications

class ViewRouter: ObservableObject 
    @Published var selectedPet: Pet? = nil
    @Published var pets: [Pet] = [Pet(name: "Louie"), Pet(name: "Fred"), Pet(name: "Stanley")]
    
    
    func goToFred() 
        self.selectedPet = pets[1]
    
    
    func sendNotification() 
        let content = UNMutableNotificationContent()
        let categoryIdentifire = "Notification Type"
        
        content.title = "Go To Fred"
        content.body = "Click me to go to Fred."
        content.sound = UNNotificationSound.default
        content.badge = 1
        content.categoryIdentifier = categoryIdentifire
        
        let request = UNNotificationRequest(identifier: "identifier", content: content, trigger: nil)
        UNUserNotificationCenter.current().add(request)  (error) in
            if let error = error 
                print("Error \(error.localizedDescription)")
            
        
        
    
    




import SwiftUI

struct PetView: View 
    
    @EnvironmentObject var viewRouter: ViewRouter

    var body: some View 
        
        if let pet = viewRouter.selectedPet 
            Text(pet.name)
         else 
            EmptyView()
        
    


struct PetView_Previews: PreviewProvider 
    static var previews: some View 
        PetView()
    




import Foundation

struct Pet: Identifiable, Hashable 
    var name: String
    var id: String  name 





import Foundation
import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate 
    
    let notificationCenter = UNUserNotificationCenter.current()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        
    
        if #available(ios 10.0, *) 
          // For iOS 10 display notification (sent via APNS)
          UNUserNotificationCenter.current().delegate = self

          let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
          UNUserNotificationCenter.current().requestAuthorization(
            options: authOptions,
            completionHandler: _, _ in )
         else 
          let settings: UIUserNotificationSettings =
          UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
          application.registerUserNotificationSettings(settings)
        

        application.registerForRemoteNotifications()
        
        return true
    



extension AppDelegate: UNUserNotificationCenterDelegate 
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
      withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 
      let userInfo = notification.request.content.userInfo
        print("Notification created")

      // Change this to your preferred presentation option
      completionHandler([[.alert, .sound]])
    

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) 
      let userInfo = response.notification.request.content.userInfo
        
        print("user clicked on notification. Now take them to Fred.")
        //todo Somehow I want to call viewRouter.goToFred(), but I don't have access to that object in AppDelegate
        
      completionHandler()
    



【问题讨论】:

【参考方案1】:

我尝试了这种方法,它很有效。诚然,我不知道这是正确的还是建议的方式。

appDelegate 必须引用 MainView 正在使用的同一 ViewRouter 实例,并且 ContentView 必须将 appDelegateViewRouter 实例挂钩。

ContentView.swift:

@main
struct ContentView: App 
    
    let router = ViewRouter()
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
  
    var body: some Scene 
        WindowGroup 
            MainView()
                .environmentObject(router)
                .onAppear(perform:setUpAppDelegate)
        
    
    
    func setUpAppDelegate()
        appDelegate.router = router
    

AppDelegate.swift:

weak var router: ViewRouter?
func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) 
  let userInfo = response.notification.request.content.userInfo
    
    print("user clicked on notification. Now take them to Fred.")
    //todo Update the ViewRouter to
    router?.goToFred()
    
    completionHandler()

【讨论】:

谢谢!我猜这可能是推荐的这样做方式,因为它看起来很优雅。我希望 Apple 能够提供更多 SwiftUI 示例,展示此类事物的最佳实践。【参考方案2】:

Apple's documentation里写的很详细

您所要做的就是在AppDelegate中点击userNotificationCenter(_:didReceive:withCompletionHandler:)中的通知时调用您要运行的方法

【讨论】:

我得到了那个部分。但是,如果我将“@EnvironmentObject var viewRouter: ViewRouter”放在 AppDelegate 中,XCode 会抛出错误“未知属性 'EnvironmentObject'”。所以,问题是如果我不能在 AppDelegate 中实例化我想要调用的方法,我该如何调用它?

以上是关于如何在 SwiftUI 中从通知深层链接到屏幕?的主要内容,如果未能解决你的问题,请参考以下文章

在发出 OAuth 令牌交换请求时,如何进行深层链接并导航到“LogInLoading”屏幕?

轻按即可从 SwiftUI 小部件执行深层链接

IOS推送通知深度链接?

SwiftUI 工作表中未触发深层链接 onChange

如何将我的登录屏幕链接到我的主屏幕 SwiftUI

在创建 branch.io 深层链接时添加应用程序版本检查