migrating from UILocalNotification to UNUserNotification swift

647 Views Asked by At

I'm migrating to NUUserNotification but I'm having problems as I'm learning notifications with the newer framework and I don't know the older. I hope this is not some duplicate as I read a lot of posts and tutorial to how to migrate but haven't found something that made me understand how to apply to this case. The code I'm updating is not mine and took me time understanding it. It's a clone of the iOS Alarm ( https://github.com/natsu1211/Alarm-ios-swift) that I incorporate in my app to perform things at scheduled time/days etc etc... The two things I'm having trouble to understand are the notification repetitions, as far as I understood is now for every week day or a single time right? The fireDate parameter is depreciated so how would I rewrite this? :

private func minFireDateWithIndex(notifications: [UILocalNotification]) -> (Date, Int)? {
    if notifications.isEmpty {
        return nil
    }
    var minIndex = -1
    var minDate: Date = notifications.first!.fireDate!
    for n in notifications {
        let index = n.userInfo!["index"] as! Int
        if(n.fireDate! <= minDate) {
            minDate = n.fireDate!
            minIndex = index
        }
    }
    return (minDate, minIndex)
}

The other code I'm having trouble updating is the checking for existing notifications that now getPendingNotificationRequestsWithCompletionHandler:]. It's if let n = UIApplication.shared.scheduledLocalNotificationsinside the old function that define the notification, that I think I have updated for the remaining part. Old code:

func setupNotificationSettings() -> UIUserNotificationSettings {

        var snoozeEnabled: Bool = false

        if let n = UIApplication.shared.scheduledLocalNotifications {
            if let result = minFireDateWithIndex(notifications: n) {
                let i = result.1
                snoozeEnabled = alarmModel.alarms[i].snoozeEnabled
            }
        }

        // Specify the notification types.

        let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.sound]
        // Specify the notification actions.
        let stopAction = UIMutableUserNotificationAction()
        stopAction.identifier = Id.stopIdentifier
        stopAction.title = "OK"

        stopAction.activationMode = UIUserNotificationActivationMode.background    // choose activation mode for app on tapping notification

        stopAction.isDestructive = false
        stopAction.isAuthenticationRequired = false

        let snoozeAction = UIMutableUserNotificationAction()
        snoozeAction.identifier = Id.snoozeIdentifier
        snoozeAction.title = "Snooze"
        snoozeAction.activationMode = UIUserNotificationActivationMode.background
        snoozeAction.isDestructive = false
        snoozeAction.isAuthenticationRequired = false

        let actionsArray = snoozeEnabled ? [UIUserNotificationAction](arrayLiteral: snoozeAction, stopAction) : [UIUserNotificationAction](arrayLiteral: stopAction)
        let actionsArrayMinimal = snoozeEnabled ? [UIUserNotificationAction](arrayLiteral: snoozeAction, stopAction) : [UIUserNotificationAction](arrayLiteral: stopAction)
        // Specify the category related to the above actions.
        let alarmCategory = UIMutableUserNotificationCategory()
        alarmCategory.identifier = "myAlarmCategory"
        alarmCategory.setActions(actionsArray, for: .default)
        alarmCategory.setActions(actionsArrayMinimal, for: .minimal)


        let categoriesForSettings = Set(arrayLiteral: alarmCategory)
        // Register the notification settings.
        let newNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: categoriesForSettings)
        UIApplication.shared.registerUserNotificationSettings(newNotificationSettings)
        return newNotificationSettings
    }

