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

问题描述 投票:0回答:3

我正在尝试在我的应用程序中设置深层链接。我已经设置了应用程序以及推送通知。但是,我无法完成 AppDelegate 接收用户单击我想要深层链接到的屏幕的通知的最终链接。我基本上想从 AppDelegate 调用 viewRouter.goToFred() 。我在这里设置了一个 git 存储库 (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()
    }

}

swift swiftui apple-push-notifications appdelegate
3个回答
6
投票

我尝试了这个方法并且有效。诚然,我不知道这是否是正确的或建议的方式。

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

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()
}

0
投票

Apple 的文档

有很详细的说明

您所要做的就是在 AppDelegate 中点击

userNotificationCenter(_:didReceive:withCompletionHandler:)
中的通知时调用您想要运行的方法


0
投票

我正在使用不同的模式来完成非常相似的事情,这不需要我将环境对象公开给应用程序委托。在我们的例子中,我们有一个应用程序状态容器对象,而不是路由器,但最终它的工作方式非常相似。

我发现这很重要,因为更改 appState 上的值需要这些更改在

App
的上下文中发生。如果我从
path
中更新
appState
上的
AppDelegate
属性,则会导致注入的
appState
对象实例上的值与通过
appState
修饰符提供的引用
.environmentObject
对象之间的连接断开。这导致视图接收下游的状态更改,但它们无法将更新发送回上游。

AppDelegate
添加已发布的属性

final class AppDelegate: NSObject, UIApplicationDelegate {

    @Published var userNotificationPath: String?

    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        let userInfo = response.notification.request.content.userInfo
        userNotificationPath = userInfo["path"] as? String

        ...
    }
}

App
监控属性的发布情况,并更新环境对象
appState

struct MyApp: App {

    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    @StateObject var appState = AppStateContainer()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environmentObject(appState)
                .onReceive(appDelegate.$userNotificationPath) { path in
                  appState.path = path
                  appDelegate.userNotificationPath = nil
                }
        }
    }
}

在任何需要响应的视图中监控

appState

struct SomeChildView: View {

  @EnvironmentObject private var appState: AppState
  
  var Body: some View {
    View()
      .onReceive(appState.$path) { path in
         ...handle business
      }
  }

© www.soinside.com 2019 - 2024. All rights reserved.