UILocalNotification handle when opened via App icon? - ios

I'm working with UILocalNotifications for the first time. Mostly working with repeating notifications. Most all makes sense, except one thing.
Apple Documentation states several cases for handling local notifications when they fire.
First, a case for when the user "taps the notification" when outside of the App:
If the notification is an alert and the user taps the action button
(or, if the device is locked, drags open the action slider), the
application is launched. In the
application:didFinishLaunchingWithOptions: method the application
delegate can obtain the UILocalNotification object from the passed-in
options dictionary by using the
UIApplicationLaunchOptionsLocalNotificationKey key. The delegate can
inspect the properties of the notification and, if the notification
includes custom data in its userInfo dictionary, it can access that
data and process it accordingly.
It also states a case for what happens when the user is inside the App:
If the application is foremost and visible when the system delivers
the notification, no alert is shown, no icon is badged, and no sound
is played. However, the application:didReceiveLocalNotification: is
called if the application delegate implements it. The
UILocalNotification instance is passed into this method, and the
delegate can check its properties or access any custom data from the
userInfo dictionary.
In both of those cases the developer can access the uilocalnotification and then decide what to do with it. However, in a third case - when the user, outside of the App, sees and ignores the notification and then later launches the App, no method is called that allows the application to know which notifications have previously fired?
At first I thought that this statement was describing that behavior, but now I am not sure:
On the other hand, if the local notification only badges the
application icon, and the user in response launches the application,
the application:didFinishLaunchingWithOptions: method is invoked, but
no UILocalNotification object is included in the options dictionary.
How can I handle the third case? How can I know which local notifications have fired? Do I need to iterate through my list and check all their times myself? Is there a better way to accomplish this?

You need to keep track of what is happening with your notifications. What I mean with this is that, because the notification has fired, and the user didn't launch the app because of a notification nor was your app running at the time of the notification, you need to check your sources to verify if a previously scheduled notification fired date has already passed.

Related

Automatically performing task when user notification is delivered in Swift 5

I want to perform a simple task in my app when a local user notification is triggered while the app is not in the foreground. Can I do this?
I can't use UNNotificationAction, because I don't want to rely on the user to do anything. And it seems I can't use UNUserNotificationCenterDelegate because it only has a method to handle notifications while the app is in foreground.
I have came to conclusion that what I asked is not possible. Here is my alternative solution: I wanted to perform an action within my app when a location based user notification was fired. To accomplish what I needed, I set up the monitoring for my CLCircularRegion. The delegate for CLLocationManager launches the app in the background upon entry to the region, and I can do what I want inside delegate method.

Can notification actions be handled while the device is locked?

I've been working with notifications for quite a while now, and I could've sworn that userNotificationCenter(_:didReceive:withCompletionHandler:) used to only be processed if the app was opened via the notification.
However, upon looking into actionable notifications, I found this:
When the user selects an action, the system launches your app in the background and notifies the shared UNUserNotificationCenter object, which notifies its delegate. Use your delegate object's userNotificationCenter(_:didReceive:withCompletionHandler:) method to identify the selected action and provide an appropriate response.
Does this mean that the seemingly universal didReceive delegate method now works even while the device is locked?
Or must the app always be opened to actually do anything meaningful in response to notification actions (writing to local database, sending HTTPS requests, etc.)?
Yes, the delegate works even if the device is locked. The app is only woken up if the user chose an action. Also, I am not sure how much time you will get before you invoke the completion block.
Important
If your response to action involves accessing files on disk,
consider a different approach. Users can respond to actions while the
device is locked, which would make files encrypted with the complete
option unavailable to your app. If that happens, you may need to save
changes temporarily and integrate them into your app's data structures
later.
From this Delegate Method : userNotificationCenter(_:didReceive:withCompletionHandler:) notification is works when your device is locked, but if you want to perform any action.
For Example: Suppose through Notification you want to pick any phone call within application then you have to setup another things as well.
you have to set up PushKit Framework within your application . As shown in images:

Push notifications not working properly when the app is not active (killed)

