40

I am scheduling new notifications in iOS10, like this:

func scheduleNotification (event : Meeting, todaysBadgeCounter: Int) {

    if #available(iOS 10.0, *) {

        let minutesBefore = 10
        //interval in seconds from current point in time to notification
        let interval : NSTimeInterval = NSTimeInterval(secondsFromNowTo(event.startTime.dateByAddingTimeInterval(-minutesBefore * 60)))

        //only schedule in the future
        if(interval > 0){


            let category = NotificationsController.notificationCategory

            let center = NotificationsController.notificationCenter
            center.setNotificationCategories([category])
            let content = UNMutableNotificationContent()

            content.title = NSString.localizedUserNotificationStringForKey(event.title, arguments: nil)
            if(minutesBefore <= 1){
                content.body = NSString.localizedUserNotificationStringForKey("IOS10: Your \(event.title) is about to start", arguments: nil)
            }else{
                content.body = NSString.localizedUserNotificationStringForKey("IOS10: You have \(event.title) in \(Int(minutesBefore)) minutes", arguments: nil)

            }
            content.sound = UNNotificationSound.defaultSound()

            let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: interval, repeats: false)
            let identifier = NSString.localizedUserNotificationStringForKey("sampleRequest\(event.UUID)", arguments: nil)
            let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)

            //setting the delegate
            center.delegate = self

            center.addNotificationRequest(request, withCompletionHandler: { (error) in
                // handle the error if needed
                log.error(error?.localizedDescription)
                print("SCHEDULING >=iOS10:", event.title, ", interval:", interval)
            })
        }


//return category
@available(iOS 10.0, *)
class var notificationCategory : UNNotificationCategory {
    struct Static {
        static let callNow = UNNotificationAction(identifier: NotificationActions.callNow.rawValue, title: "Call now", options: [])
        static let clear = UNNotificationAction(identifier: NotificationActions.clear.rawValue, title: "Clear", options: [])
        static let category : UNNotificationCategory = UNNotificationCategory.init(identifier: "CALLINNOTIFICATION", actions: [callNow, clear], intentIdentifiers: [], options: [])

    }
    return Static.category
}

I am able to schedule notifications, and receive local notifications at the right time. BUT: my delegate methods that I used according to the tutorial are never executed, however the didReceiveLocalNotification is executed each time I tap on the notification:

extension NotificationsController: UNUserNotificationCenterDelegate {

@available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {

    print("IOS10 delivered")

    // Response has actionIdentifier, userText, Notification (which has Request, which has Trigger and Content)
    switch response.actionIdentifier {
    case NotificationActions.NotifyBefore.rawValue:
        print("notify")
        break

    case NotificationActions.callNow.rawValue:
        print("callNow")
        break
    case NotificationActions.clear.rawValue:
        print("clear")
    default: break
    }
}

@available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {

        // Delivers a notification to an app running in the foreground.
        print("IOS10 delivered 2222")

    }
}

Wasn't didReceiveLocalNotification deprecated? How to make these methods to be called?

UPDATE:

I updated my code with some suggestions from here, namely:

  1. I added assigning of the UNUserNotificationCenter.delegate to the applicationDidFinishLaunchingWithOptions.
  2. I also tried moving these methods (delegate methods) out of the extension to the NotificationsController.swift class, and setting this class as UNUserNotificationCenterDelegate. Did not work for me either.
Async-
  • 3,140
  • 4
  • 27
  • 49
  • Hey there I am having the exact same problem, I am just wondering have you set `UINotificationExtensionDefaultContentHidden` in the extension .plist file to YES ? If so did it work ? This shouldn't effect your question, just I am having the same issue as you and wondering if you are able to hide the default content – A.Roe Sep 08 '16 at 00:16
  • 1
    Hi, no I haven't changed anything in the .plist – Async- Sep 08 '16 at 08:36
  • have you conform and implemented UNUserNotificationCenterDelegate in your appdelegate as well? I am getting call for those methods. – Avijit Nagare Sep 08 '16 at 12:40
  • yes, I have in fact implemented it in AppDelegate. Do you use swift3 or swift2.3 ? – Async- Sep 08 '16 at 14:10
  • Are you using a custom Notification extension interface ? If so are you able to change the text labels/images ? Also maybe the identifier for the notification request when scheduling has something to do with it – A.Roe Sep 08 '16 at 22:33
  • @AvijitNagare can you share your project? or make a small project out of your project with only relevant code (if you cant share)? – Async- Sep 09 '16 at 12:00
  • @Async-alright before that just go through https://swifting.io/blog/2016/08/22/23-notifications-in-ios-10/ – Avijit Nagare Sep 09 '16 at 13:31
  • @Async- what version of iOS your app supports? – Adam Smaka Oct 03 '16 at 09:14

11 Answers11

23

Request identifier is not the notification category.

Just add this line:

