ios - 在自己的应用程序/ View 中接收本地通知(或如何在 SwiftUI 中注册 UNUserNotificationCenterDelegate)

标签 ios swift notifications swiftui countdown

我正在使用包含倒计时功能的 SwiftUI 重新开发适用于 iOS 的 android 应用程序。当倒计时结束时,应该通知用户倒计时结束。通知应该有点侵入性并且在不同的场景中工作,例如当用户没有主动使用手机时,当用户正在使用我的应用程序时,以及当用户正在使用另一个应用程序时。我决定使用本地通知来实现这一点,这是 android 的工作方法。 (如果这种方法完全错误,请告诉我什么是最佳做法)
但是,当用户当前使用我的应用程序时,我无法收到通知。通知仅显示在消息中心(所有通知排队的地方),但不会主动弹出。
到目前为止,这是我的代码:
用户被要求允许在我的 CountdownOrTimerSheet 结构中使用通知(从不同的 View 中调用作为 actionSheet):

/**
    asks for permission to show notifications, (only once) if user denied there is no information about this , it is just not grantedand the user then has to go to settings to allow notifications 
    if permission is granted it returns true
 */
func askForNotificationPermission(userGrantedPremission: @escaping (Bool)->())
{
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
        if success {
            userGrantedPremission(true)
        } else if let error = error {
            userGrantedPremission(false)
        }
    }
}
仅当用户允许通知我的 TimerView 结构时才被调用
                         askForNotificationPermission() { (success) -> () in
                            
                            if success
                            {
                                
                                // permission granted

                                ...
                                // passing information about the countdown duration and others..
                                ...
                                
                                userConfirmedSelection = true // indicates to calling view onDismiss that user wishes to start a countdown
                                showSheetView = false // closes this actionSheet
                            }
                            else
                            {
                                // permission denied
                                showNotificationPermissionIsNeededButton = true
                            }
                        }
从上一个 View
                   .sheet(isPresented: $showCountDownOrTimerSheet, onDismiss: {
                        // what to do when sheet was dismissed
                        if userConfirmedChange
                        {
                            // go to timer activity and pass startTimerInformation to activity
                            programmaticNavigationDestination = .timer
                            
                        }
                    }) {
                        CountdownOrTimerSheet(startTimerInformation: Binding($startTimerInformation)!, showSheetView: $showCountDownOrTimerSheet, userConfirmedSelection: $userConfirmedChange)
                    }


                    ...


                    NavigationLink("timer", destination:
                                TimerView(...),
                               tag: .timer, selection: $programmaticNavigationDestination)
                        .frame(width: 0, height: 0)
在我的 TimerView 的初始化中,通知最终被注册
        self.endDate = Date().fromTimeMillis(timeMillis: timerServiceRelevantVars.endOfCountDownInMilliseconds_date)
        
        // set a countdown Finished notification to the end of countdown
        let calendar = Calendar.current
        let notificationComponents = calendar.dateComponents([.hour, .minute, .second], from: endDate)
        let trigger = UNCalendarNotificationTrigger(dateMatching: notificationComponents, repeats: false)
        
        
        let content = UNMutableNotificationContent()
        content.title = "Countdown Finished"
        content.subtitle = "the countdown finished"
        content.sound = UNNotificationSound.defaultCritical

        // choose a random identifier
        let request2 = UNNotificationRequest(identifier: "endCountdown", content: content, trigger: trigger)

        // add the notification request
        UNUserNotificationCenter.current().add(request2)
        {
            (error) in
            if let error = error
            {
                print("Uh oh! We had an error: \(error)")
            }
        }
如上所述,当用户在everyWhere 但我自己的应用程序时,通知会按预期显示。然而,TimerView 显示有关倒计时的信息,并且最好是用户设备上的事件 View 。因此,我需要能够在此处接收通知,也需要在我的应用程序中的其他任何地方接收通知,因为用户还可以在我的应用程序中的其他位置导航。如何实现?
this example类似的事情已经完成,不幸的是不是用 swiftUI 而是用以前的通用语言编写的。我不明白这是如何完成的,或者如何完成这个。我在互联网上没有找到任何东西。我希望你能帮助我。

最佳答案

引用文档:
Scheduling and Handling Local Notifications
在关于当您的应用程序处于前台时处理通知的部分:

If a notification arrives while your app is in the foreground, you can silence that notification or tell the system to continue to display the notification interface. The system silences notifications for foreground apps by default, delivering the notification’s data directly to your app...


据此,您必须为 UNUserNotificationCenter 实现一个委托(delegate)。并调用completionHandler告诉您希望如何处理通知。
我建议你像这样,在 AppDelegate 上您为 UNUserNotificationCenter 分配代表因为文档说必须在应用程序完成启动之前完成(请注意文档说应该在应用程序完成启动之前设置委托(delegate)):
// AppDelegate.swift
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // Here we actually handle the notification
        print("Notification received with identifier \(notification.request.identifier)")
        // So we call the completionHandler telling that the notification should display a banner and play the notification sound - this will happen while the app is in foreground
        completionHandler([.banner, .sound])
    }
}
你可以告诉 SwiftUI 使用这个 AppDelegate通过使用 UIApplicationDelegateAdaptor在您的 App 上场景:
@main
struct YourApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

关于ios - 在自己的应用程序/ View 中接收本地通知(或如何在 SwiftUI 中注册 UNUserNotificationCenterDelegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65782435/

相关文章:

ios - Swift 3 将图像从 URL 添加到 UIImageView

ios - Swift 中 NSTimer 的问题

android - 在 Android 通知中隐藏时间而不使用自定义布局

javascript - 来自 Web 应用程序的 iOS 通知

ios - 如何在 SwiftUI 中通过点击手势制作放大缩小按钮动画?

ios Parse.com - 启用本地数据存储 PFFacebookUtils loginWithPermission block 后永远不会被调用

swift - 如何从 firebase 标题文件夹 Xcode 文件 TableView

ios - APNS 保存通知

ios - 当我在 ios 模拟器中运行我的应用程序时,它会卡在白屏上,显示版权信息

ios - “CGPointMake”在 swift 中不可用