I'm facing a problem with push notifications, I'm using Google Cloud Messaging,
My problem is that my app works with some cached data, and I refresh that cache with the data coming from push notificacions, so when the app is killed by the user (home button + swipe-up) and the app receive a push notification it doesn't call the method didReceiveRemoteNotification, so the app can't access to the payload of that notification and update the cached data.
So, is there a way to achieve this?
Only when the user taps the notifications and the app gets it through the application:didFinishLaunchingWithOptions method?
If the user opens the app by clicking the application icon I can't get the notificaton's payload?
PD: If the app is in foreground or background (not killed) it works perfectly
If you wish your app to receive the push notification even when it is killed, add the key "content-available":"1" to the push payload.
Look here
EDIT:
After digging some more, I found out that silent push (e.g content-available:1), does not wake the app if it was killed by the user:
However, the system does not automatically launch your app if the user has force-quit it
I must say, I can't really understand why the OS does not let my app wake up when silent push arrives when it's dead. What's the point then???
This is the expected behavior. didReceiveRemoteNotification will not be called if the user killed the app, unless your app has VoIP permissions.
In case of killed application. both application:didFinishLaunchingWithOptionsandapplication:didReceiveRemoteNotification:fetchCompletionHandler:are called. In the prior one. The key of notification either remote or local is passed in options parameter. And the later one is called in only case when remote notification. The process is explained in detail in Apple Docs Here. The snapshot is pasted here as well.
Handling an Actionable Notification
If your app is not running in the foreground, to handle the default action when a user just swipes or taps on a notification, iOS launches your app in the foreground and calls the UIApplicationDelegate method application:didFinishLaunchingWithOptions: passing in the local notification or the remote notification in the options dictionary. In the remote notification case, the system also calls application:didReceiveRemoteNotification:fetchCompletionHandler:.
If your app is already in the foreground, iOS does not show the notification. Instead, to handle the default action, it calls one of the UIApplicationDelegate methods application:didReceiveLocalNotification: or application:didReceiveRemoteNotification:fetchCompletionHandler:. (If you don’t implement application:didReceiveRemoteNotification:fetchCompletionHandler:, iOS calls application:didReceiveRemoteNotification:.)
Finally, to handle the custom actions available in iOS 8 or newer , you need to implement at least one of two new methods on your app delegate, application:handleActionWithIdentifier:forRemoteNotification:completionHandler: or application:handleActionWithIdentifier:forLocalNotification:completionHandler:. In either case, you receive the action identifier, which you can use to determine what action was tapped. You also receive the notification, remote or local, which you can use to retrieve any information you need to handle that action. Finally, the system passes you the completion handler, which you must call when you finish handling the action. Listing 2-8 shows an example implementation that calls a self-defined action handler method. Reference Apple Docs
Edit:
The user taps the default button in the alert or taps (or clicks) the app icon. If the default action button is tapped (on a device running iOS), the system launches the app and the app calls its delegate’s application:didFinishLaunchingWithOptions: method, passing in the notification payload (for remote notifications) or the local-notification object (for local notifications). Although application:didFinishLaunchingWithOptions: isn’t the best place to handle the notification, getting the payload at this point gives you the opportunity to start the update process before your handler method is called.
Reference: Apple Docs
When the app is killed and the push notification triggered contains some actionable button. When we click on a Action Button of Push Notification then:
The first delegate that is executed is:
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
But in here the launchOptions are nil.
The second delegate that is executed is:
-(void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(nonnull NSDictionary *)userInfo completionHandler:(nonnull void (^)())completionHandler
In this the variable userInfo contains all the payload of the Push Notification. With the help of identifier we can identify which action was invoked and then perform our respective operations.
add target of notification service extension in your project it contains two methods upper one method call in killed time and add a key "mutable_content" : true in server side of your notification payload

How to access last local notification if the user ignored the last alert on ios?

My app is in background, and the notification alert did show correctly and the badge number was increased by 1.
IF the user taps on the alert everything works fine and the delegate didReceiveLocalNotification is called.
But the user ignored the alert.
After a while when the user open my app again, the only delegate called is didBecomeActive.
How can I do something (show a UIAlertView for example), based on the last ignored alert?
iOS itself doesn't provide any method for that.
But you can simply do that:
While you are registering notification to iOS - write all notification data to a file.
On application start load notification file and all notification that expired (NSDate < now) - those are ignored notifications.
Before doing 2. you need to check if maybe someone came from a notification and delete it from your file.
It's kind of duplicating iOS notification management system which is not allowing to get a list of registered notification unfortunately.
Writing a notification to a file should be easy: it's already implementing NSCoding protocol.
As far as I know, if the notification is ignored, you can't recover it once you open the app again.
You'll need to check the service that pushed that notification to know if there's something new
Unfortunately, there is not a straight forward way to achieve it.
Possible workaround - If the user dismisses/ignored your push notification, your app could take a look at messages on your server and handle it the appropriate way.

Deleting notifications from the notification center revisited

The previous posting on here regarding deleting notifications from the notification center claim its not possible to delete individual notifications, only all of them.
However individual notifications do get deleted for the reminder app - set 3 reminders to fire in a couple of minutes, when they fire go to the notification center, now select one, after the reminder app launches go back to the notification center and that specific notification has been deleted but others remain. So how is this achieved?
The Reminders app probably fires local notifications. Local notifications can be withdrawn, using cancelLocalNotification: on UIApplication.
(Additionally, push notifications when sent using the enhanced call (first byte is 1) supports an expiry parameter (when sending, not inside the JSON payload) that is supposed to mean that this notification, if not delivered by a certain date, should not be delivered. It is possible that this parameter is also used in a similar way to hide received notifications.
It is also highly possible that Apple's own apps do whatever the hell they want.)
When the user taps on the notification:
If the app was running in the background, you retrieve it using AppDelegate's method didReceiveLocalNotification.
If it wasn't running, then the notification can be obtained with the didFinishLaunchingWithOptions method. You just need to search the launchOptions dictionary for the UIApplicationLaunchOptionsLocalNotificationKey.
If you want to delete specific notifications which have already fired, specially when the user doesn't enter the app by tapping on the notification, then it's probably better to store them in NSUserDefaults so that you can still obtain them later. That approach is explained here.

Resources