3

好的 - 我现在对这段代码感到非常沮丧,准备放弃!基本上,当模拟到模拟器或实际设备时,我得到 requestAuthorisation 工作没有问题,但触发器永远不会启动。我在网上关注了几个人,他们的代码很容易运行!当我使用按钮启动 UNTimeIntervalNotificationTrigger 时,它可以工作,但这不是我想要的。目前在 iOS 14.3 中作为构建目标进行测试。应用程序的其余部分构建没有问题。我究竟做错了什么?!忍不住想,在试图让它工作的某个地方,我可能已经损坏了 info.plist 或类似的东西?!我已经测试过重复触发而不是重复,但都不起作用。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //NOTIFICATIONS
        // Step 1 - Ask the use for permission to notify
        let randVerseCenter = UNUserNotificationCenter.current()
        randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
            if granted {
                print("Yay - request authorisation worked!")
            } else {
                print ("D'oH - request Authorisation did not work!")
            }
        }
        // Step 2 - Create the Notification Content
        let randVerseContent = UNMutableNotificationContent()
        randVerseContent.title = "Random Reference"
        randVerseContent.body = "Random Verse"
        randVerseContent.sound = UNNotificationSound.default
        // Step 3 - Create the trigger for the notification by delay
        let randVerseDate = Date().addingTimeInterval(30)
        let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)
        let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
        // Step 4 - Creating the request
        let randVerseUUIDString = UUID().uuidString
        let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
        // Step 5 - Register the request
        randVerseCenter.add(randVerseRequest) { (error) in
            if let error = error{
                print (error.localizedDescription)
            }
            //Check the error parameter and handle any errors
        }
    }
4

2 回答 2

3

获得更多详细信息后,我想我知道为什么您仍然看不到正在发送的通知。我正在另一个答案中让它不要太长,但我会保留我之前的答案以供参考。
也许您正在等待应用程序在前台的通知?我将参考文档的另一部分:

调度和处理本地通知
在关于当您的应用程序处于前台时处理通知的部分:

如果在您的应用处于前台时收到通知,您可以将该通知静音或告诉系统继续显示通知界面。系统默认将前台应用程序的通知静音,将通知数据直接发送到您的应用程序...

因此,如果是这种情况,您必须为UNUserNotificationCenter.
我建议您这样做,在 AppDelegate 上分配委托,UNUserNotificationCenter因为文档说必须在应用程序完成启动之前完成:

// AppDelegate.swift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {


    func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }

    // Rest of your code on AppDelegate...
}

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

在您处理通知授权和请求注册的视图控制器上,您可以这样做:

class NotificationsViewController: UIViewController {
    
    static let notificationAuthorizedNotification = NSNotification.Name(rawValue: "NotificationAuthorizedNotification")
    let randVerseCenter = UNUserNotificationCenter.current()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // We call this method when we know that the user granted permission, so we know we can then make notification requests
        NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationAuthorization), name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
        
        randVerseCenter.getNotificationSettings { [weak self] settings in
            // We check current settings and asks for permission if not granted before
            if settings.authorizationStatus == .notDetermined {
                // Step 1 - Ask the use for permission to notify
                self?.randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
                    if granted {
                        NotificationCenter.default.post(name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
                        print("Yay - request authorisation worked!")
                    } else {
                        print ("D'oH - request Authorisation did not work!")
                    }
                }
            }
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // We stop listening to those notifications here
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc
    func handleNotificationAuthorization() {
        // Step 2 - Create the Notification Content
        let randVerseContent = UNMutableNotificationContent()
        randVerseContent.title = "Random Reference"
        randVerseContent.body = "Random Verse"
        randVerseContent.sound = UNNotificationSound.default
        // Step 3 - Create the trigger for the notification by delay
        let randVerseDate = Date().addingTimeInterval(30)
        let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
        let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
        // Step 4 - Creating the request
        let randVerseUUIDString = UUID().uuidString
        let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
        // Step 5 - Register the request
        randVerseCenter.add(randVerseRequest) { (error) in
            if let error = error{
                print (error.localizedDescription)
            } else {
                print("Successfully registered notification with id \(randVerseUUIDString) at every second \(randVerseDateComponents.second!) of a minute")
            }
        }
    }
}

您可能仍然安排了较旧的通知,因为您的代码正在请求它们,viewDidLoad并且您可能没有删除它们或删除应用程序。您可以在您的示例
上使用它来检查待处理的通知:viewDidLoad

        randVerseCenter.getPendingNotificationRequests() { requests in
            for request in requests {
                guard let trigger = request.trigger as? UNCalendarNotificationTrigger else { return }
                print("Notification registered with id \(request.identifier) is schedulled for \(trigger.nextTriggerDate()?.description ?? "(not schedulled)")")
            }
        }

并使用randVerseCenter它们的标识符删除它们或删除所有它们。

于 2021-01-07T08:50:49.287 回答
2

问题是触发器是如何创建的。我们可以查看文档UNCalendarNotificationTrigger以获得更多理解:

当您想要安排在指定日期和时间发送本地通知时,请创建一个 UNCalendarNotificationTrigger 对象。您可以使用 NSDateComponents 对象指定时间信息,该对象允许您仅指定对您重要的时间值。系统使用提供的信息来确定与指定信息匹配的下一个日期和时间。

https://developer.apple.com/documentation/usernotifications/uncalendarnotificationtrigger

因此,UNCalendarNotificationTrigger当您想要创建触发器以匹配日期组件时使用。下面的代码将创建一个触发器,该触发器将在每天早上 8:30 发送通知,因为指定了.hour.minute组件:

    var date = DateComponents()
    date.hour = 8
    date.minute = 30 
    // This trigger will match these two components - hour and minute
    let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)

在您的情况下,您使用日期的所有组件(年、月、日、小时、分钟、秒)创建了一个触发器:

let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)

这使得重复触发成为不可能的条件——因为不会再有 2021 年——所以它不会被触发。

您需要考虑如何触发此通知。如果您的意图是从特定时间开始在同一秒内发送通知,那么您必须仅使用.second日期组件:

let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)

假设randVerseDate类似于2021-01-06-20:01:35,我们使用上面的代码行。然后这将在时钟达到35秒时每分钟触发一次通知: 20:02:35,然后20:03:35,然后20:04:35,依此类推...

于 2021-01-06T19:04:16.747 回答