greenygh0st / capacitor-plugin-silent-notifications

Capacitor plugin designed to allow silent push notifications on iOS.
2 stars 3 forks source link

Can silent push notifications be received when app is not open and not in background? #2

Closed bluepuma77 closed 3 months ago

bluepuma77 commented 3 months ago

Currently experimenting with capacitor-plugin-silent-notifications. It seems the silent push notifications arrive when 1) the app is running and when 2) the app is in background. When the app not running at all, it seems silent push notifications are not received.

Somewhere I read that only the native part gets invoked, but it seems that the function

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // debug
    print("Received by: didReceiveRemoteNotification w/ fetchCompletionHandler")

    // Perform background operation, need to create a plugin
    NotificationCenter.default.post(name: Notification.Name(rawValue: "silentNotificationReceived"), object: nil, userInfo: userInfo)

    // Give the listener a few seconds to complete, system allows for 30 - we give 25. The system will kill this after 30 seconds.
    DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
        // Execute after 25 seconds
        completionHandler(.newData)
    }
}

is not invoked at all. Is there a way to trigger an action in iOS native with a silent push when the app is not running at all?

greenygh0st commented 3 months ago

So for it work with Ionic I believe that the application has to be at least in the background, you can use that same handler to do native actions as well.

That said I am not sure exactly how its handled if the app is completed closed, my understanding is that it does not work at all even natively. Here is the specific docs page for background notifications: https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app

bluepuma77 commented 3 months ago

Did some tests and I really don't like the results:

Receive silent push | App active  | App background | App killed 
--------------------+-------------+----------------+--------------
Screen on           |      y      |       n*       |      n
--------------------+-------------+----------------+--------------
Screen off          |      n*     |       n*       |      n

* only active for 10-30 secs longer than last action. Constant pushes can keep alive.

I don't know if capacitor-plugin-silent-notifications is not called anymore, or of iOS is just not allowing the URL request in background. But that would be necessary for my use case.

    // capacitor-plugin-silent-notifications
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        // debug
        print("Received by: didReceiveRemoteNotification w/ fetchCompletionHandler")

        // Perform background operation, need to create a plugin
        NotificationCenter.default.post(name: Notification.Name(rawValue: "silentNotificationReceived"), object: nil, userInfo: userInfo)

        let formatter = ISO8601DateFormatter()
        let currentDate = Date()
        let isoDateString = formatter.string(from: currentDate)

        defaults.set("true", forKey: "CapacitorStorage.silentNotification." + isoDateString)
        print( "################" )
        print( defaults.dictionaryRepresentation().keys.filter { $0.hasPrefix("CapacitorStorage.silentNotification.") } )
        print(isoDateString)

        performHTTPRequest()

        // Give the listener a few seconds to complete, system allows for 30 - we give 25. The system will kill this after 30 seconds.
        DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
            // Execute after 25 seconds
            completionHandler(.newData)
        }
    }

    func performHTTPRequest() {
        guard let url = URL(string: "http://example.com/alive") else { return }
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }

            if let response = response as? HTTPURLResponse, response.statusCode == 200 {
                if let data = data, let dataString = String(data: data, encoding: .utf8) {
                    print("Response data string: \(dataString)")
                }
            } else {
                print("HTTP Request failed")
            }
        }
        task.resume()
    }

Any ideas how to improve this?

bluepuma77 commented 3 months ago

I have spent many, many hours on this, currently it seems to work. The function in AppDelegate.swift now calls native background download and regular download function, both show up on the server.

It seems to be very important to use the same identifier as the app in

URLSessionConfiguration.background(withIdentifier: "my.app.xyz")

Updated table:

Receive silent push | App active  | App background | App killed 
--------------------+-------------+----------------+--------------
Screen on           |      y      |       y        |      n
--------------------+-------------+----------------+--------------
Screen off          |      y      |       y        |      n