为什么在 ios 模拟器上不会触发通知? [英] Why might a notification not fire on the ios simulator?

查看:96
本文介绍了为什么在 ios 模拟器上不会触发通知?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

过去在模拟器中为我触发本地通知,直到我基本上使用不同的数据模型重写了我的应用程序.现在,即使一切似乎都在发生,需要发生通知才能触发,但事实并非如此.

Local notifications used to fire for me in the simulator until I basically rewrote my app using a different data model. Now, even though everything seems to be happening that needs to happen for a notification to fire, it's not.

以下是我可以确认显示在日志中的内容,按顺序:

Here's what I can confirm show up in the logs, in order:

  • 用户已启用通知权限"

  • "User has notifications permissions enabled"

用户启用的通知权限"

正在尝试创建时间为 19:37 的闹钟"

"Attempting to create alarm with time 19:37"

正在创建日期:19,时间:19:36,uuid=B76AA489-CF41-49CB-9C3D-CF48590A9933"

"Creating notification for day: 19, time: 19:36, with uuid=B76AA489-CF41-49CB-9C3D-CF48590A9933"

创建的警报有 1 个通知 Uuid"

"There are 1 notificationUuids attached to the alarm created"

"----- notificationUuids: -----"

"----- notificationUuids: -----"

uuid:B76AA489-CF41-49CB-9C3D-CF48590A9933"

"uuid: B76AA489-CF41-49CB-9C3D-CF48590A9933"

没有打印错误.这让我相信警报已正确创建,并且通知已添加到通知中心,但由于某种原因它没有触发.下面是我的代码,为了减少不必要的复杂性而进行了删节.我只删除了与通知无关的内容.

No errors are printed. This leads me to believe that the alarm is getting created properly, and the notification is getting added to the notification center, but it's not firing for some reason. Below is my code, abridged to reduce unneeded complexity. I only removed things that aren't related to notifications.

class AlarmTableViewController: UITableViewController {

//MARK: Public properties
var alarms = [Alarm]()
let ALARM_CELL_IDENTIFIER = "AlarmTableViewCell"

override func viewDidLoad() {
    super.viewDidLoad()
    requestUserNotificationsPermissionsIfNeeded()
    NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(didReceiveNotification), name: NotificationNames.randomAlarmNotification, object: nil)
    loadAlarms()

    for alarm in self.alarms {
        os_log("There are %d notifications for alarm %d", log: OSLog.default, type: .debug, alarm.notificationUuids.count, alarm.alarmNumber)
    }
}

deinit {
    NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: NotificationNames.randomAlarmNotification, object: nil)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    guard let cell = tableView.dequeueReusableCell(withIdentifier: ALARM_CELL_IDENTIFIER, for: indexPath) as? AlarmTableViewCell else {
        fatalError("The dequeued cell is not an instance of AlarmTableViewCell.")
    }

    guard let alarm = self.alarms[safe: indexPath.row] else {
        os_log("Could not unwrap alarm for indexPath in AlarmTableViewController.swift", log: OSLog.default, type: .default)
        self.tableView.reloadData()
        return AlarmTableViewCell()
    }

    let alarmNumber = alarm.value(forKey: "alarmNumber") as! Int
    cell.alarmNumberLabel.text = "Alarm " + String(alarmNumber)

    let beginTime = alarm.value(forKey: "startTimeInterval") as! Double
    let endTime = alarm.value(forKey: "endTimeInterval") as! Double
    let beginTimeHour = Alarm.extractHourFromTimeDouble(alarmTimeDouble: beginTime)
    let beginTimeMinute = Alarm.extractMinuteFromTimeDouble(alarmTimeDouble: beginTime)
    cell.beginTimeLabel.text = formatTime(hour: beginTimeHour, minute: beginTimeMinute)
    let endTimeHour = Alarm.extractHourFromTimeDouble(alarmTimeDouble: endTime)
    let endTimeMinute = Alarm.extractMinuteFromTimeDouble(alarmTimeDouble: endTime)
    cell.endTimeLabel.text = formatTime(hour: endTimeHour, minute: endTimeMinute)

    guard let notificationUuids = self.getNotificationUuidsFromAlarmMO(alarm: alarm) else {
        os_log("Could not get notificationUuids from AlarmMO in tableView(cellForRowAt:) in AlarmTableViewController.swift", log: OSLog.default, type: .debug)
        return cell
    }
    os_log("----- notificationUuids: -----", log: OSLog.default, type: .debug)
    for uuid in notificationUuids {
        os_log("uuid: %@", log: OSLog.default, type: .debug, uuid)
    }

    return cell

}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    let alarmNotificationIdentifier = response.notification.request.identifier
    var alarmActedOn: Alarm? = nil
    for alarm in self.alarms {
        guard let notificationUuids = self.getNotificationUuidsFromAlarmMO(alarm: alarm) else {
            os_log("Could not get notificationUuids from AlarmMO in tableView(forRowAt:) in AlarmTableViewController.swift", log: OSLog.default, type: .debug)
            return
        }
        for notificationUuid in notificationUuids {
            if notificationUuid == alarmNotificationIdentifier {
                alarmActedOn = alarm
            }
        }
    }
    if response.actionIdentifier == "snooze" {
        alarmActedOn?.setNewRandomAlarmTime()
    }
    completionHandler()

}

