8

So I am able to schedule notifications like so;

//iOS 10 Notification
if #available(iOS 10.0, *) {

    var displayDate: String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = DateFormatter.Style.full
        return dateFormatter.string(from: datePicker.date as Date)
    }
    let notif = UNMutableNotificationContent()


    notif.title = "I am a Reminder"
    notif.subtitle = "\(displayDate)"
    notif.body = "Here's the body of the notification"
    notif.sound = UNNotificationSound.default()
    notif.categoryIdentifier = "reminderNotification"

    let today = NSDate()
    let interval = datePicker.date.timeIntervalSince(today as Date)

    let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: false)

    let request = UNNotificationRequest(identifier: "reminderNotif", content: notif, trigger: notifTrigger)

    UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
        if error != nil {
            print(error)
           // completion(Success: false)
        } else {
            //completion(Sucess: true)
        }
        })
}

I have asked for permissions in the appDelegate and the notifications appear fine with my custom view using the notification extension.

I have added notification actions in the appDelegate for the notification category; these also appear.

//Notifications Actions 

private func configureUserNotifications() {
    if #available(iOS 10.0, *) {

        let tomorrowAction = UNNotificationAction(identifier: "tomorrowReminder", title: "Remind Me Tomorrow", options: [])

        let dismissAction = UNNotificationAction(identifier: "dismissReminder", title: "Dismiss", options: [])


        let category = UNNotificationCategory(identifier: "reminderNotification", actions: [tomorrowAction, dismissAction], intentIdentifiers: [], options: [.customDismissAction])

        UNUserNotificationCenter.current().setNotificationCategories([category])

    } else {
        // Fallback on earlier versions
    }
}

I have the same category set in the notification extension .plist file. And in the notification extension I have the following to change the text when the user taps on a action.

 //Handle Notification Actions And Update Notification Window 


 private func didReceive(_ response: UNNotificationResponse, completionHandler done: (UNNotificationContentExtensionResponseOption) -> Void) {

    if response.actionIdentifier == "tomorrowReminder" {
        print("Tomrrow Button Pressed")
        subLabel.text = "Reminder For Tomorrow"
        subLabel.textColor = UIColor.blue
        done(.dismissAndForwardAction)
    }

    if response.actionIdentifier == "dismissReminder" {
        print("Dismiss Button Pressed")
        done(.dismiss)

    } else {
        print("Else response")
        done(.dismissAndForwardAction)
    }

}

However the text does not change and none of the statements are called;

Over in the appDelegate I have the following;

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().delegate = self
        configureUserNotifications()

    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {

@available(iOS 10.0, *)
private func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .sound])
}

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

    print("Recieved Action For \(response.actionIdentifier)")

    if response.actionIdentifier == "tomorrowReminder" {
        print("Tomorrow Reminder")


        //Set new reminder for tomorrow using the notification content title



        completionHandler()
    }

    if response.actionIdentifier == "dismissReminder" {
        print("Dismiss Reminder...")
        completionHandler()
    }
}

}

Neither of these functions are actually called in the appDelegate either. I am not sure if the problem with updating the extension view is related to the app delegate. I don't think so, I have followed Apple's WWDC video as well as other tutorials and look at the document API and can't figure out;

  • Why is the Notification Extension Text Labels not updating ?
  • Why are the functions in the appDelegate not getting called ?
  • How can I using the notification content in the app delegate to use for the action ?

PS: I have spent the past few weeks researching and trying to figure this out, it seemed fairly straight forward and I am unsure what I am missing. I know I am not the only one having these issues.

mfaani
  • 33,269
  • 19
  • 164
  • 293
A.Roe
  • 973
  • 3
  • 15
  • 34

1 Answers1

5

I haven't checked whole code of yours, but at least, these function headers need to be changed as follows:

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

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

func didReceive(_ response: UNNotificationResponse,
                completionHandler done: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {

Simple rule: Remove private, add @escaping.

You may have received wrong suggestions from Xcode, but with making them private, Objective-C entry points are not generated. iOS runtime uses Objective-C selectors internally, so it cannot find your methods, thus, they are not executed.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • What does `@escaping` mean and do? – mfaani Sep 11 '16 at 04:34
  • Omg thank you ! Do you know how I could `notification.request.content.title` over in the app delegate to schedule a new notification ? For example I am trying to schedule a reminder and have an action to remind again for tomorrow – A.Roe Sep 11 '16 at 21:15
  • Doesn't matter all I needed to do was use `response.notification.request.content.title` – A.Roe Sep 11 '16 at 21:40
  • 1
    @Honey, `@escaping` means "this closure may be executed later". Compiler can easily optimize non-escaping closures which are often used in `map`, `filter` and such, Swift 3 has made it default. All completion handlers are executed when some task is completed -- later, so we need to annotate `@escaping` for all completion handlers in Swift 3. – OOPer Sep 11 '16 at 22:35
  • @OOPer is there any documentation on `@escaping`. I couldn't find any. FYI I usually find it difficult to find proper Swift documentation... – mfaani Sep 12 '16 at 14:01
  • 1
    @Honey, there's [a nice article here in SO](http://stackoverflow.com/a/39063972/6541007). [swift.org's article](https://swift.org/migration-guide/) is not so kind, but one of the must-see for who work with Swift 2&3.Apple's documentations are improving, [the latest references](https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter) (unfortunately not all up-to-date) and [Xcode release notes](https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html) give us a little info. – OOPer Sep 12 '16 at 21:14