New code edit:

    func setRouteNotification(_ date: Date, onWeekdaysForNotify weekdays:[Int], snoozeEnabled:Bool,  onSnooze: Bool, soundName: String, routeName: String, index: Int) {
    // Notification content
    let routeCheckNotificationContent = UNMutableNotificationContent()
    let datesForNotification = correctDate(date, onWeekdaysForNotify: weekdays)
    routeCheckNotificationContent.title = "Hello!! Are you ready to cycle?"
    routeCheckNotificationContent.body = "Check route for alerts?"
    routeCheckNotificationContent.categoryIdentifier = Id.notificationCategory
    routeCheckNotificationContent.sound = UNNotificationSound.init(named: soundName + ".mp3") // check for the + ".mp3"

    // Define actions
    let check = UNNotificationAction(identifier: Id.checkActionIdentifier, title: " Check", options: [.foreground])
    let wait = UNNotificationAction(identifier: Id.waitActionIdentifier, title: "Wait", options: [])
    // Define category
    let routeCategory = UNNotificationCategory(identifier: Id.notificationCategory, actions: [check, wait], intentIdentifiers: [], options: [])
    // Register category
    UNUserNotificationCenter.current().setNotificationCategories([routeCategory])


    let repeating: Bool = !weekdays.isEmpty
    routeCheckNotificationContent.userInfo = ["snooze" : snoozeEnabled, "index": index, "soundName": soundName, "routeName": routeName, "repeating" : repeating]
    //repeat weekly if repeat weekdays are selected
    //no repeat with snooze notification
    if !weekdays.isEmpty && !onSnooze{
    }

    syncAlarmModel()
    for d in datesForNotification {
        if onSnooze {
            alarmModel.alarms[index].date = Scheduler.correctSecondComponent(date: alarmModel.alarms[index].date)
        }
        else {
            alarmModel.alarms[index].date = d
        }
        // Notification trigger

        let calendar = Calendar(identifier: .gregorian)


        let components = calendar.dateComponents(in: .current, from: d)
        let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute, second: components.second, weekday: components.weekday)
        let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: true)
        // Notification Request
        let routeNotificationRequest = UNNotificationRequest(identifier: "routeNotificationRequest", content: routeCheckNotificationContent, trigger: trigger)

        // Add request
        UNUserNotificationCenter.current().add(routeNotificationRequest) { (Error) in
            if Error != nil {
                print("something went wrong with adding notification")
            }
        }
    }
}

var weekdays: [Int]!is an array populated in WeekdaysViewController ( a TableViewController ), selecting the rows.

Now when I select any day, notification won't fire.

2

There are 2 best solutions below

0
On BEST ANSWER

@Mikael. @Honey. After struggling a bit with code, because I thought that I could only use .weekday component to set repetitions I found that I could use the same logic of setting up a notification per day of repetition using Date as I store the selected dates into an array, so I did set a for in loop to set a new notification with unique identifier for each stored date. Final code is:

        func setRouteNotification(_ date: Date, onWeekdaysForNotify weekdays:[Int], snoozeEnabled:Bool,  onSnooze: Bool, soundName: String, routeName: String, index: Int) {
            // Notification content
            let routeCheckNotificationContent = UNMutableNotificationContent()
            let datesForNotification = correctDate(date, onWeekdaysForNotify: weekdays)
            routeCheckNotificationContent.title = "Hello!! Are you ready to cycle?"
            routeCheckNotificationContent.body = "Check route for alerts?"
            routeCheckNotificationContent.categoryIdentifier = Id.notificationCategory
            routeCheckNotificationContent.sound = UNNotificationSound.init(named: soundName + ".mp3") // check for the + ".mp3"

            // Define actions
            let check = UNNotificationAction(identifier: Id.checkActionIdentifier, title: " Check", options: [.foreground])
            let wait = UNNotificationAction(identifier: Id.waitActionIdentifier, title: "Wait", options: [])
            // Define category
            let routeCategory = UNNotificationCategory(identifier: Id.notificationCategory, actions: [check, wait], intentIdentifiers: [], options: [])
            // Register category
            UNUserNotificationCenter.current().setNotificationCategories([routeCategory])


            let repeating: Bool = !weekdays.isEmpty
            routeCheckNotificationContent.userInfo = ["snooze" : snoozeEnabled, "index": index, "soundName": soundName, "routeName": routeName, "repeating" : repeating]
            //repeat weekly if repeat weekdays are selected
            //no repeat with snooze notification
            if !weekdays.isEmpty && !onSnooze{
            }

            syncAlarmModel()
            var counter = 0

            for d in datesForNotification {
                if onSnooze {
                    alarmModel.alarms[index].date = Scheduler.correctSecondComponent(date: alarmModel.alarms[index].date)
                }
                else {
                    alarmModel.alarms[index].date = d
                }
                // Notification trigger

                let calendar = Calendar(identifier: .gregorian)


                let components = calendar.dateComponents(in: .current, from: d)
                var newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute, second: components.second, weekday: components.weekday)
                newComponents.weekday = weekdays[counter]
                let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: true)
                // Notification Request
//                let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
                let routeNotificationRequest = UNNotificationRequest(identifier: "routeNotificationRequest\(counter)", content: routeCheckNotificationContent, trigger: trigger)

                // Add request
                UNUserNotificationCenter.current().add(routeNotificationRequest) { (Error) in
                    if Error != nil {
                        print("something went wrong with adding notification")
                    }
                }
                print("added request\(counter)")
                counter = ( counter + 1 )

            }
            print(alarmModel.alarms)
        }
6
On

If I understand correctly, you want to have a repeated local notification. If so, you only need to setup your notification with

let dateInfo = Calendar.current.dateComponents([.weekday,.hour,.minute], from: aDateWithTheDayHourAndMinuteYouWantToTriggerTheNotification)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateInfo, repeats: true)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

Et voila