Apple Push Notification setting up Remote Notifications method overrides other methods - ios

I have properly set up my app for Remote Notifications with the method
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void)
and it processes it correctly now every time the app receives an Apple Push Notification.
But my issue is that this method is being called in all instances now.
didFinishLaunchingWithOptions does not seem to be called anymore? Or the launchOptions is coming up empty?
Basically I used the didReceiveRemoteNotification as a catch all to process any incoming notifications which is my intended purpose, but then when I click the notification itself, it fires the didReceiveRemoteNotification again. Thus processing the notification twice, which I do not want.
So this is how I want my app to handle notifications:
Anytime a notification is received, would like to automatically process the notification in the background
When user clicks notification from outside app, to run a different method than the didReceiveRemoteNotification
When user is inside app, to run a different method than the other

Apple does not allow what you want. Push notifications work in the following way
1) When the application is not running, and the user clicks a notification, the application is launched and the payload of the notification is loaded in the function didFinishLaunchingWithOptions.
2) When the application is running in background, and the user clicks a notification, the application becomes active and the function didReceiveRemoteNotification is called. Now this function contains the payload of the notification.
3) When the application is running and a push notification is called, the function didReceiveRemoteNotification is called and this function contains the payload of the notification.
4) When the app is running in background or inactive and notification is received, nothing can be done with the notifications until the user clicks the notification

Related

didReceiveRemoteNotification in AppDelegate not called for silent push notifications with notification service extension

I have a notification service extension for my app for modifying my regular push notifications, which works fine. The didReceive method in the extension is overriding the didReceiveRemoteNotification method in AppDelegate. However, if I send a silent notification or just a notification without the 'mutable-content' parameter set to true, the extension is not called (as expected). But, didReceiveRemoteNotification is not called either, so I have no way to handle the notification.
Without the extension, didReceiveRemoteNotification is being called on silent notifications and notifications without 'mutable-content' set.
Shouldn't it be called in my case as well? Or am I misunderstanding something about the extension?

Obtain contents of remote push notification received while the app is not running after user launches the application from app icon

