SwiftUI 处理在终止状态下点击的推送通知

发布于 01-10 22:39 字数 5389 浏览 2 评论 0原文

我有一个具有聊天功能的 SwiftUI 应用程序,它使用 Firebase 作为后端,并使用 Cloud Function 来侦听新的聊天消息并通知用户。当用户在相应的聊天室之一收到新的聊天消息时,云功能向登录用户的所有设备发送通知。为了导航,我创建了一个NotificationManager,如果用户点击了通知,它可以帮助应用程序在应用程序的嵌套视图中导航。

一切正常,但是当用户在应用程序终止后点击通知时,导航不起作用。我猜这是因为在用户恢复之前导航被点击并且其属性被更改......?我试图寻找与 UIKit 相关的解决方案(因为我没有找到任何与 SwiftUI 相关的解决方案),但还没有找到合适的解决方案。

在我的 AppDelegate 中:

weak var notificationManager: NotificationManager?

用户点击通知:

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID from userNotificationCenter didReceive: \(messageID)")
        }
        
        print("*** NotificationInfo: \(userInfo) ***")
        let info = userInfo as NSDictionary
        guard let chatID = info.value(forKey: "chatID") as? String else { return } // retrieving the ChatID from notification payload
        
        
        // Navigate to the room of the chatID when chat notification is tapped
        
        
        notificationManager?.pageToNavigationTo = 1 // navigate to messages view
        notificationManager?.recievedNotificationFromChatID = chatID // navigate to correct chat
        
        
        completionHandler()
    }

生命周期:

@main
struct VitaliiApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    @StateObject var session = SessionStore()
    let notificationManager = NotificationManager()
    
    func setUpdNotificationManager() {
        appDelegate.notificationManager = notificationManager
    }
    
    var body: some Scene {
        WindowGroup {
            
            ContentView()
                .environmentObject(session)
                .environmentObject(notificationManager)
                .onAppear {
                    setUpdNotificationManager()
                }   
        }
       
        
    }
}

NotificationManager:

class NotificationManager: ObservableObject {
    static let shared = NotificationManager()
    @Published var pageToNavigationTo : Int?
    @Published var recievedNotificationFromChatID: String?
}

ContentView 的 LightWeight 示例< /强>

struct ContentView: View {
    
    @State var selection: Int = 0
    @EnvironmentObject var session: SessionStore
    
    @State var activeChatID: String?
    
    let tabBarImageNames = ["person.3", "message", "person"]
    
    @EnvironmentObject private var notificationManager: NotificationManager
    

        
    var body: some View {
        
        ZStack {
            Color.black
                .ignoresSafeArea()
            
            VStack {
                
                ZStack {
                    
                    switch selection {
                    case 0:
                         NavigationView {
                            HomeView()
                        }                        
                    case 1:
                        NavigationView {
                            InboxMessagesView(user: session.userSession, activeChatID: $activeChatID)
                            
                        }
                        .accentColor(.white)
                   
                    default:
                        NavigationView {
                            ProfileView(session: session.userSession)
                        }
 
                    }
                    
                }
                
                Spacer()
                
                
                    HStack {
                        
                        ForEach(0..<3) { num in
                            Button {
                                selection = num
                            } label: {
                                Spacer()
                                
                                Image(systemName: tabBarImageNames[num])
                                    .padding(.top, 10)
                                    .font(.system(size: 20, weight: .medium))
                                    .foregroundColor(selection == num ? .red : .white.opacity(0.7))
                                Spacer()
                            }
                            
                        }
                        
                        
                    }
            }
        }
        .ignoresSafeArea(.keyboard, edges: .bottom)
        .onReceive(notificationManager.$pageToNavigationTo) {
            guard let notificationSelection = $0 else  { return }
            self.selection = notificationSelection // navigates to page InboxMessagesView
            
        }
        .onReceive(notificationManager.$recievedNotificationFromChatID) {
            guard $0 != nil else { return }
            
            self.activeChatID = $0 // navigates to the correct chat that is associated with the chatID when the user taps on a chat notification
            
        }

        
    }
    
}

I have a SwiftUI app with chat functionality that uses Firebase as its backend and Cloud Function to listen for new chat messages and notify users. The Cloud Function sends the notification to all devices of the logged-in user when the user has received a new chat message in one of the corresponding chat rooms. To navigate I have created a NotificationManager that helps the App to navigate through nested Views of the app if the user has tapped a notification.

Everything works, however when the user taps a notification after the app is terminated the navigation doesn't work. I guess it is because the navigation is tapped and its properties are changed before the user is restored...? I tried to look for solutions related to UIKit(since I didn't find any related to SwiftUI), but couldn't figure out a proper solution yet.

Above in my AppDelegate:

weak var notificationManager: NotificationManager?