// MARK: Actions

@IBAction func unwindToAlarmList(sender: UIStoryboardSegue) {

    if let sourceViewController = sender.source as? AddAlarmViewController, let alarm = sourceViewController.alarm {
        let newIndexPath = IndexPath(row: self.alarms.count, section: 0)
        os_log("There are %d notificationUuids attached to the alarm created", log: OSLog.default, type: .debug, alarm.notificationUuids.count)
        saveAlarm(alarmToSave: alarm)
        tableView.insertRows(at: [newIndexPath], with: .automatic)
    }

}

// MARK: Private functions

@objc private func didReceiveNotification() {

    os_log("entered the function", log: OSLog.default, type: .debug)

}

private func removeNotifications(notificationUuids: [String]) {

    os_log("Removing %d alarm notifications", log: OSLog.default, type: .debug, notificationUuids.count)
    let notificationCenter = UNUserNotificationCenter.current()
    notificationCenter.removePendingNotificationRequests(withIdentifiers: notificationUuids)

}

private func loadAlarms() {

    os_log("loadAlarms() called", log: OSLog.default, type: .debug)
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
            return
    }
    let managedContext = appDelegate.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<Alarm>(entityName: "Alarm")

    do {
        if self.alarms.count == 0 {
            self.alarms = try managedContext.fetch(fetchRequest)
            os_log("Loading %d alarms", log: OSLog.default, type: .debug, self.alarms.count)
        } else {
            os_log("Didn't need to load alarms", log: OSLog.default, type: .debug)
        }
    } catch let error as NSError {
        print("Could not fetch alarms. \(error), \(error.userInfo)")
    }

}

private func saveAlarm(alarmToSave: Alarm) {

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
            return
    }
    let managedContext = appDelegate.persistentContainer.viewContext
    let entity = NSEntityDescription.entity(forEntityName: "Alarm", in: managedContext)!
    let alarm = Alarm(entity: entity, insertInto: managedContext)

    alarm.setValue(alarmToSave.alarmTime, forKeyPath: "alarmTime")
    alarm.setValue(alarmToSave.alarmNumber, forKeyPath: "alarmNumber")
    alarm.setValue(alarmToSave.startTimeInterval, forKeyPath: "startTimeInterval")
    alarm.setValue(alarmToSave.endTimeInterval, forKeyPath: "endTimeInterval")
    alarm.setValue(RecurrenceOptions.getRawValue(value: alarmToSave.recurrence), forKeyPath: "recurrenceIndex")
    alarm.setValue(alarmToSave.notificationUuids, forKeyPath: "notificationUuids")
    alarm.setValue(alarmToSave.note, forKeyPath: "note")

    if managedContext.hasChanges {
        do {
            try managedContext.save()
            self.alarms.append(alarm)
        } catch let error as NSError {
            print("Could not save alarm to CoreData. \(error), \(error.userInfo)")
        }
    } else {
        os_log("No changes to the context to save", log: OSLog.default, type: .debug)
    }

}

