1

My app can receive background notifications through PushKit. These PushKit notifications trigger an action to clean up previously delivered regular notifications.

When I terminate the app and receive a PushKit notification, viewDidAppear is triggered on my initial view controller (as configured in the storyboard). This is causing some problems for me. I understand that PushKit launches your app in the background, but I don't understand why viewDidAppear is triggered; as the app is actually never opened.

pseudo AppDelegate.swift:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    voipRegistration()
    Messaging.messaging().delegate = self

    // setup Firebase/Bugfender

    UNUserNotificationCenter.current().delegate = self
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) {granted, _ in
        // setup regular notifications
    }

    application.registerForRemoteNotifications()
    return true
}

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)
}

func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
    // register token with server
}

fileprivate func voipRegistration() {
    let voipRegistry = PKPushRegistry(queue: nil)

    voipRegistry.delegate = self
    voipRegistry.desiredPushTypes = [.voIP]
}

I wonder if it's normal behaviour that viewDidAppear gets triggered through PushKit? The viewDidAppear in the initial controller starts my authentication process and I don't want that to happen while the app is in the background.

jp1987
  • 121
  • 15

2 Answers2

1

As described in the link @fewlinesofcode posted, viewDidAppear doesn't trigger when it physically appears on the screen, it triggers when it's added to the view controller hierarchy. So it makes sense that this triggers on startup.

I solved this using UIApplication.shared.applicationState, like so:

pseudo AppDelegate.swift:

    func applicationWillEnterForeground(_ application: UIApplication) {
        center.post(name: .ApplicationWillEnterForeground, object: self, userInfo: nil)
    }

pseudo BaseViewController.swift:

class BaseViewController: UIViewController {
    var applicationWillEnterForegroundObserver: NSObjectProtocol?
    let center = NotificationCenter.default

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if UIApplication.shared.applicationState != .background {
            authenticate()
        } else {
            if applicationWillEnterForegroundObserver == nil {
                applicationWillEnterForegroundObserver = center.addObserver(
                    forName: .ApplicationWillEnterForeground,
                    object: nil, queue: nil) { (_) in
                        self.center.removeObserver(self.applicationWillEnterForegroundObserver!)     
                        self.authenticate()
                    }
            }
        }
    }

    fileprivate func authenticate() {
        // do authentication logic
    }
}

This checks in viewDidAppear if the app is running in the background (like when PushKit launches the app). If it doesn't, simply authenticate and proceed. If it does, schedule a listener for applicationWillEnterForeground that authenticates as soon as the app actually comes to the foreground.

jp1987
  • 121
  • 15
0

As you can find from Apple official documentation

Upon receiving a PushKit notification, the system automatically launches your app if it isn't running. By contrast, user notifications aren't guaranteed to launch your app.

So yes, it is correct behaviour.

UPD: The key point is, that application is woken up after receiving of the PushKit notification. As soon as app is running it has some execution time and your code is executing.

fewlinesofcode
  • 3,007
  • 1
  • 13
  • 30