I was reading how handling remote notification on developer apple guide. My questions are two:
what is the interpretation of the phrase
The notification is delivered when the application isn’t running in
the foreground
isn’t running in the foreground covers background and not running or in the background state just. Based on the interpretation, the following sentence:
the application icon is tapped on a device running iOS, the application calls the same method, but furnishes no information about the notification.
has a different sense.
The second question concerns the situation where I get two consecutive remote notifications: When I open the app in the method
application:didFinishLaunchingWithOptions:
or
application:didReceiveRemoteNotification:
I have information on all notifications or just the last?
If your application is running, either in background suspended state or foreground, the method that will get called upon receiving a notification will always be application:didReceiveRemoteNotification:.
If your application is not running at all, upon launch after receiving a notification, it will trigger application:didFinishLaunchingWithOptions: and the options will be a non-nil object containing information about your remote notification.
As far as I know, only the last notification info is available.
Related
Right now I have a framework that receives a silent notification, get the data from it (custom data) and translate it into a local notification to show the alert to the user (this is donde in didReceiveRemoteNotification:fetchCompletionHandler method). I have implemented this framework on an app and everything seems to be working correctly, silent notifications are being process when the app is in background and foreground. However, when the app is killed by the user or it is not running, I cannot receive notifications because of this:
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a remote
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
The reason I use this method for showing notifications is because the payload I sent to APNS has custom data with key-values that indicate how the notification must behave.
I've been doing some research and I found that Pushkit for VoIP can do the job. However, many post suggest that this can cause app rejection.
So my question is, how can I achieve receiving remote notifications even if my app was killed and considering that data in the payload has custom information to build the notification?
Silent push notifications are unreliable: they might get delayed, delivered in groups or even not delivered at all.
If you need to modify the content of the notification before presenting a banner for the user, you should use a Notification Service App Extension. You can also share some information between your app and this extension - using app groups or the keychain - if it needs something from your app to process the notification data.
When I receive a remote notification I updated a counter that I save to UserDefaults and I also show a local notification. Everything works as expected when the app is in the foreground, background, and suspended states Ref. When the app is in the Not Running state my counter is not updated nor is my local notification shown.
It is my understanding that I should be able to receive and process Remote Notifications while the app is completely off. A few articles online claim that when a Remote Notification arrives while in the Not Running state that the application:didFinishLaunchingWithOptions: should be called followed by application: didReceiveRemoteNotification:fetchCompletionHandler: but in my case it is not.
Is it possible to receive remote notifications while in Not Running state?
If your app is a VoIP app and you are using VoIP pushes through PushKit then a push notification will launch your app from the terminated state in order to deliver the notification. If you are using standard push notifications then your app will not receive the notification if it is terminated. You can include an alert text with a 'silent' notification that will be displayed to the user in the event that your app is terminated in order to prompt them to launch your app.
First of all, it sounds like you have a silent notification set up. Once you add alert data to your push notification (information like the title, body etc.. you can find more on that here), it'll start to display on the lock screen.
Second, it's not possible for your application to launch from a push notification, silent or otherwise, if it's in the Not Running state. The documentation on this is actually incorrect, as it states that the application will only not be launched if the user has quit the app. However, this actually also applies for any circumstance under the not-running state, for example if your app has never been launched since installation/rebooting, or if it was quit due to memory (a fairly common occurrence - iOS purges apps which haven't been run recently as required).
did you check this mark when app is background?
I already implemented this:
enable remote notifications in background modes
silent notification {"aps": {"content-available": 1}}
In Apple docs on Push Notification, they are saying:
When a silent notification arrives, iOS wakes up your app in the
background so that you can get new data from your server or do
background information processing.
#
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
After receive silent notification. In this method, I am downloading some data from the server then data synchronization.
EXPECTED:
The goal is when user open/using the app, it should reflect the latest position for the user.
ACTUAL:
Receive silent notification, download data from server, then data synchronization are works fine when app is running in foreground and background.
But can't wakes up the app in background and to do that when my app is not in running state (app is not launched or killed from app switcher).
QUESTION:
I silent notification doesn't work when app not running? (can't wake up app in background)
Is there missing code in my code which need implemented?
If this way doesn't work when app not running. Is there any way to keep my app local data always same/synced with the database server (when I'm using or not the app)? how other app do it?
Silent notifications are able to bring your app into the background to do any tasks that you may require, as per the Apple documentation:
the system wakes the app in the background (or launches it into the background)
The method you are using, userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:,
is called to let your app know which action was selected by the user for a given notification.
The correct method you should be implementing (even in iOS10) is:
application:didReceiveRemoteNotification:fetchCompletionHandler:. This tells the app that a remote notification arrived that indicates there is data to be fetched.
The way you are doing it is the correct (and probably most popular way) of syncing data between a server and application instance when the user is not actively engaging with the app. Your issue seems to be that you are using the wrong app delegate instance method for your callback.
Update 1:
As per your updated question, I have updated my answer.
That the notifications are not being delivered to your app after it has been terminated by the user is by design. According to the documentation:
The system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.
To combat this, you can look into Apple PushKit. Originally intended for VoIP apps, it would also work for your application. However unlike regular silent notifications, these do wake up your app even if it has been terminated by the user.
However, keep in mind that once a user terminates an application he expects to have terminated it for good (or until he manually restarts it). To override this would be poor design. Maybe it is fine with the user if the app spends some time downloading the latest data upon launch if the user had terminated the app previously? Depending on the exact reason why you need your app to have the latest information before launch, maybe you don't need to account for this scenario.
It looks like no matter I return UIBackgroundFetchResultNewData or UIBackgroundFetchResultNoData from performFetchWithCompletionHandler, it clears all previous notifications (local or push) for my app. This is not acceptable for me.
Anyone knows how do I do fetch and not make previous notifications cleared (disappear)?
My guess here is that you had a background fetch that was setting your badge number to 0, if that ever happens all your notifications are cleared.
Source: Apple's AppDelegate API Reference
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
Tells the app that a remote notification arrived that indicates there is data to be fetched. Use this method to process incoming remote notifications for your app. Unlike the
application(_:didReceiveRemoteNotification:)
method, which is called only when your app is running in the foreground, the system calls this method when your app is running in the foreground or background. In addition, if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a remote notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.
If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification.
Note: You need to make sure that the app is checking for the state before settings the application's badge number to 0. Test the below cases:
Application running, receives a notification
Application terminated, receives a notification
Launch app directly from app icon
Launch app from notification received
Knowing the difference between the 3 approached below is essential in your use case.
Checking the notification object received in application(_:didReceiveRemoteNotification:)
Checking the notification object received in application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
Checking the didFinishLaunchingWithOptions for UIApplicationLaunchOptionsRemoteNotificationKey
I might by fighting the windmills but I need to put some clearance into my knowledge of remote notifications. I want to handle notification while my app is turned off (killed from the app switcher) but user decides to turn it on not by tapping on notification but by tapping on app icon.
As far is I know, when app is killed and a remote notification arrives user has to tap on notification to pass it in launchOptions from didFinishLaunchingWithOptions: but... I want to handle this notification when user taps on app icon and nothing is passed to lauchOptions. In that scenario, at the moment I have no notification at all...
Is it possible to handle notification in background and perform action on it no matter how the app has been launched? Maybe didReceiveRemoteNotification: fetchCompletionHandler:?
Yeah, you are fighting the windmills :)
I recommend you to read Local and Push Notifications in depth. It's a bit long but the doc is clarifying.
In a fragment of this doc you can read:
If the application icon is tapped on a device running iOS, the
application calls the same method
(application:didFinishLaunchingWithOptions:), but furnishes no
information about the notification.
And later on:
When handling remote notifications in
application:didFinishLaunchingWithOptions: or
applicationDidFinishLaunching:, the application delegate might perform
a major additional task. Just after the application launches, the
delegate should connect with its provider and fetch the waiting data.
Listing 2-5 gives a schematic illustration of this procedure.
So having said this you could have a list of sent notifications for each device in your server and each time your app launches check if the device has any pending notification.
From the Local and Push Notification Programming Guide
As a result of the presented notification, the user taps the action button of the alert or taps (or clicks) the application icon.
If the action button is tapped (on a device running iOS), the system launches the application and the application calls its delegate’s application:didFinishLaunchingWithOptions: method (if implemented); it passes in the notification payload (for remote notifications) or the local-notification object (for local notifications).
If the application icon is tapped on a device running iOS, the application calls the same method, but furnishes no information about the notification
So, if your application is launched from the icon then you won't receive the notification data. You need your app to query the service/data store that is responsible for sending the notification to see if there is outstanding data/transactions/whatever.