Spielo
Spielo

Reputation: 571

launchOptions always nil when launching from a push notification

I'm sending push notifications from a Django app (using django-push-notifications) to an iOS app. The app targets iOS 13 and I'm running it on an iPhone 7 running iOS 13.3.1. I'm debugging in Xcode 11.3.1

I'm trying two different methods to send the notification from the Django side:

Method 1:

devices.send_message(message={"title" : title, "body" : message}, thread_id="events", extra={"foo": "bar"})

Method 2:

devices.send_message("[will be overwritten]", extra={
    "aps": {
        "alert": {
            "title": "Bold text in the notification",
            "body": "Second line in the notification"
        },
        "sound": "default",
    },
    "foo": "bar"
})

As far as I can tell, both methods should result in a payload which looks like Method 2.

I'm debugging by doing the following:

  1. Set "wait for executable to be launched" in my device scheme
  2. Build and run in Xcode
  3. Ensure app has been killed in the task switcher
  4. Trigger sending of remote notification
  5. Tap on received notification to launch app

No matter what I do, launchOptions is always nil. I've tried setting a breakpoint to inspect the variables. I've tried using os_log to log to the console if launchOptions is not nil, and I've tried triggering an alert (following advice from this question) to rule out Xcode debugger interference. It's always nil.

My AppDelegate currently looks like this:

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

    let notificationOption = launchOptions?[.remoteNotification]

    let alert = UIAlertController(title: "Your title", message: notificationOption.debugDescription, preferredStyle: .alert)
    let cancel = UIAlertAction(title: "Cancel", style: .default, handler: { action in
    })
    alert.addAction(cancel)
    DispatchQueue.main.async(execute: {
        application.windows.first!.rootViewController?.present(alert, animated: true, completion: nil)

    })
    return true
}

The alert triggers, but the alert content simply reads "nil".

I can't figure out what's missing. It's possible that my notification payload isn't exactly what I think it is (I've asked on the Github page for django-push-notifications to confirm if there's an issue on that end). It's also possible I've missed a step in setting up remote notifications, but I do reliably receive the notifications and they display as I expect, so they seem to be working.

Any advice greatly appreciated!

Upvotes: 15

Views: 9480

Answers (3)

sahiljain
sahiljain

Reputation: 2374

In iOS 13.0 and above When the app is killed, if you tap on notification, would like to open the app and get hold of notification payload. Here is how you do it.

Please check for connectOptions under sceneDelegate

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//look for remote notification response
   if let response = connectionOptions.notificationResponse{
        print(response.notification.request.content.userInfo)
   }

   guard let _ = (scene as? UIWindowScene) else { return }
} 

Upvotes: 32

oski225
oski225

Reputation: 132

It seems that after changes in iOS 13 we don't have to process notifications in didFinishLaunchingWithOptions function.

We can just use:

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

        let userInfo = response.notification.request.content.userInfo

        if let aps = userInfo["aps"] as? [String: AnyObject] {
            // Do what you want with the notification
        }

      completionHandler()
    }
}

It works for any scenario when the user clicks on a notification.

Upvotes: 6

Spielo
Spielo

Reputation: 571

I didn't find a solution to this issue, but I found a workaround. I still have no idea why launchOptions was always nil, but I've been able to access the payload by doing the following:

In AppDelegate.swift:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

...

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UNUserNotificationCenter.current().delegate = self
        return true
    }

...

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

        switch actionIdentifier {
        case UNNotificationDismissActionIdentifier: // Notification was dismissed by user
            // Do something
            completionHandler()
        case UNNotificationDefaultActionIdentifier: // App was opened from notification
            // Do something
            completionHandler()
        default:
            completionHandler()
        }
    }

If I then set a breakpoint in userNotificationCenter, I can dig out the notification payload: Debugger screen shot (breakpoint)

Upvotes: 7

Related Questions