SWIFT - call function at specific time of day - ios

Im trying to make an app and at 9am I want to call a function and send an a notification. Then another at 12pm, and another at 6pm. Everyday. Does anyone know the best way how?

Use a UNCalendarNotificationTrigger :
var dateComponents = DateComponents()
dateComponents.hour = 9
dateComponents.minute = 0
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents,
repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString,
content: YourContent,
trigger: trigger)

Related

Repeating iOS Notification

Is there any way to schedule local notification which fires at specific date and the repeats every minute?
Example: user receive first notification at 8:00 AM and then 8:01, 8:02...
To schedule repeated notification you need date components in trigger initialization.
e.g.
let date = Date(timeIntervalSinceNow: 3600)
let triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
trigger repeats notification daily
try to setup trigger using only .second in date components
let date = Date(timeIntervalSinceNow: 3600)
let triggerDaily = Calendar.current.dateComponents([.second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)

Triggering all pending notifications before the time set for it

In the app I'm developing, there is an option for triggering notification x amount of time before the actual time set for the said notification. For example I set the reminder for 10:00. But in the app's local settings, I set the notification to trigger 10 minutes before the time set. So, in this example's case, the notification will trigger in 9:50.
Now, I can do the above when I'm setting time for individual notification. But what I want to do is trigger all pending notifications before the actual time set for it.
This is the function I'm using to set notifications:
func scheduleNotification(at date: Date, identifier: String, threadIdentifier: String, body: String) {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, year: components.year, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "TestNotification"
content.body = body
content.threadIdentifier = threadIdentifier
content.sound = UNNotificationSound.default()
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) {
error in
if let error = error {
print("Error in delivering notification. Error: \(error.localizedDescription)")
}
}
}
The date is coming from the date set by the date picker. I tried to change trigger properties by using this code:
UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
for request in requests {
request.trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10*60, repeats: false)
}
}
But now I get an error saying 'trigger' is a get-only property.
There is no way to change fire time of a scheduled notification , you can remove all of them and re-schedule again

Set local notifications for particular days and time swift 3

In my app, i want to add local notifications. The scenario will be that the user can select any time and days from Mon to Sun. For example, if the user selects Mon, Thur and Sat as days and time as 11:00 pm, so now the user should be notified at all the selected days and that particular time.
Code:
let notification = UNMutableNotificationContent()
notification.title = "Danger Will Robinson"
notification.subtitle = "Something This Way Comes"
notification.body = "I need to tell you something, but first read this."
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
// let test = UNCalendarNotificationTrigger()
let request = UNNotificationRequest(identifier: "notification1", content: notification, trigger: notificationTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
I am using this code but this doesn't work according to what I need.
To get local notifications that repeat on a certain weekday at a certain time you can use a UNCalendarNotificationTrigger:
let notification = UNMutableNotificationContent()
notification.title = "Danger Will Robinson"
notification.subtitle = "Something This Way Comes"
notification.body = "I need to tell you something, but first read this."
// add notification for Mondays at 11:00 a.m.
var dateComponents = DateComponents()
dateComponents.weekday = 2
dateComponents.hour = 11
dateComponents.minute = 0
let notificationTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: "notification1", content: notification, trigger: notificationTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
If you want notifications on Monday, Thursday and Saturday at 11:00 you need to add 3 separate requests. To be able to remove them you have to keep track of the identifiers.

How to update the daily local notification message in iOS 10?

I'm trying to find out how to update the message in the local notifications when it is repeated daily.
Currently I have the following code in my AppDelegate:
func scheduler(at date: Date, numOfNotes: Int)
{
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, hour: components.hour, minute: components.minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: true)
content.badge = numOfNotes as NSNumber
content.body = "REMINDER: " + String(numOfNotes) + " needs to be looked at!"
content.sound = UNNotificationSound.default()
let request = UNNotificationRequest(identifier: "reminderNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request) {(error) in
}
}
I am storing numOfNotes in UserDefaults. I have a UISwitch in my UITableViewCell, that upon being switched on calls the scheduler function like so:
func remindMeSwitch(_ remindMeSwitch: UISwitch)
{
numOfNotes = UserDefaults.standard.integer(forKey: "Notes")
let delegate = UIApplication.shared.delegate as? AppDelegate
delegate?.scheduler(at: time, numOfNotes: numOfNotes)
}
However, when setting the repeats parameter to true to have the notification repeat daily at the specified time, numOfNotes is only called once, which is when I toggle the UISwitch on.
How can I set the notification to alert daily but still be able to update the notification message as needed?
Thanks.
In general, you don't have any possibility to change Local Notifications.
Only one way - delete/cancel the old notification and create the new notification. But you can use a copy function.
For example, if you want to change the notification's content:
// create new content (based on old)
if let content = notificationRequest.content.mutableCopy() as? UNMutableNotificationContent {
// any changes
content.title = "your new content's title"
// create new notification
let request = UNNotificationRequest(identifier: notificationRequest.identifier, content: content, trigger: notificationRequest.trigger)
UNUserNotificationCenter.current().add(request)
}

