Repeat interval for UNNotification
Asked Answered
S

2

4

There is an option to set the repeat interval for UILocalNotification. Since Apple has deprecated UILocalNotification and recommend to use UNNotification instead, I couldn't find a way to set a notification with custom repeat interval with UNNotification.

    var comp = DateComponents()
    comp.year = 2019
    comp.month = 1
    comp.day = 9
    comp.hour = 14
    comp.minute = 14
    comp.second = 0
    let calendar = Calendar.current
    let notification: UILocalNotification = UILocalNotification()
    notification.category = "Daily Quote"
    notification.alertBody = "Body"
    notification.alertTitle = "Title"
    notification.fireDate = calendar.date(from: comp)
    notification.repeatInterval = NSCalendar.Unit.day
    UIApplication.shared.scheduleLocalNotification(notification)

So can I set a similar notification that repeats hourly or daily after waiting for the initial notification using the new UNNotification?

Screech answered 7/1, 2019 at 14:15 Comment(4)
This already has many answers especially when in the context of alarms. Look at the related links when typing in your question, and use methods described in here and related: #38381283Maurita
@Nithin, have you tried my example below?Instrumentality
@mijokaliger Actually the situation is like I need to schedule the notification for next Tuesday and repeat it every day. I think this could not be achieved with your solution.Screech
@NithinDevN I've edited my answerInstrumentality
I
3

To mimic the UILocalNotification's API fireDate and repeatInterval you can create two triggers, one non-repeating which would be used for fireDate to kickoff and other repeating for repeatInterval.

Here's an example:

import UserNotifications

/// Schedules notificaiton to fire at specific date, and then it repeats by specified repeat component
/// (week, day, hour, etc.) and repeat interval. For example to repeat every 20minutes repeatComponent
/// would be .minute and repeatInterval would be 20.
/// - Parameters:
///   - fireDate: Date for initial notification delivery
///   - repeatComponent: Component by which repeating would be performed (week, day, hour, etc.)
///   - repeatInterval: Interval by which repeating by specified component would be performed. Defaults value is 1.
func scheduleNotification(fireDate: Date, repeatComponent: Calendar.Component, repeatInterval: Int = 1) {

    let content = UNMutableNotificationContent()
    content.title = "Daily Quote"
    content.body = "Inspirational quote."
    content.categoryIdentifier = "quote.category"

    UNUserNotificationCenter.current().requestAuthorization(
        options: [.alert,.sound])
    {
        (granted, error) in

        if let error = error {
            print("granted, but Error in notification permission:\(error.localizedDescription)")
        }

        let fireTrigger = UNTimeIntervalNotificationTrigger(timeInterval: fireDate.timeIntervalSinceNow, repeats: false)

        let fireDateRequest = UNNotificationRequest(identifier: "quote.starter", content: content, trigger: fireTrigger)

        UNUserNotificationCenter.current().add(fireDateRequest) {(error) in
            if let error = error {
                print("Error adding firing notification: \(error.localizedDescription)")
            } else {

                if let firstRepeatingDate = Calendar.current.date(byAdding: repeatComponent, value: repeatInterval, to: fireDate) {

                    let repeatingTrigger = UNTimeIntervalNotificationTrigger(timeInterval: firstRepeatingDate.timeIntervalSinceNow, repeats: true)

                    let repeatingRequest = UNNotificationRequest(identifier: "quote.repeater", content: content, trigger: repeatingTrigger)

                    UNUserNotificationCenter.current().add(repeatingRequest) { (error) in
                        if let error = error {
                            print("Error adding repeating notification: \(error.localizedDescription)")
                        } else {
                            print("Successfully scheduled")
                            // Successfully scheduled
                        }
                    }

                }
            }
        }

        UNUserNotificationCenter.current().delegate = self
    }
}

Delegate (for debug):

extension ViewController: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("\(notification.request.identifier): \(Date())")
        UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
            for request in requests {
                if let timeIntervalTrigger = request.trigger as? UNTimeIntervalNotificationTrigger {
                    print(Date(timeIntervalSinceNow: timeIntervalTrigger.timeInterval))
                }

            }
        }
    }
}

Usage for your requirement:

let interval = 7 // One week from now
if let fireDate = Calendar.current.date(byAdding: .day, value: interval, to: Date()) {
    _ = scheduleNotification(fireDate: fireDate, repeatComponent: .day)
}

NOTE

Specifying repeating interval less than 60sec would result with exception:

'NSInternalInconsistencyException', reason: 'time interval must be at least 60 if repeating'

Instrumentality answered 7/1, 2019 at 14:29 Comment(6)
what is the category identifier? If i have 2 local notification per day should i set different identifiers?Yare
What happens when I use timeIntervalSince1970 instead of timeIntervalSinceNow? @mijokaligerYare
Hmmm. I think the specifics of the code here do not work as intended. I think what is intended is that the UNUserNotificationCenter.current().add completion handler is called after the delay. And that is then used to schedule the repeating requests after the delay. However, the UNUserNotificationCenter.current().add completion handler is called immediately. It is the delegate method that is called after the delay. Thus, AFAIK, the delegate method would have to be used to schedule the repeating requests.Domesticate
And if you are looking for this to operate in the background, this will not be reliable. The delegate methods are not called unless the user interacts with the notification. See docs in developer.apple.com/documentation/usernotifications/…Domesticate
In the code, repeatingTrigger's timeInterval is calculated based on present time, but I think present time is irrelevant for repeatingTrigger. That line can be changed like so: let repeatingTrigger = UNTimeIntervalNotificationTrigger(timeInterval: firstRepeatingDate.timeIntervalSince(fireDate), repeats: true)Sambo
Useless approach when app is terminated. The old API from Apple was able to do it. Is there any chance the new UserNotification API can do the same as notification.fireDate and notification.repeatInterval for a completely terminated App ???Commitment
L
1

you should use UNTimeIntervalNotificationTrigger Check the doc https://developer.apple.com/documentation/usernotifications/untimeintervalnotificationtrigger

Luminescent answered 7/1, 2019 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.