Schedule Local Notification Every N Days (Timezone Safe)

Schedule local notification every n days (timezone safe)

You can set them up upfront using UNCalendarNotificationTrigger for an n number of times and using an adjusted calendar for the current timeZone

import SwiftUI

class NotificationManager: NSObject, UNUserNotificationCenterDelegate{
static let shared: NotificationManager = NotificationManager()
let notificationCenter = UNUserNotificationCenter.current()

private override init(){
super.init()
requestNotification()
notificationCenter.delegate = self
getnotifications()
}

func requestNotification() {
print(#function)
notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in

if let error = error {
// Handle the error here.
print(error)
}

// Enable or disable features based on the authorization.
}
}
/// Uses [.day, .hour, .minute, .second] in current timeZone
func scheduleCalendarNotification(title: String, body: String, date: Date, repeats: Bool = false, identifier: String) {
print(#function)

let content = UNMutableNotificationContent()
content.title = title
content.body = body

let calendar = NSCalendar.current

let components = calendar.dateComponents([.day, .hour, .minute, .second], from: date)

let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: repeats)

let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
notificationCenter.add(request) { (error) in
if error != nil {
print(error!)
}
}
}
///Sets up multiple calendar notification based on a date
func recurringNotification(title: String, body: String, date: Date, identifier: String, everyXDays: Int, count: Int){
print(#function)
for n in 0..<count{
print(n)
let newDate = date.addingTimeInterval(TimeInterval(60*60*24*everyXDays*n))
//Idenfier must be unique so I added the n
scheduleCalendarNotification(title: title, body: body, date: newDate, identifier: identifier + n.description)
print(newDate)
}
}
///Prints to console schduled notifications
func getnotifications(){
notificationCenter.getPendingNotificationRequests { request in
for req in request{
if req.trigger is UNCalendarNotificationTrigger{
print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
}
}
}
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

completionHandler(.banner)
}
}
class ZuluNotTriggerViewModel:NSObject, ObservableObject, UNUserNotificationCenterDelegate{
@Published var currentTime: Date = Date()
let notificationMgr = NotificationManager.shared


///Sets up multiple calendar notification based on a date
func recurringNotification(title: String, body: String, date: Date, identifier: String, everyXDays: Int, count: Int){
print(#function)
notificationMgr.recurringNotification(title: title, body: body, date: date, identifier: identifier, everyXDays: everyXDays, count: count)

//just for show now so you can see the current date in ui
self.currentTime = Date()
}
///Prints to console schduled notifications
func getnotifications(){
notificationMgr.getnotifications()
}

}
struct ZuluNotTriggerView: View {
@StateObject var vm: ZuluNotTriggerViewModel = ZuluNotTriggerViewModel()
var body: some View {
VStack{
Button(vm.currentTime.description, action: {
vm.currentTime = Date()
})
Button("schedule-notification", action: {
let twoMinOffset = 120
//first one will be in 120 seconds
//gives time to change settings in simulator
//initial day, hour, minute, second
let initialDate = Date().addingTimeInterval(TimeInterval(twoMinOffset))
//relevant components will be day, hour minutes, seconds
vm.recurringNotification(title: "test", body: "repeat body", date: initialDate, identifier: "test", everyXDays: 2, count: 10)
})

Button("see notification", action: {
vm.getnotifications()
})
}
}
}

struct ZuluNotTriggerView_Previews: PreviewProvider {
static var previews: some View {
ZuluNotTriggerView()
}
}

Local notification trigger only last ten days based an expiry dates

You should add next day with current day and changed time zone as system zone identifier.

Try let me know :

func deviceExpiryReminder(){
let dateString = "2019-08-30 00:00:00"
let formatter : DateFormatter = DateFormatter()
formatter.timeZone = NSTimeZone.local
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: dateString)
if date != nil && date! > Date() {
let message = "Device Expire Reminder"
let title1 = "Your device is expire in {2} days"
let title2 = "Your device is due to expire"
let identifier = "DeviceExpire"
//trigger for every day from configured days before
let numberofDays = self.daysBetweenDates(startDate: Date(), endDate: date ?? Date())
for i in (0...numberofDays) {
let preTitleMsg = title1.replacingOccurrences(of: "{2}", with: String(numberofDays - i))
var title = preTitleMsg
if numberofDays == i {
title = title2 + " today"
} else if numberofDays - 1 == i{
title = title2 + " tomorrow"
}
let userInfo = ["reminderType": "Device Expire",
"title": title,
"message": message,
"identifier": identifier]
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: self.getCurrentTimeZone())!
let currentDay = Date()
let nextDate = Calendar.current.date(byAdding: .day, value: i, to: currentDay)
if nextDate! >= Date() {
createReminder(title, message, nextDate, false, NSCalendar.Unit.day, nil, userInfo, identifier + String(i))
}
}
}
}
func getCurrentTimeZone() -> String{
return String (TimeZone.current.identifier)
}
func createReminder(_ title: String, _ message: String, _ triggerDate: Date?, _ canRepeat: Bool, _ repeatInterval: NSCalendar.Unit, _ triggerDateComponents: DateComponents?, _ userInfo: [AnyHashable : Any]?, _ identifier: String) {
if #available(iOS 10.0, *) {
let content = UNMutableNotificationContent()
content.title = title
content.body = message
content.sound = UNNotificationSound.default
let center = UNUserNotificationCenter.current()
// *** create calendar object ***
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: self.getCurrentTimeZone())!
let components = calendar.dateComponents([.month, .hour, .year, .day], from: triggerDate!)
print("trigger dates one by one : \(triggerDate!)")
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: canRepeat)
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
center.add(request) { (error) in
if error != nil {
print("Error")
}else {
print("Notification added")
}
}
}
}