UNCalendarNotificationTrigger doesn't get stored unless repeats is true

I've noticed that if I create an UNCalendarNotificationTrigger with a custom date it does't get added unless i put:
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: **true**)
Apple Example is:
let date = DateComponents()
date.hour = 8
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
which make sense to be repeats == true.
In my scenario I dont need to create one notification that gets repeated many times, but I need multiple notificaitons fired only once on a specific calendar date (which is of course in the future)..
If I'm doing:
let calendar = Calendar(identifier: .gregorian)
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
let newdate = formatter.date(from: "20161201")
let components = calendar.dateComponents(in: .current, from: newdate!)
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
then i always get 0 pending notifications...
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (notifications) in
print("num of pending notifications \(notifications.count)")
})
num of pending notification 0
Any idea?
EDIT1:
Adding other context as pointed out by one of the answers.
I'm actually adding the request to the current UNUserNotificationQueue.
let request = UNNotificationRequest(identifier: "future_calendar_event_\(date_yyyyMMdd)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
// Do something with error
print(error.localizedDescription)
} else {
print("adding \((request.trigger as! UNCalendarNotificationTrigger).dateComponents.date)")
}
}
I have the same problem and I solve it now. It is due to dateComponents' year.
In order to solve this problem, I test the following code:
1.
let notificationCenter = UNUserNotificationCenter.current()
let notificationDate = Date().addingTimeInterval(TimeInterval(10))
let component = calendar.dateComponents([.year,.day,.month,.hour,.minute,.second], from: notificationDate)
print(component)
let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false)
let request = UNNotificationRequest(identifier: item.addingDate.description, content: content, trigger: trigger)
self.notificationCenter.add(request){(error) in
if let _ = error {
assertionFailure()
}
}
In console, print component:
year: 106 month: 2 day: 14 hour: 12 minute: 3 second: 42 isLeapMonth: false
And in this case, the notification cannot be found in pending notification list.
2.When I set component's year explicitly to 2017:
let notificationDate = Date().addingTimeInterval(TimeInterval(10))
var component = calendar.dateComponents([.year,.day,.month,.hour,.minute,.second], from: notificationDate)
component.year = 2017
print(component)
let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false)
let request = UNNotificationRequest(identifier: item.addingDate.description, content: content, trigger: trigger)
self.notificationCenter.add(request){(error) in
if let _ = error {
assertionFailure()
}
}
In console, the component is:
year: 2017 month: 2 day: 14 hour: 12 minute: 3 second: 42 isLeapMonth: false
Then this notification can be found in pending notification list.
And next, I check in the pending notification requests to find whether the trigger-date's year component is 106 or 2017:
notificationCenter.getPendingNotificationRequests(){[unowned self] requests in
for request in requests {
guard let trigger = request.trigger as? UNCalendarNotificationTrigger else {return}
print(self.calendar.dateComponents([.year,.day,.month,.hour,.minute,.second], from: trigger.nextTriggerDate()!))
}
}
I find the trigger's nextTriggerDate components are:
year: 106 month: 2 day: 14 hour: 12 minute: 3 second: 42 isLeapMonth: false
Conclusion
So if you want to set the trigger's repeats to false, you should make sure the trigger date is bigger than current date.
The default dateComponents' year may be unsuitable, such as 106. If you want the notification to fire in 2017, you should set the components year to 2017 explicitly.
Perhaps this is a bug, because I set trigger's dateComponents' year to 2017 but get 106 in nextTriggerDate of pending notification request.
On my app, I request permission after notification set by mistake. So If I want to get pending notification count, I got 0.
I requested permission on AppDelegate but notifications setted on first view viewdidload(). I added a notification function to trigger with button. After get permission click button and finally setted my notification and I got pending notification count 1.
I hope that's will help you.
UNCalendarNotificationTrigger creates the schedule for which a notification should occur, but it does not do the scheduling. For this you need to create a UNNotificationRequest and then add this to the notification center. Something along the lines of:
let request = UNNotificationRequest(identifier: "MyTrigger", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
// Do something with error
} else {
// Request was added successfully
}
}

Resources