private func getNotificationUuidsFromAlarmMO(alarm: Alarm) -> [String]? {

    guard let notificationUuids = alarm.value(forKey: "notificationUuids") as! [String]? else {
        os_log("Found nil when attempting to unwrap notificationUuids in getNotificationUuidsFromAlarmMO() in AlarmTableViewController.swift, returning nil",
               log: OSLog.default, type: .default)
        return nil
    }
    return notificationUuids

}

private func requestUserNotificationsPermissionsIfNeeded() {

    let notificationCenter = UNUserNotificationCenter.current()

    notificationCenter.getNotificationSettings { (settings) in
        guard settings.authorizationStatus == .authorized else {
            return
        }
        os_log("User has notifications permissions enabled", log: OSLog.default, type: .debug)
    }

    notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) {
        (granted, error) in
        if !granted {
            self.presentNotificationsPermissionDenialConfirmationAlert()
        } else {
            os_log("User enabled notifications permissions", log: OSLog.default, type: .debug)
        }
    }

    return

}

private func presentNotificationsPermissionDenialConfirmationAlert() {

    let alert = UIAlertController(title: "Are you sure you don't want to allow notifications?",
                                  message: "The application cannot function without notifications.", preferredStyle: .alert)

    alert.addAction(UIAlertAction(title: "Yes", style: .cancel, handler: {
        action in
        fatalError("User declined to allow notifications permissions")
    }))
    alert.addAction(UIAlertAction(title: "No", style: .default, handler: {
        action in
        self.requestUserNotificationsPermissionsIfNeeded()
    }))

    self.present(alert, animated: true)

}

}

那是我的 AlarmTableViewController 代码.这是我删节后的 Alarm.swift 代码:

That is my AlarmTableViewController code. Here's my abridged Alarm.swift code:

class Alarm: NSManagedObject {

@NSManaged var alarmTime: Double
@NSManaged var alarmNumber: Int
@NSManaged var startTimeInterval: Double
@NSManaged var endTimeInterval: Double
@NSManaged var note: String
@NSManaged var notificationUuids: [String]
@NSManaged var recurrenceIndex: Int
let NUMBER_OF_ALLOWED_NOTIFICATIONS_CREATED_AT_ONE_TIME = 10

}

