Bonan
Bonan

Reputation: 719

How to set a local notification to fire on the last day of each month using UNNotificationRequest?

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())

Upvotes: 1

Views: 431

Answers (1)

Ali Murad
Ali Murad

Reputation: 132

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.

Upvotes: 3

Related Questions