本地通知通常会在模拟器中为我触发,直到我基本上使用其他数据模型重写了我的应用程序为止。现在,即使一切似乎都在发生,而触发通知也需要发生,但事实并非如此。
以下是我可以确认显示在日志中的内容:
“用户已启用通知权限”
“用户启用的通知权限”
“尝试在19:37的时间创建警报”
“创建一天的通知:19,时间:19:36,使用uuid = B76AA489-CF41-49CB-9C3D-CF48590A9933”
“已将1个通知Uuid附加到已创建的警报中”
“ ----- notificationUuids:-----”
“ uuid:B76AA489-CF41-49CB-9C3D-CF48590A9933”
没有打印错误。这使我相信该警报已正确创建,并且该通知已添加到通知中心,但是由于某种原因它没有触发。以下是我的代码,这些代码经过精简以减少不必要的复杂性。我只删除了与通知无关的内容。
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代码:
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
}
}
答案 0 :(得分:0)
我知道了。在Alarm.swift的createNotification()中,我正在创建当前时间的通知,而不是随机警报时间。这是一个简单的错误,但这意味着警报时间在间隔时间之前结束,并且一旦您重新打开该应用程序,它就会删除警报,因为它是“旧的”。