extension Alarm {

static func newAlarm(context: NSManagedObjectContext, alarmNumber: Int, timeIntervals: TimeIntervals, note: String, recurrence: RecurrenceOptions) -> Alarm? {

    let startInterval = Alarm.convertToTimeDouble(hour: timeIntervals.hourStartInterval, minute: timeIntervals.minuteStartInterval)
    let endInterval = Alarm.convertToTimeDouble(hour: timeIntervals.hourEndInterval, minute: timeIntervals.minuteEndInterval)

    if endInterval < startInterval {
        os_log("Error: Alarm time endInterval is before startInterval", log: OSLog.default, type: .info)
        return nil
    }
    let newAlarm = Alarm(context: context)
    newAlarm.alarmNumber = alarmNumber
    newAlarm.note = note
    newAlarm.recurrenceIndex = RecurrenceOptions.getRawValue(value: recurrence)
    newAlarm.notificationUuids = [String]()
    newAlarm.startTimeInterval = startInterval
    newAlarm.endTimeInterval = endInterval

    newAlarm.setNewRandomAlarmTime()

    return newAlarm
}

var recurrence: RecurrenceOptions {
    get {
        return RecurrenceOptions.getValueFromIndex(index: recurrenceIndex)
    }

    set {
        self.recurrenceIndex = RecurrenceOptions.getRawValue(value: newValue)
    }
}

var hour: Int {
    return Int(floor(self.alarmTime))
}

var minute: Int {
    return Int(round((self.alarmTime - floor(self.alarmTime)) * 60))
}

func setNewRandomAlarmTime() {

    let startInterval = self.startTimeInterval
    let endInterval = self.endTimeInterval

    let currentDateAndTime = Date()
    let currentDateAndTimeComponents = Calendar.current.dateComponents([.hour, .minute], from: currentDateAndTime)
    guard let currentHour = currentDateAndTimeComponents.hour else {
        os_log("Could not unwrap currentDateAndTimeComponents.hour in Alarm.setNewRandomAlarmTime()", log: OSLog.default, type: .default)
        return
    }
    guard let currentMinute = currentDateAndTimeComponents.minute else {
        os_log("Could not unwrap currentDateAndTimeComponents.minute in Alarm.setNewRandomAlarmTime()", log: OSLog.default, type: .default)
        return
    }
    let currentTimeDouble = Alarm.convertToTimeDouble(hour: currentHour, minute: currentMinute)

    // We should start the random alarm interval from the current
    // time if the current time is past the startInterval already
    if currentTimeDouble > startInterval {
        self.alarmTime = Double.random(in: currentTimeDouble ... endInterval)
    } else {
        self.alarmTime = Double.random(in: startInterval ... endInterval)
    }

    scheduleNotifications()

}

func scheduleNotifications() {
    os_log("Attempting to create alarm with time %d:%02d", log: OSLog.default, type: .info, self.hour, self.minute)
    let date = Date()
    let calendar = Calendar.current
    let currentDateComponents = calendar.dateComponents([.year, .month, .day, .timeZone, .hour, .minute], from: date)
    createNotifications(dateComponents: currentDateComponents)
}

//MARK: Private functions

private func createNotifications(dateComponents: DateComponents) {

    switch (self.recurrence) {
    case .today:
        self.createNotification(for: dateComponents)
    case .tomorrow:
        self.createNotification(for: self.day(after: dateComponents))
    case .daily:
        var numberOfCreatedNotifications = 0
        var currentDay: DateComponents? = dateComponents
        while numberOfCreatedNotifications < self.NUMBER_OF_ALLOWED_NOTIFICATIONS_CREATED_AT_ONE_TIME {
            self.createNotification(for: currentDay)
            currentDay = self.day(after: currentDay)
            numberOfCreatedNotifications += 1
        }
    }

}

private func createNotification(for dateComponents: DateComponents?) {

    let center = UNUserNotificationCenter.current()

    let content = UNMutableNotificationContent()
    content.title = "Random Alarm"
    content.subtitle = "It's time!"
    content.body = self.note
    content.sound = UNNotificationSound.default

    guard let dateComponents = dateComponents else {
        os_log("Could not unwrap dateComponents in createNotification() in Alarm.swift", log: OSLog.default, type: .debug)
        return
    }
    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

    let uuidString = UUID().uuidString
    let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
    self.notificationUuids.append(uuidString)

    guard let day = dateComponents.day else {
        os_log("Could not unwrap dateComponents.day in createNotification() in Alarm.swift", log: OSLog.default, type: .debug)
        return
    }
    guard let hour = dateComponents.hour else {
        os_log("Could not unwrap dateComponents.hour in createNotification() in Alarm.swift", log: OSLog.default, type: .debug)
        return
    }
    guard let minute = dateComponents.minute else {
        os_log("Could not unwrap dateComponents.minute in createNotification() in Alarm.swift", log: OSLog.default, type: .debug)
        return
    }
    os_log("Creating notification for day: %d, time: %d:%02d, with uuid=%s", log: OSLog.default, type: .debug, day, hour, minute, uuidString)

    center.add(request) { (error) in
        if let err = error {
            print("error \(err.localizedDescription)")
        }
    }

}

private func day(after dateComponents: DateComponents?) -> DateComponents? {

    let calendar = Calendar.autoupdatingCurrent

    guard let dateComponents = dateComponents,
        let date = calendar.date(from: dateComponents),
        let tomorrow = calendar.date(byAdding: .day, value: 1, to: date)
        else {
            os_log("Could not calculate tomorrow in Alarm.swift", log: OSLog.default, type: .debug)
            return nil
    }
    let newDateComponents = calendar.dateComponents([.year, .month, .day, .timeZone, .hour, .minute], from: tomorrow)
    return newDateComponents

}

}

推荐答案

我想通了.在 Alarm.swift 的 createNotification() 中,我正在创建当前时间的通知,而不是随机警报时间.一个简单的错误,但这意味着闹钟时间在间隔之前结束,一旦您重新打开应用程序,它就会删除闹钟,因为它是旧的".

I figured it out. In createNotification() in Alarm.swift I was creating the notification for the current time, not the random alarm time. Simple mistake, but that meant that the alarm time ended up before the interval, and once you reopen the app it deletes the alarm because it's "old".

这篇关于为什么在 ios 模拟器上不会触发通知?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