content.categoryIdentifier = identifier

Update: Just made a simple app. Everything seems to working fine:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    UNUserNotificationCenter.current().delegate = self

    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
        if granted {
            self.registerCategory()
            self.scheduleNotification(event: "test", interval: 3)
            self.scheduleNotification(event: "test2", interval: 5)
        }
    }

    return true
}

func registerCategory() -> Void{

    let callNow = UNNotificationAction(identifier: "call", title: "Call now", options: [])
    let clear = UNNotificationAction(identifier: "clear", title: "Clear", options: [])
    let category : UNNotificationCategory = UNNotificationCategory.init(identifier: "CALLINNOTIFICATION", actions: [callNow, clear], intentIdentifiers: [], options: [])

    let center = UNUserNotificationCenter.current()
    center.setNotificationCategories([category])

}

func scheduleNotification (event : String, interval: TimeInterval) {
    let content = UNMutableNotificationContent()

    content.title = event
    content.body = "body"
    content.categoryIdentifier = "CALLINNOTIFICATION"
    let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: interval, repeats: false)
    let identifier = "id_"+event
    let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)

    let center = UNUserNotificationCenter.current()
        center.add(request, withCompletionHandler: { (error) in
    })

}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    print("didReceive")
    completionHandler()
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    print("willPresent")
    completionHandler([.badge, .alert, .sound])
}

}

Update 2: Rewritten in Swift 2.3

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

var window: UIWindow?

func applicationDidFinishLaunching(application: UIApplication) {
    UNUserNotificationCenter.currentNotificationCenter().delegate = self
    UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptions([.Badge, .Sound, .Alert]) { (granted, error) in
        if granted {
            self.registerCategory()
            self.scheduleNotification("test", interval: 3)
            self.scheduleNotification("test2", interval: 5)
        }
    }
}


func registerCategory() -> Void{

    let callNow = UNNotificationAction(identifier: "call", title: "Call now", options: [])
    let clear = UNNotificationAction(identifier: "clear", title: "Clear", options: [])
    let category : UNNotificationCategory = UNNotificationCategory.init(identifier: "CALLINNOTIFICATION", actions: [callNow, clear], intentIdentifiers: [], options: [])

    let center = UNUserNotificationCenter.currentNotificationCenter()
    center.setNotificationCategories([category])

}

func scheduleNotification(event : String, interval: NSTimeInterval) {
    let content = UNMutableNotificationContent()

    content.title = event
    content.body = "body"
    content.categoryIdentifier = "CALLINNOTIFICATION"
    let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: interval, repeats: false)
    let identifier = "id_"+event
    let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)

    let center = UNUserNotificationCenter.currentNotificationCenter()
    center.addNotificationRequest(request) { (error) in

    }
}

func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {

    print("willPresent")
    completionHandler([.Badge, .Alert, .Sound])
}

func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {

    print("didReceive")
    completionHandler()
}

}
Nagendra Rao
  • 7,016
  • 5
  • 54
  • 92
livsik
  • 416
  • 3
  • 7
  • I am getting the same issue. Were you able to fix your code? – user1140780 Sep 19 '16 at 07:50
  • @Async- I've added example in swift 2.3. Seems to be working for me. – livsik Sep 23 '16 at 10:46
  • yes, it works!!! but only the print("didReceive") is being printed, the willPresent is not printed, but that is already enough for me to work! thanks man, I will accept this as an answer – Async- Sep 23 '16 at 13:11
  • "willPresent" is only called when app is in foreground – livsik Sep 23 '16 at 13:48
  • I'm glad that i was able to help – livsik Sep 23 '16 at 13:49
  • question though: I implemented the same code in my separate class, and it stopped working. Is there a special need for it to be in AppDelegate? – Async- Sep 28 '16 at 13:35
  • In my production code, all notification stuff is in separate class. And everything works fine. As far as I remember the only requirement is that UNUserNotificationCenter.delegate is set before the completion of ApplicationDidFinishLoading method. – livsik Sep 29 '16 at 09:27
  • @livsik.. please look to my question.I also struck in displaying message in swift http://stackoverflow.com/questions/41198651/xcode-8-1-push-notifications-in-swift-2-3-with-firebase-integration-not-getting – Uma Madhavi Dec 19 '16 at 10:27
6

Use belwo delegate method for Swift 2.3:

func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void)
user2084611
  • 450
  • 3
  • 8
  • Hi. My push notifications are not displaying from top.Please check my code and guide me how can i achieve this http://stackoverflow.com/questions/41198651/xcode-8-1-push-notifications-in-swift-2-3-with-firebase-integration-not-getting – Uma Madhavi Dec 19 '16 at 10:34
5

Make sure your AppDelegate implementing UNUserNotificationCenterDelegate protocol.

For Swift

let center = UNUserNotificationCenter.current()
center.delegate = self