User tapped notification:

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID from userNotificationCenter didReceive: \(messageID)")
        }
        
        print("*** NotificationInfo: \(userInfo) ***")
        let info = userInfo as NSDictionary
        guard let chatID = info.value(forKey: "chatID") as? String else { return } // retrieving the ChatID from notification payload
        
        
        // Navigate to the room of the chatID when chat notification is tapped
        
        
        notificationManager?.pageToNavigationTo = 1 // navigate to messages view
        notificationManager?.recievedNotificationFromChatID = chatID // navigate to correct chat
        
        
        completionHandler()
    }

Lifecycle:

@main
struct VitaliiApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    @StateObject var session = SessionStore()
    let notificationManager = NotificationManager()
    
    func setUpdNotificationManager() {
        appDelegate.notificationManager = notificationManager
    }
    
    var body: some Scene {
        WindowGroup {
            
            ContentView()
                .environmentObject(session)
                .environmentObject(notificationManager)
                .onAppear {
                    setUpdNotificationManager()
                }   
        }
       
        
    }
}

The NotificationManager:

class NotificationManager: ObservableObject {
    static let shared = NotificationManager()
    @Published var pageToNavigationTo : Int?
    @Published var recievedNotificationFromChatID: String?
}

LightWeight example of ContentView

struct ContentView: View {
    
    @State var selection: Int = 0
    @EnvironmentObject var session: SessionStore
    
    @State var activeChatID: String?
    
    let tabBarImageNames = ["person.3", "message", "person"]
    
    @EnvironmentObject private var notificationManager: NotificationManager
    

        
    var body: some View {
        
        ZStack {
            Color.black
                .ignoresSafeArea()
            
            VStack {
                
                ZStack {
                    
                    switch selection {
                    case 0:
                         NavigationView {
                            HomeView()
                        }                        
                    case 1:
                        NavigationView {
                            InboxMessagesView(user: session.userSession, activeChatID: $activeChatID)
                            
                        }
                        .accentColor(.white)
                   
                    default:
                        NavigationView {
                            ProfileView(session: session.userSession)
                        }
 
                    }
                    
                }
                
                Spacer()
                
                
                    HStack {
                        
                        ForEach(0..<3) { num in
                            Button {
                                selection = num
                            } label: {
                                Spacer()
                                
                                Image(systemName: tabBarImageNames[num])
                                    .padding(.top, 10)
                                    .font(.system(size: 20, weight: .medium))
                                    .foregroundColor(selection == num ? .red : .white.opacity(0.7))
                                Spacer()
                            }
                            
                        }
                        
                        
                    }
            }
        }
        .ignoresSafeArea(.keyboard, edges: .bottom)
        .onReceive(notificationManager.$pageToNavigationTo) {
            guard let notificationSelection = $0 else  { return }
            self.selection = notificationSelection // navigates to page InboxMessagesView
            
        }
        .onReceive(notificationManager.$recievedNotificationFromChatID) {
            guard $0 != nil else { return }
            
            self.activeChatID = $0 // navigates to the correct chat that is associated with the chatID when the user taps on a chat notification
            
        }

        
    }
    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你是暖光i2025-01-17 22:39:50

好的,我在 Swift 中发现了同样的问题,解决方案是将点击的通知的操作延迟一秒钟。感觉更像是一个黑客,我想确切地知道异步发生了什么,但它工作得很好。

解决方案如下:

 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // <- here!!
            self.notificationManager?.pageToNavigationTo = 1
            self.notificationManager?.recievedNotificationFromChatID = chatID
 }

Okay, I found the same problem in Swift, and the solution was to just delay the action of the notification tapped with one second. It feels more like a hack, I want to know exactly what happens asynchronously, but it works perfectly.

Here is the solution:

 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // <- here!!
            self.notificationManager?.pageToNavigationTo = 1
            self.notificationManager?.recievedNotificationFromChatID = chatID
 }
情定在深秋2025-01-17 22:39:50

我最近遇到了同样的问题,看到你的解决方案有助于解决我的问题,因为你在那里有一个单例,而

class NotificationManager: ObservableObject {
    static let shared = NotificationManager()
...
}

不是延迟通知 1 秒,这可能会适得其反,如果你在 < 中使用单例会怎么样? code>AppDelegate 以及(notificationManager,可能尚未启动,这就是为什么可能延迟 1 秒解决你的问题)

以某事结束

let notificationManager = self.notificationManager ?? NotificationManager.shared()
notificationManager.pageToNavigationTo = 1
notificationManager.recievedNotificationFromChatID = chatID

对我来说,做这样的事情就成功了!

I'm facing the same issue recently, watching your solution help to workaround with mine, since you are having a singleton there in the

class NotificationManager: ObservableObject {
    static let shared = NotificationManager()
...
}

instead of delaying the notification 1 seconds, which probably will be counterproductive, what if you use the singleton inside the AppDelegate as well, (the notificationManager, might not be initiated yet, that's why probably delaying 1 second fix your issue)

ending with something

let notificationManager = self.notificationManager ?? NotificationManager.shared()
notificationManager.pageToNavigationTo = 1
notificationManager.recievedNotificationFromChatID = chatID

For me doing something like this did the trick!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文