func daysBetweenDates(startDate: Date, endDate: Date) -> Int {
let daysBetween = Calendar.current.dateComponents([.day], from: startDate, to: endDate)
print(daysBetween.day!)
return daysBetween.day!
}

Daily Local Notifications Are Not Working

Look at the comments within the code

import SwiftUI
//struct and class should start with an uppercase
struct NotificationView: View {
//Central location for Notification code including the delegate
// A call to the notificationManager just like the line of code below has to be included in
// application(_:willFinishLaunchingWithOptions:) or
// application(_:didFinishLaunchingWithOptions:)
//https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate
//https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-an-appdelegate-to-a-swiftui-app
let notificationManager: NotificationManager = NotificationManager.shared
var body: some View {
VStack {
VStack {
Button("Request Permission") {
//Call a func here don't define it
notificationManager.requestAuthorization()
}
.frame(width: 200, height: 60, alignment: .center)
.foregroundColor(.black)
.background(Color.blue)
.cornerRadius(10.0)
.padding()
Button("Add Notifications For Morning") {
//Unique date components
var dateComponents = DateComponents()
dateComponents.hour = 6
dateComponents.minute = 30
//Reusable method
self.notificationManager.scheduleTriggerNotification(title: "Morning Time", body: "Wake Up And Be Productive!", categoryIdentifier: "reminder", dateComponents: dateComponents, repeats: true)
}
.padding()
Button("Add Notifications For Middle Of The Day") {
var dateComponents = DateComponents()
dateComponents.hour = 12
dateComponents.minute = 30
//Reusable method
self.notificationManager.scheduleTriggerNotification(title: "Middle Of The Day", body: "Did you have your daily run?", categoryIdentifier: "reminder", dateComponents: dateComponents, repeats: true)

}
.padding()
Button("Add Notifications For Night") {
var dateComponents = DateComponents()
dateComponents.hour = 20
dateComponents.minute = 51
//Reusable method
self.notificationManager.scheduleTriggerNotification(title: "Night Time", body: "Time to sleep", categoryIdentifier: "reminder", dateComponents: dateComponents, repeats: true)

}
.foregroundColor(.blue)
.padding()

Button("Print Notifications") {
//Reusable method
self.notificationManager.printNotifications()
}
.foregroundColor(.blue)
.padding()
Button("Delete Notifications") {
//Reusable method
self.notificationManager.deleteNotifications()
}
.foregroundColor(.blue)
.padding()
}
}
}
}
//You need a central location for the notification code because
// it is needed in more than 1 spot. At launch in the AppDelegate
// and wherever you schedule your notifications
class NotificationManager: NSObject, UNUserNotificationCenterDelegate{
//Singleton is requierd because of delegate
static let shared: NotificationManager = NotificationManager()
let notificationCenter = UNUserNotificationCenter.current()

private override init(){
super.init()
//This assigns the delegate
notificationCenter.delegate = self
}

func requestAuthorization() {
print(#function)
notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Access Granted!")
} else {
print("Access Not Granted")
}
}
}

func deleteNotifications(){
print(#function)
notificationCenter.removeAllPendingNotificationRequests()
}
///This is just a reusable form of all the copy and paste you did in your buttons. If you have to copy and paste make it reusable.
func scheduleTriggerNotification(title: String, body: String, categoryIdentifier: String, dateComponents : DateComponents, repeats: Bool) {
print(#function)
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.categoryIdentifier = categoryIdentifier
content.sound = UNNotificationSound.default

let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: repeats)

let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
notificationCenter.add(request)
}
///Prints to console schduled notifications
func printNotifications(){
print(#function)
notificationCenter.getPendingNotificationRequests { request in
for req in request{
if req.trigger is UNCalendarNotificationTrigger{
print((req.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()?.description ?? "invalid next trigger date")
}
}
}
}
//MARK: UNUserNotificationCenterDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

completionHandler(.banner)
}
}
struct NotificationView_Previews: PreviewProvider {
static var previews: some View {
NotificationView()
}
}

Repeating a Local Notification after every 14 days(two weeks)?

You can only set the repeat interval to a calendar unit. In order to get the right time interval you may have to set several notifications though.

If you for instance wanted a notification every 20 minutes you would have to create 3 notifications 20 minutes apart with a repeat interval of NSHourCalendarUnit.

The problem in your case is that the next thing up from the week unit is a month but a month is not exactly 4 weeks.

To actually set a notification for every 14 days you will have to create 26 notifications with a repeat interval of NSYearCalendarUnit.

Set Local Notification for Specific Time

Here is how you do it.

Fully Tested on Swift 2.0

//Note I have taken date time from date picker

   func scheduleLocalNotification() {
let localNotification = UILocalNotification()

localNotification.fireDate = fixNotificationDate(datePicker.date)
localNotification.alertBody = "Hey, you must go shopping, remember?"
localNotification.alertAction = "View List" // Change depending on your action

localNotification.category = "reminderCategory"

UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}

//This function will help you in generating fire date.

func fixNotificationDate(dateToFix: NSDate) -> NSDate {
let dateComponets: NSDateComponents = NSCalendar.currentCalendar().components([NSCalendarUnit.Day, NSCalendarUnit.Month, NSCalendarUnit.Year, NSCalendarUnit.Hour, NSCalendarUnit.Minute], fromDate: dateToFix)

dateComponets.second = 0

let fixedDate: NSDate! = NSCalendar.currentCalendar().dateFromComponents(dateComponets)

return fixedDate
}

Don't forget to request permission for push notification.

Link to my project i worked on
https://drive.google.com/open?id=0B2csGr9uKp1DR0ViZFZMMEZSdms



Related Topics



Leave a reply



Submit