1

I was trying to set up local notifications to fire on the last day of each month regardless of whether it's the 28th, 29th, 30th, or 31st.

With the previous framework UILocalNotification I could specify the fireDate and repeatInterval and it worked. But now that I'm updating the framework to UNNotofication, I tried to use UNCalendarNotificationTrigger and set the DateComponents' day to 0. However it didn't fire. Anything I was doing wrong? Thanks!

The code is as below. nextTriggerDate is nil.

var components = DateComponents()
components.hour = 8
components.minute = 0
components.day = 0

let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
print(trigger.nextTriggerDate())
shim
  • 9,289
  • 12
  • 69
  • 108
Bonan
  • 709
  • 4
  • 18
  • Your result is `nil` because you set `day` to `0`. – shim Nov 03 '21 at 05:15
  • @shim, you are right, however it worked before with UILocalNotification that with day set to 0 it'd give me the last day of previous month. `Calendar.current.date(from: components)` actually gives you Dec 31. Problem is this way the dateComponents doesn't work with UNCalendarNotificationTrigger – Bonan Nov 03 '21 at 10:16
  • Please show your original code for comparison – shim Nov 03 '21 at 11:21

1 Answers1

2

You can use the following code, next last day month is calculated after notification is shown.

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
    { (granted, error) in
        // Enable or disable features based on authorization
    }
    let current = UNUserNotificationCenter.current()
    current.delegate = self
    setNotificationRequest()
}


func setNotificationRequest(offset: Int = 0) {
    let content = UNMutableNotificationContent()
    content.title = "Alarm"
    content.body = "Alarm for End of Month"
    content.sound = UNNotificationSound.default
    
    var dateCompo = DateComponents()
    dateCompo.day = lastDay(offset: offset)
    let trigger = UNCalendarNotificationTrigger.init(dateMatching: dateCompo, repeats: true)
    let request = UNNotificationRequest(identifier: "alarm-id", content: content, trigger: trigger)
    UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    UNUserNotificationCenter.current().add(request)
}
func lastDay(offset: Int) -> Int {
    let cal = Calendar.current
    let m = cal.component(.month, from: Date()) + 1
    let y = cal.component(.year, from: Date())
    var comps = DateComponents(calendar: cal, year: y, month: m)
    comps.setValue(m + 1, for: .month)
    comps.setValue(0, for: .day)
    let date = cal.date(from: comps)!
    return cal.component(.day, from: date)
} }


extension ViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // offset is set to 1 as we want to get last of next month now.
    setNotificationRequest(offset: 1)
    completionHandler([.badge, .sound])
} }

I have tested it, working fine for me.

Ali Murad
  • 122
  • 1
  • 5
  • Thanks. however it looks like this solution relies on the user to open the App to schedule to next round? That's not ideal... – Bonan Nov 03 '21 at 22:02
  • Next notification request is setting in userNotificationCenter call back, which mean when user shown a notification application automatically set request for next notification so user won't have to open application. – Ali Murad Nov 04 '21 at 08:12
  • as the document said: ```userNotificationCenter(_:willPresent:withCompletionHandler:) Asks the delegate how to handle a notification that arrived while the app was running in the foreground.``` If a notification is shown in the notification center with the App in the background, this won't get called. I'm thinking of solving this issue by schedule 12 yearly notifications instead of monthly ones... – Bonan Nov 04 '21 at 12:51