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?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • 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: https://stackoverflow.com/questions/38380783/usernotification-in-3-days-then-repeat-every-day-hour-ios-10?rq=1 – Stephen J Jan 07 '19 at 23:41
  • @Nithin, have you tried my example below? – manikal Jan 08 '19 at 08:00
  • @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. – nithindev.n Jan 08 '19 at 11:30
  • @NithinDevN I've edited my answer – manikal Jan 16 '19 at 13:17

2 Answers2

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'

manikal
  • 953
  • 7
  • 30
  • what is the category identifier? If i have 2 local notification per day should i set different identifiers? – Bibin Jaimon Apr 04 '19 at 17:44
  • What happens when I use timeIntervalSince1970 instead of timeIntervalSinceNow? @mijokaliger – Bibin Jaimon Apr 08 '19 at 16:48
  • 2
    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. – Chris Prince Jul 16 '19 at 15:30
  • 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 https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate – Chris Prince Jul 16 '19 at 22:51
  • 1
    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) – Luke Aug 13 '19 at 07:24
  • 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 ??? – iKK May 31 '20 at 13:37
1

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