I was following Apple docs and some other sources. But can't find the answer.
Problem is as follows:
Conditions:
The remote push notification the app is receiving is time sensitive, and should be delivered to the device ASAP.
Notification contains information which is required whichever way the user launches the app (i.e. whether they click on notification or not).
Scenario:
Remote push notification is sent to a device while the app is not running (not on background, but not running at all).
User doesn't click on notification. Instead, user clicks on app icon.
It appears that in this case there's no way to receive notification contents upon application startup. Notification is not lost: it stays inside notification area until user clicks on it, but notification contents are not provided to the app upon app startup.
What did I try so far
I was able to get notification contents in every other scenario:
I have a handler
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler <...>
for when the app is in foreground
When app is in background, the handler
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: <...>
As a safeguard, I also loop through outstanding notifications like this:
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions <...> {
UNUserNotificationCenter.current()
.getDeliveredNotifications {
(notifications: [UNNotification]) -> Void in
And, in the same function I am checking if launchOptions contains any notifications.
With this combination it looks like I'm able to cover every single scenario, except for the case I outlined above. That is: when user clicks on app icon while app is not running after notification was delivered:
didReceiveRemoteNotification:fetchCompletionHandler is not called
UNUserNotificationCenter.current().getDeliveredNotifications returns 0 notifications (although notification is visible in notification area).
In didFinishLaunchingWithOptions the value of launchOptions is alos nil...
This article seems confirms this behavior, but doesn't provide any solution.
I also saw this question, but although it may be that acceptable solution for OP's needs, it does not provide a direct answer to his question, since using content-available flag changes how notification is delivered (see Configuring a Background Update Notification section on this page, and also reduces its priority, as explained in apns-priority section of this page.
BTW if I click on notification after opening the app, the behavior is as expected, so notification is not completely lost to the app, it's just not provided on its launch.
So what am I dealing with here? some edge case, bug or maybe such behavior is intended? if so, why?
Thanks in advance.

APNS: Receive silent Remote Notification if app was force quit

I am looking desperately for a way to receive silent remote notifications when the user has force quit his app.
I already experimented with this a while ago.
The only way to do that, was to remove the content-available flag. But then it wasn't a silent notification anymore. The main use case was to download additional content to the remote notification and only then schedule a local notification in turn.
As the new UNNotification Framework was introduced they also introduced the new Notification Service Extension which provides an elegant way to download content corresponding to a remote notification.
But there is still no way to do the same with silent notifications when the app is force closed. Or did I miss something ?
PS: Maybe it is a duplicate, but other threads do not respect the Notification Service Extension.
When app force closed. AppDelegate method:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
will not called. But if your json payload contain aps you will see instant message, after you swipe (or click on notification) method will be called.
You can look google and find table of difference silent and normal state and their work in other Application State
Finally I found the answer in localisation also discussed here:
Change language of alert in banner of Push Notification
I also use the new Notification Service Extension in combination to alter content before the notification is delivered.

How to know a push message info if app is launched from background directly rather than from notification msg?

I have properly registered for the push notification.
Implemented following method to receive notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
Also used UIApplicationLaunchOptionsRemoteNotificationKey from func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool.
I'm able to receive notification when the app is active. I'm also able to see the notification if I open the app from the notification by selecting it from banner or alert.
But if app is not active, it could be alive or killed and if a notification arrives at that point. And I ignore the notification and open the app by directly selecting the app icon from the home screen, I'm not getting the notification information in the UIApplicationLaunchOptionsRemoteNotificationKey from func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool.
So what is the right way to handle this scenario? Should the app call server and get the recent messages it sent? Any help is much appreciated.
If the user will open you app directly from it's home screen, you would not get access to the push notification dictionary.
If you need the data sent in the push notification, so the proper way will be a server request as you suggested.
If user enter the app by click the icon or launch from the background, the push notification message can't deliver to you, the message is cached by system, the only way to get it is from notification center
Answering my question:
It is possible for the app in the background to receive push notification.
To do so send content-available in the aps dictionary of push notification.
Enable background mode of the app from the Background modes section of the Capabilities tab in your Xcode project.
Implement application:didReceiveRemoteNotification:fetchCompletionHandler: in the app delegate.
References:
See section 'Notification Payload' in https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
See section 'Using Push Notifications to Initiate a Download' in
https://developer.apple.com/library/prerelease/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
However if app is killed then the appdelegate's application:didReceiveRemoteNotification:fetchCompletionHandler: is not getting called.
According to Will iOS launch my app into the background if it was force-quit by the user? post on iOS8.0 it is possible to do it using PushKit. But I have not tested it. And also if VOIP is not used then I dont know if it is possible for app to receive push notification information if it is killed.

Why is didReceiveRemoteNotification not called but didReceiveRemoteNotification:fetchCompletionHandler called when my app is in the foreground?

If I override
override func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("hey)
}
I successfully have the method called with the app in the foreground when I send a push notification.
If I override
override func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
println("hey")
}
I don't get any call to the method when sending a notification with the app in the foreground. Why does the first one work, but the second one doesn't when the app is in the foreground?
Note that I am only implementing one of these at a time. Not both at the same time.
You should use the callback version unless you need to support ios<7 (when it was introduced). As you are using Swift I expect that is not the case.
The older method goes back to ios3 and is deprecated in all but name:
Implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible. If your delegate implements both methods, the app object calls the application:didReceiveRemoteNotification:fetchCompletionHandler: method.
The older version will also give different results depending on whether the app is live (foreground or background) or launches from a cold start. In the latter case, it will not get called, and you will need to intercept the launchOptions dict on didFinishLaunchingWithOptions to get at the infoDict:
If the app is not running when a remote notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary. The app does not call this method to handle that remote notification.
Use the callback version. It works for all cases. You don't have to use the completion handler/block, in which case the effect is the same (well, more consistent with the newer method).
update
sorry, Apple says you do have to call the completion block "as soon as possible" - you have 30 seconds to do so before the OS gives up on you. This might be the source of your warning.
As soon as you finish processing the notification, you must call the block in the handler parameter or your app will be terminated. Your app has up to 30 seconds of wall-clock time to process the notification and call the specified completion handler block. In practice, you should call the handler block as soon as you are done processing the notification.
So call the completion block:
completionHandler(UIBackgroundFetchResult.NoData)
I have been using this method without calling that completion block and haven't experienced any problems, but i will add one now just to be safe. Apple suggests that if you don't, your app won't get foregrounded but I have not seen that in practice.
Did you register at least one UIUserNotificationType with UIApplication:registerUserNotificationSettings: ?
If you do not register for badge, sound, or alert then you can only receive silent notifications, which are only delivered with a FetchCompletionHandler.

Resources