6

I'm scheduling a daily UserNorification to trigger everyday at a specific time, notifying the user to do something. But if the user does that X hours before the notification is fired, I need to cancel today's notification, in my opinion cancel all, and reschedule again but from tomorrow's specific time. For example, if today the notification should fire at 11:00 and the user "do that thing" at 10:00, the 11:00 notification should not be fired, and I need to schedule again at the same time but starting from tomorrow. And the cycle goes on and on, same for tomorrow. My questions are:

  1. Should I unschedule the first daily notifications using the following code: UNUserNotificationCenter.current().removeAllPendingNotificationRequests() ?

  2. How to schedule daily notifications starting from a specific date?

Slavcho
  • 2,792
  • 3
  • 30
  • 48
  • Are your notifications implementing a custom interface (i.e do you provide a `UNNotificationContentExtension`)? – spassas Dec 20 '16 at 13:42
  • No, they just have title and body. Reminding the user to do something. But if he do that thing, 1 hour before the hardcoded 11:00, the notification should not be presented that day, but to continue the next day. – Slavcho Dec 20 '16 at 13:47
  • 1
    I have done that in previous notifcation framework. I atach some userinfo with each type of notiification and when the user perform the steps earlier.I retrieve that particular notificatio ,cancel it and re-register it with tommorow date. – Muhammad Zohaib Ehsan Dec 27 '16 at 10:26
  • For now thats the only solution, using the old framework. But no need to send params in userInfo. The changes that the user can make it, are within the app, so when he do that, i will know whats the next notification and i will cancel it and reschedule for tomorrow. – Slavcho Dec 27 '16 at 10:50
  • but just for my learning how you know what notification you have to cancel. u have to give any identifier to it OR filter it through the date? – Muhammad Zohaib Ehsan Dec 27 '16 at 11:02
  • Well you achieve both ways. I'm doing that with some math, currentTime.hours == firstNotFireHour -> cancel First. And respectively for every notification, because their date changes but hour dont. – Slavcho Dec 27 '16 at 12:50
  • I am converting the current time to seconds and the notification's time to seconds and subtract them to see if it is before or after it. Simple math. Then i cancel all of them and reschedule again regarding the product from subtracting. – Slavcho Jan 19 '17 at 12:44

2 Answers2

3

Unfortunately, this kind of scheduling became ridiculously hard with the notifications framework in iOS 10. The only way I could find to do something similar is to use a hybrid of both UNCalendarNotificationTrigger and UNTimeIntervalNotificationTriggerAND implement a UNNotificationContentExtension.

The concept is the following:

Every time your action is being performed you need to reschedule your notifications. If the time is earlier than the desired notification time (e.g 11:00 in your example), instead of using a calendar trigger to reschedule, use a time interval trigger to set one, temporary, non-repeating notification for the next day (let's call it tempNotification)

let nextDayAt11AMDate = /* the timestamp of tomorrow at 11am */
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: nextDayAt11AMDate.timeIntervalSinceDate(NSDate()), repeats: false)
/* schedule tempNotification */

When this notification is received (and provided that a UNNotificationContentExtension has been implemented), in your extension's view controller didReceive: method which is called when the notification arrives, cancel existing notifications and schedule the actual, repeating notification normally using a calendar trigger (let's call it actualNoticfication)

let date = DateComponents()
date.hour = 11
date.minute = 00 
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
/* schedule actualNotification */

It's a long work-around, probably not very efficient and also not 100% reliable (e.g if the device is closed when the tempNotification arrives then the actualNotification will not be scheduled) but it covers the majority of cases. I would be very glad to find a better way than that...

spassas
  • 4,778
  • 2
  • 31
  • 39
  • 1
    So, when the app is suspended and `tempNotification` is received, the code will not be run, right? I guess there is no solution for this with the new Framework. But thanks for your effort. – Slavcho Dec 20 '16 at 14:48
  • @Slavcho No, if the app is suspended the code will work; when a notification is received the content extension is triggered regardless of the state of the app. It's when the device is completely off that things get more complicated. – spassas Dec 20 '16 at 14:53
  • Well in that case I will try the solution and if it works I will accept it. Just need to see how `UNNotificationContentExtension` works. – Slavcho Dec 20 '16 at 14:57
  • `UNNotificationContentExtension` is only used for devices that has/use 3D Touch. So in my case, with iPhone 6, i can't make it works... Or if you have some demo to show me, i will try it. – Slavcho Dec 21 '16 at 08:16
  • @Slavcho `UNNotificationContentExtension` works for all iOS 10 devices (those that are not 3D Touch enabled can access the notification content by tapping on the view button). Regardless, you don't need to interact with the notification in order for the `didReceive:` to be invoked. I will try to provide some more code later (unfortunately it is an exceedingly busy day) – spassas Dec 21 '16 at 10:08
  • I've implemented everything, the custom notification is showing, but didReceive is not called. Am I missing something? – Slavcho Dec 21 '16 at 15:03
  • Now I get it. It is getting called when i interact with the notification -> dragging down. Is there any way to trigger didReceive without interacting the notification? – Slavcho Dec 21 '16 at 15:45
  • I think you mean `UNNotificationServiceExtension`, not `UNNotificationContentExtension` – Rizwan Sattar May 22 '17 at 15:06
  • 1
    @RizwanSattar the question is for local notifications, `UNNotificationServiceExtension` is available for remote notifications – spassas May 22 '17 at 15:50
  • @spassas I see - so you have to implement a dummy UIViewController for the content extension which will actually show no UI, because `didReceive:` is called even without the notification being interacted with? – Rizwan Sattar May 22 '17 at 16:08
  • I'm getting the same problem with @Slavcho, that `didReceive:` is only called when the notification has been interacted with. – Joseph Williamson Oct 06 '17 at 15:51
0

After researching, this is not possible with the new UserNotifications Framework. We must use the old UILocalNotification approach to achieve this. Because that code is deprecated in iOS10, I've changed the supported version to be from 9+, so there will be no warnings. This is a permanent approach, but lets hope that Apple will implement and extend the old feature in the new framework.

Slavcho
  • 2,792
  • 3
  • 30
  • 48