For Objective-c

//set delegate to self
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];

Assigning delegate to self will trigger following methods.

// App in foreground
    private func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
      print("willPresent")
      }
//On Action click
     private func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
      print("didReceive")
      }
Avijit Nagare
  • 8,482
  • 7
  • 39
  • 68
  • as you can see in the code snippet I posted in my question: I have it already. I also tried to put it in the AppDelegate, like @spasass suggested. That did not help, still see the same issue. – Async- Sep 08 '16 at 11:08
4

According to the UNUserNotificationCenterDelegate docs:

Important

You must assign your delegate object to the UNUserNotificationCenter object no later before your app finishes launching. For example, in an iOS app, you must assign it in the applicationWillFinishLaunching(:) or applicationDidFinishLaunching(:) method.

So, it could be an issue of setting the Notification Centre delegate too late.

Community
  • 1
  • 1
spassas
  • 4,778
  • 2
  • 31
  • 39
4

I have found the answer for this. Below delegate method is called while running app on Xcode 8 with Swift 2.3 and minimum deployment target as iOS 9.3.

func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void)

In swift 3.0 Use,

func userNotificationCenter(_ center: UNUserNotificationCenter, 
                      didReceive response: UNNotificationResponse, 
           withCompletionHandler completionHandler: @escaping () -> Void)

Reference: https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter

user1140780
  • 988
  • 2
  • 13
  • 29
  • Read why @escaping is needed in Swift 3 here: https://cocoacasts.com/what-do-escaping-and-noescaping-mean-in-swift-3/ – malhal Jan 06 '17 at 02:27
3

Check to make sure that only your AppDelegate is set as the UNUserNotificationCenter Delegate.

Are you using...

UNUserNotificationCenter.current().delegate = self

... more than once? I was trying to catch notifications with different outcomes in all of my view controllers by changing the delegate in each one and having each use this function:

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
   // code
}

The issue for me was that I hadn't implemented their functionality yet, and so the original userNotificationCenter function "didReceive" in my AppDelegate wasn't being called. This may be why yours isn't getting called.

Caleb Hensley
  • 353
  • 2
  • 7
2

For Swift 3.0

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

    print("** willPresent")
    completionHandler([.badge, .alert, .sound])
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    print("** didReceive")
    completionHandler()
}
1

You are using incorrect function signatures

The correct function signatures in swift are:

func userNotificationCenter(UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) {

//your code here

}

and

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

//your code here

}
dcrow
  • 570
  • 5
  • 11
  • changing it to this did not help, still do not hit the UNUserNotificationCenterDelegate methods. (signature was initially like you suggested - from the example as well (_ center: .... ) - but xcode complained about 'Extraneous '_' in parameter 'center' has no keyword argument name', so I had to change it how xcode suggested. – Async- Aug 16 '16 at 08:44
  • Why are you creating an extension with the delegate implementation? – dcrow Aug 16 '16 at 22:10
  • Also, what object instance are you setting as the delegate? You want to set an (allocated and initialized) instance of one of your classes as the delegate. Right now, it looks like you have made an extension where you've implemented the delegate methods that should be implemented in the (allocated and initialized) class instance where you would like to receive the callbacks. – dcrow Aug 16 '16 at 22:17
  • I was confused a bit, now I'm having a NotificationsController class, that is a UserNotificationsCenter's delegate (center.delegate = self - during registering of notifications through that class). Also in that class I have extension methods that are supposed to be called. But it's now not happening – Async- Aug 23 '16 at 11:06
  • I am setting this class as Center's delegate, just before scheduling the notification request. I have an example project, where it's working and is being done in exactly same way, BUT in swift3. In that case the only difference is that he does not have a completion handler in the UNUserNotificationCenter.current().add(request){(error) in if (error != nil){ //handle here } } – Async- Sep 01 '16 at 12:30
  • 1
    ok, in the example project it also does not execute the delegate methods... I am completely stuck here – Async- Sep 07 '16 at 10:52
1

I had the same issue, didReceive never called when user tap on Notification with this function signatures:

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

private func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,withCompletionHandler: @escaping (UNNotificationPresentationOptions) -> Void)

but for me fix the problem with this signatures:

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

internal func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,withCompletionHandler: @escaping () -> Void)
TylerH
  • 20,799
  • 66
  • 75
  • 101
keto 182
  • 11
  • 1
  • 1
0

The docs says to set the delegate in applicationWillFinishLaunching(:) or applicationDidFinishLaunching(:). So include the following code in AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
   UNUserNotificationCenter.current().delegate = self
}

After this delegate is being set, the following willPresent function will be called.

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
   print("willPresent")
   completionHandler([.alert, .sound])
}
bartosss
  • 1,030
  • 9
  • 16
0

I had the same issue.

Replacing

[UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:NO];

With

[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];

Helped me. Sure this does other thing but at least it works now