I got a problem of tracking local notifications. I know that there are such functions as willPresent (called when the notification arrives when the application is in the foreground) and didReceive (called when the application is launched by tapping on the notification). There is also a didReceiveRemoteNotification function, which, as I understand it, is triggered when REMOTE notification is received. And what function is called when receiving LOCAL notification?
I have studied all the methods of UNUserNotificationCenterDelegate and did not find the right one there. It seems that the didReceiveRemoteNotification method is suitable, but it is not called when a local notification is received.
Related
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?
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.
I am working on a reminder app.The app will show notifications at particular times which is set by the user.I have used local notifications to show the reminder.
The problem is I want to run some codes when it receives the local notifications even if the user does not tap on the notification. Is there any way to do this in swift ?
I believe you should add method application(_ application: UIApplication, didReceive notification: UILocalNotification) inside your AppDelegate.swift. The code you want to be implemented should be there. The code will be executed even the user does not tap on the notification. But anyhow, the app should be running.
Here is an extract from apple docs:
If
the app is not active in the foreground when the notification fires,
the system uses the information in the UILocalNotification object to
determine whether it should display an alert, badge the app icon, or
play a sound. If the app is running in the foreground, the system
calls this method directly without alerting the user in any way.
You might implement this method in your delegate if you want to be
notified that a local notification occurred. For example, a calendar
app might use local notifications to alert the user to upcoming
events.
If the user chooses to open the app when a local notification occurs,
the launch options dictionary passed to the
application(:willFinishLaunchingWithOptions:) and
application(:didFinishLaunchingWithOptions:) methods contains the
localNotification key. This method is called at some point after your
delegate’s application(_:didFinishLaunchingWithOptions:) method.
I did some research on lots of stackoverflow issues and websites in trying to figure out how do the iOS push notifications influence AppDelegate lifecycle methods and when is which method (not) getting triggered. Main focus of the research was on "standard" iOS push notifications (with alert field) and silent ones (with just content-available set to 1) and on AppDelegate's application:didReceiveRemoteNotification and application:didFinishLaunchingWithOptions methods.
I don't want to ask lots of questions for different scenarios, but would rather try to write down the statements about different test cases I tried and ask you after that:
Is there any statement that is wrong and if yes, which one and why?
Scenario 1: App has been used and put to background by tapping the home button.
If standard push notification is sent, in the moment of push notification arrival, none of the methods gets triggered, app remains inactive in background. Once push notification has been tapped and app got opened because of it, application:didReceiveRemoteNotification method got called and application:didFinishLaunchingWithOptions doesn't get called. I tested this scenario right after putting the app to background and also after an app being in background for more than an hour - same behaviour. I guess that if for some reason iOS decided to kill my app while being in background, this test case becomes like Scenario 2, statement 1 from below, right?
If silent push notification is sent, in the moment of the push notification arrival, application:didReceiveRemoteNotification method got called and application:didFinishLaunchingWithOptions doesn't get called.
Scenario 2: App has been used and killed by swiping it out of the list of running apps.
If standard push notification is sent, in the moment of push notification arrival, none of the methods get triggered, app remains killed. Once push notification has been tapped and app got opened because of it, application:didReceiveRemoteNotification method got called and application:didFinishLaunchingWithOptions doesn't get called.
If silent push notification is sent, none of the methods gets triggered since silent push notifications fail to be sent to the app that got killed. After opening an app after notification is sent, application:didFinishLaunchingWithOptions gets called as part of the normal flow and without any push notification information. application:didReceiveRemoteNotification doesn't get called.
If you can maybe think of some other real life scenarios that I maybe forgot to mention, I would be really grateful to find out about them and what happens in those cases.
Cheers
Update #1
Thanks to Sandeep Bhandari for the update and additional scenarios. I forgot to mention in my original question that I was exploring scenarios in which application is arriving to the app that is currently not in the foreground for whatever reason.
Adding Sandeep's scenarios to the list:
Scenario 3: App is being used and push notification arrives.
If standard push notification is sent application:didReceiveRemoteNotification method will get called. application:didFinishLaunchingWithOptions will not get called.
If silent push notification is sent application:didReceiveRemoteNotification method will get called. application:didFinishLaunchingWithOptions will not get called.
Scenario 4: App being alive in background.
If standard push notification is sent application:didReceiveRemoteNotification method will get called. application:didFinishLaunchingWithOptions will not get called.
If silent push notification is sent application:didReceiveRemoteNotification method will get called. application:didFinishLaunchingWithOptions will not get called.
From an experience and digging alot on the iOS push notification. App
being in foreground or alive in background. both situations triggers
same delegate methods. only didReceiveRemoteNotification.
The silent push notification have a different handler: (content-available 1 means silent notification)
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
}
When app is dead. didReceiveRemoteNotification never called for regular push notification. It must be handled in didFinishLaunchingWithOptions as following:
// handle notification when app is closed.
let notification = launchOptions?[.remoteNotification]
if notification != nil {
self.application(application, didReceiveRemoteNotification: notification as! [AnyHashable : Any])
}
Additional information:
To test receiving push notification when app is killed. remove from the list that appears when double tapping home button?
The proper way to see the logging and do debugging is by editting the run scheme and selecting Wait for executable to be launched:
Run the app from the xcode. Then send push notification from server and then tap the notification from notification center.
I don't see any issue in the statement you made there, but I believe you missed to iterate over two more scenarios that I can think of.
App is in foreground and receives the push notification : didReceiveRemoteNotification gets called as soon as APNS gets delievered to iOS and you can handle it by checking application state in didRecieveRemoteNotification Method.
App being alive in background : I believe you are aware of background modes of iOS. If app is making use of expiration handler, app will be alive even if you put it to background by tapping on home button. Duration the app lives in background depends on various factors (some tutorials say app remains alive for 3 mins which I can't guarantee) Even in this case didReceiveRemoteNotification gets called as soon as APNS gets delievered to iOS. Only this time app wont be in foreground but yet its alive!!!
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