Unable to Handle Local Notification when app has been terminated - ios

The app that I have made keeps track of Goals and Milestones(which belong to a goal) and reminds the user when they are about to reach the due date of a goal or a milestone through a UILocalNotification
my app has been successful in handling local notifications when the app is in a background state and when the app is in the foreground.
I am aware that if you want to handle local notifications when they are received when the app is terminated you are required to access the local notification through the launch options of the application:didFinishLaunchingWithOptions: method in your appDelegate.
However whenever I test the app and activate the local notification after the app has terminated, I am unable to handle the local notification in application:didFinishLaunchingWithOptions:
I suspect it has something to do with either launch options being nil or launch options not having the local notification payload.
The way I test this scenario is as such:
I build and run the app (command + R)
I schedule the notification
I stop running the simulator and then change my mac's time to just
before the fire date
I build and run the app again and then proceed to terminate the app
from the simulator (by shift + command + h twice and then
swiping up)
I lock the screen and wait for the notification
After it fires I swipe the notification on the lock screen
After the notification fires and i swipe the notification, the app is launched but the method that i used in handling the local notification payload in application:didFinishLaunchingWithOptions: is not called whatsoever.
My Code in my application:didFinishLaunchingWithOptions: to handle the local notification payload is as follows:
if let options = launchOptions {
let value = options[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification
if let notification = value {
self.application(application, didReceiveLocalNotification: notification)
}
}
My Code in my application:didReceiveLocalNotification: is as follows:
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
//If the user receives a notification while app is in the foreground
println("")
if application.applicationState == UIApplicationState.Active {
println("did receive notification")
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
let alertController = UIAlertController(title: nil, message: notification.alertBody!, preferredStyle: UIAlertControllerStyle.Alert)
let alertAction = UIAlertAction(title: "View", style: UIAlertActionStyle.Default) { (alertAction) -> Void in
self.performFollowUpActionForNotification(notification)
}
let cancelAction = UIAlertAction(title: "Close", style: UIAlertActionStyle.Cancel, handler: nil)
alertController.addAction(alertAction)
alertController.addAction(cancelAction)
self.window!.rootViewController!.presentViewController(alertController, animated: true, completion: nil)
}
else {
performFollowUpActionForNotification(notification)
application.applicationIconBadgeNumber = 0
}
}
as well as the helper method performFollowUpActionForNotification:
func performFollowUpActionForNotification(notification:UILocalNotification) {
if notification.alertAction! == "View Goal" {
if let tabVC = self.window?.rootViewController? as? UITabBarController {
let navCon = tabVC.viewControllers![0] as UINavigationController
if navCon.topViewController is GoalPageViewController {
}
else {
navCon.pushViewController(navCon.viewControllers![0] as UIViewController, animated: true )
}
}
}
if notification.alertAction! == "View Milestone" {
if let tabVC = self.window?.rootViewController? as? UITabBarController {
let navCon = tabVC.viewControllers![0] as UINavigationController
if let goalPageVC = navCon.viewControllers![0] as? GoalPageViewController {
goalPageVC.findGoalThatContainsMilestoneAndGoToDetailForNotification(notification)
}
else {println("cant down cast view controller to goal page view controller")}
}
}
}
Is the problem with my code or how i test my app for this scenario? I am desperately in need of an answer.

Hope this will be helpful for you please have look below.
In iOS 7.1 Apple has introduced a very important update to how the system handles notifications triggered by beacons. Now an app can take action when enter/exit monitoring events occur, even if it has been terminated. It’s an amazing improvement in comparison to iOS 7, but there’s still a lot of confusion regarding how it really works, so we’ve prepared a short tutorial.
Location events (related to the beacons in this case) are handled the same way as any other app launching events. Every single time phone enters or exits beacon’s region while the app is terminated, it will be automatically launched and application:didFinishLaunchingWithOptions: method (of AppDelegate class) is called with UIApplicationLaunchOptionsLocationKey key existing in launchOptions parameter.
When you verify this key exists (so location was the reason that your app was launched) you should create new instance of ESTBeaconManager class, set delegate to AppDelegate object (or any other object that is working as ESTBeaconManagerDelegate and was created before this event occurred) and start monitoring. Region you are passing to the startMonitoringForRegion: method is not important, as ESTBeaconManager delegate will receive most recent region information. You can just pick any of the ones your app registered in iOS. When the monitoring was revoked, app will automatically receive most recent entered/exited region event in beaconManager:didEnterRegion: or beaconManager:didExitRegion: method.
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if([launchOptions objectForKey:#"UIApplicationLaunchOptionsLocationKey"])
{
self.beaconManager = [ESTBeaconManager new];
self.beaconManager.delegate = self;
// don't forget the NSLocationAlwaysUsageDescription in your Info.plist
[self.beaconManager requestAlwaysAuthorization];
[self.beaconManager startMonitoringForRegion:[[ESTBeaconRegion alloc]
initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
identifier:#"AppRegion"]];
}
return YES;
}
-(void)beaconManager:(ESTBeaconManager *)manager didEnterRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Enter region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
-(void)beaconManager:(ESTBeaconManager *)manager didExitRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Exit region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

Related

After Receiving Notification how can i can get payload on app icon click?

My push notification works properly in Normal case but i have problem with following case:
1) when my app remove from background and get notification & tap on app icon then i want to push view controller and display payload data in that view controller.
2 ) when my app in background and get notification & tap on app icon then i want to push view controller and display payload data in that view controller.
following is my userInfo
{
aps = {
alert = "Call from rohan panchal";
appointmentId = 220;
badge = 0;
"call_token" = "T1==cGFydG5lcl9pZD00NTI1ODY1MiZzaWc9MzM1MmM0M2E2MjkwN2JiYWMzNjgyNjk0MjFlZWMyNWEzNTZmZmM3MjpzZXNzaW9uX2lkPTJfTVg0ME5USTFPRFkxTW41LU1UUTNNREl3TVRBd01qVXdOWDV3WXpCRFMyWTRlR2xhUWpGdU1YbFpNamhvV0hoNFVHTi1VSDQmY3JlYXRlX3RpbWU9MTQ3MDIwMTAwMiZyb2xlPXB1Ymxpc2hlciZub25jZT0xNDcwMjAxMDAyLjUyMDM0NDAzNjQzMjMmZXhwaXJlX3RpbWU9MTQ3MDgwNTgwMg==";
doctorId = 238;
"doctor_country" = US;
"doctor_name" = "John smith";
patientId = 239;
"patient_country" = US;
"patient_name" = "Lottry patel";
sessionId = "2_MX40NTI1ODY1Mn5-MTQ3MDIwMTAwMjUwNX5wYzBDS2Y4eGlaQjFuMXlZMjhoWHh4UGN-UH4";
sound = default;
};
}
following my code.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// when i remove app from background & click on notification then following code run.
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if(notificationPayload)
{
NSLog(#"%#",notificationPayload);
WebViewController *DashBoard = [[WebViewController alloc]initWithNibName:#"WebViewController" bundle:nil];
self.navcntrl=[[UINavigationController alloc]initWithRootViewController:DashBoard];
}
else
{
DoctorMenuViewController *DoctorVC = [[DoctorMenuViewController alloc]initWithNibName:#"DoctorMenuViewController" bundle:nil];
self.navcntrl=[[UINavigationController alloc]initWithRootViewController:DoctorVC];
}
}
When I got notification then following method called.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"%#",userInfo);
WebViewController *DashBoard = [[WebViewController alloc]initWithNibName:#"WebViewController" bundle:nil];
[self.navcntrl pushViewController:DashBoard animated:YES];
}
Please help.Any help appreciated.
The push notification payload consists of:
alert - the alert string and actions
badge
sound
content-available
The key content-available is a new feature, and it is this key that makes silent push possible.
To enable, you also have to add remote-notifcation as your app UIBackgroundModes as described here.
This is what happens when content-available is in the payload:
If app is Suspended, the system will bring it into Background
If app was killed by user, nothing happens and app remains in Not Running
Read about app state changes.
A potential is pitfall:
You enable with content-available=1. But, it is WRONG to disable with content-available=0. To disable, you have to REMOVE the key in the payload.
plz use this
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if(application.applicationState == UIApplicationStateInactive)
{
NSLog(#"Inactive");
//do your things when you click on notification
}
else if (application.applicationState == UIApplicationStateBackground)
{
NSLog(#"Background");
}
else if (application.applicationState == UIApplicationStateActive)
{
NSLog(#"Active");
}
}
for more information plz read this link http://samwize.com/2015/08/07/how-to-handle-remote-notification-with-background-mode-enabled/
application: didReceiveRemoteNotification:
is called when the OS receives a RemoteNotification and the app is running (in the background/suspended or in the foreground.)
and
application:(UIApplication *)application didFinishLaunchingWithOptions
is called when app is not running and you click on app icon.
1) when my app remove from background and get notification & tap on app icon -
TestViewController *testVC = your_test_vc;
UINavigationController *navVC = [[UINavigationController alloc]initWithRootViewController:testVC];
In TestViewController you can push your controller to show the payload data.
2) Whatever you wrote is correct.
Hope this will help you.
Apps have notification data delivered in a specific set of circumstances. If the user's interaction does not coincide with one of these, the data is not available. There are no exceptions.
the user taps a notification banner (or alert). It does not matter if the app was running or not.
the payload has the content-available field set to a true value, and iOS opts to deliver the notification.
Note: this answer does not explain how to invoke or handle any of the scenarios.
The field value from the userinfo is what you require then under
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground || state == UIApplicationStateInactive)
{ //notification is received when your app is in background
//open the view controller you expected
}
else if(state == UIApplicationStateActive)
{
//notification is received when your app is in foreground
//do nothing
}
}

Handling Push Notifications when App is Terminated

When my App is not running and receives a Push Notification, if I click on that notification, the App is launched - but then it doesn't prompt the user with the Alert-View I set up, asking them whether they want to view the Notification's contents or not. It just launches, and sits there.
The Push Notifications do work perfectly when the App is running - either as the Active app or while in the background - but nothing works correctly when the app is not running.
I tried logging-out the launchOptions NSDictionary in application: didFinishLaunchingWithOptions: to see what load its bringing - but it comes up as "(null)". So It basically contains nothing - which doesn't make sense cause shouldn't it contain the Notification's load?
Anybody have any ideas how to make Push Notifications work when they arrive while the App was NOT running?
I mean how to handle the Push notifications when the App is in not running state. What if, if you receive many notifications & you did not open the app, neither did you tap the system's notification panel. How are you preserving those push for a later retrieval.
1) When application is running in background and When application is running in foreground
application:didReceiveRemoteNotification: method will called as below.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if (application.applicationState == UIApplicationStateInactive)
{
// opened from a push notification when the app was on background
NSLog(#"userInfo->%#", [userInfo objectForKey:#"aps"]);
}
else if(application.applicationState == UIApplicationStateActive)
{
// a push notification when the app is running. So that you can display an alert and push in any view
NSLog(#"userInfo->%#", [userInfo objectForKey:#"aps"]);
}
}
2) When application is not launched (close) then application:didFinishedLaunchingWithOptions method will called.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (launchOptions != nil)
{
// opened from a push notification when the app is closed
NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo != nil)
{
NSLog(#"userInfo->%#", [userInfo objectForKey:#"aps"]);
}
}
else
{
// opened app without a push notification.
}
}
3) There is no way to remove a specific notification as of. The way to remove all the notifications from your app so they don't show in the Notification Center when the user opens the app from one of them, is to set the app badge to 0.
As per your question, there is no way to hold all the notification when you open the app, better you call an api to get all notification as per time stamp from your back end/server that's how Facebook does.
You can retrieve notifications delivered to your app by using the getDeliveredNotifications(completionHandler:) method. Note that this only returns notifications currently displayed in the Notification Center and not the ones that have been manually cleared by the user.
UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
// notifications: An array of UNNotification objects representing the local
// and remote notifications of your app that have been delivered and are still
// visible in Notification Center. If none of your app’s notifications are
// visible in Notification Center, the array is empty.
// As said in the documentation, this closure may be executed in a background
// thread, so if you want to update your UI you'll need to do the following:
DispatchQueue.main.sync { /* or .async {} */
// update UI
}
}
The app does not process push notification in the background, what it really does the OS is wake up the app once you press in the notification. You can catch this moment in the following way:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
// Your app has been awoken by a notification...
}
}
There is no way of handling this on application end.
You would need to maintain the unread badge count at the server.
When the app is killed the badge value is updated from the server.
So when you open the application any time , you would need to call a web service to get the required notifications and update the badges of tabbar(if used).
Perform actions after notification is received in the terminated state - iOS 13 - Scene Delegate
Recently I came across an issue where I received remote push notification when my app was in the terminated state. In the latest iOS versions Scene delegate is responsible to handle view life cycle methods rather than App Delegate.
Older iOS versions - Handled by App Delegate
When the app is terminated and remote push notification is received the payload is reflected in the didfinishLaunchingWithOptions method of App delegate. Using the launch parameter of this method it’s possible to get the payload data and perform any interaction.
New iOS version - Handled by Scene Delegate
Similarly when the app is terminated and remote push notification is received the payload is reflected in the scene(willConnectTo session) of the scene delegate. Using the connectingOption parameter of this method it’s possible to get the payload data and perform any interaction.
Hint: To perform any interaction or pass this payload to another view controller once the view controller is set as root view controller keep a delay of some seconds to pass the data.
Example code:
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
if defaults.string(forKey: UserDefaultsKeys.TenantID.rawValue) != nil && connectionOptions.notificationResponse != nil {
let rootViewController = UINavigationController(rootViewController: DashboardVC())
window?.rootViewController = rootViewController
window?.makeKeyAndVisible()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
NotificationCenter.default.post(
name: .passApnsDataToDashboardNotification,
object: nil,
userInfo: connectionOptions.notificationResponse?.notification.request.content.userInfo)
}
}
You can display the alert after launching a previously terminated app from a notification like this:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
// display the alert
}
For your use case, a BGTask is the best solution which can call the server and get the data and update your database, so that next time, you have available data immediately.
BGTask scheduling is done by OS based on 7 factors (like app usage, batter life, rate limit etc), If your app does not execute any BGTask (you can check easily by saving it to UserDefalts), you can fetch the server data explicitly when the app is live.
Just FYI:
You can configure and Use UserNotificaions to receive a local/remote notification
https://developer.apple.com/documentation/usernotifications?language=objc
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void(^)(void))completionHandler
https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter?language=objc

3D Touch Home Shortcuts In Obj-C

All of my apps are currently written in Obj-C. The link https://developer.apple.com/library/content/samplecode/ApplicationShortcuts/Introduction/Intro.html#//apple_ref/doc/uid/TP40016545 for the sample code of implementing Home Screen Shortcuts with 3D Touch is completely compiled in Swift. Anyone come across documentation for Obj-C, so I don't have to go through my AppDelegate and translate it all?
UPDATE:
After adding in all the shortcuts in Info.plist, I added in the AppDelegate.m:
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
UINavigationController *nav = (UINavigationController *) self.tabBarController.selectedViewController;
NSLog(#"%#", shortcutItem.type);
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addPrayerRequest"]) {
Requests *gonow = [[Requests alloc] init];
[nav pushViewController:gonow animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addPrayer"]) {
PrayerStats *controller = [[PrayerStats alloc] init];
[nav pushViewController:controller animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addFast"]) {
FastStats *controller1 = [[FastStats alloc] init];
[nav pushViewController:controller1 animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addStudy"]) {
StudyStats *controller2 = [[StudyStats alloc] init];
[nav pushViewController:controller2 animated:YES];
}
}
This allows it to work, without putting any other methods in, or adding anything to didFinishLaunchingWithOptions.
There are two states from where the user can open the app through Quick Actions.
TL;DR
You are always doing the same thing regardless of the state in which the app is when the quick action is done, that's why you only need to override application:performActionForShortcutItem:completionHandler: So if you wanted to do different things, then you would want to handle them in the two places, if not then just the overridden is enough.
One is if the app is killed or not running in background where we get the shortcut info on launch.
The other is if the app is running in background where we get the shortcut info on the new app delegate method.
To handle these Quick Action shortcuts when in background you need to override this method on App Delegate:
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
And for not running in background (killed) on your
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
or
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
you should check if the app was launched by a Quick Action:
UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKey:UIApplicationLaunchOptionsShortcutItemKey];
(Link to related Apple Documentation)
Quote from the Official Apple Docs
It’s your responsibility to ensure the system calls this method
conditionally, depending on whether or not one of your app launch
methods (application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions:) has already handled a
quick action invocation. The system calls a launch method (before
calling this method) when a user selects a quick action for your app
and your app launches instead of activating.
The requested quick action might employ code paths different than
those used otherwise when your app launches. For example, say your app
normally launches to display view A, but your app was launched in
response to a quick action that needs view B. To handle such cases,
check, on launch, whether your app is being launched via a quick
action. Perform this check in your
application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method by checking for the
UIApplicationLaunchOptionsShortcutItemKey launch option key. The
UIApplicationShortcutItem object is available as the value of the
launch option key.
If you find that your app was indeed launched using a quick action,
perform the requested quick action within the launch method and return
a value of NO from that method. When you return a value of NO, the
system does not call the
application:performActionForShortcutItem:completionHandler: method.
Implement below 3 simple steps:
Step 1 : Write below method in AppDelegate class to Configure dynamic shortcut items.
NOTE : You can configure shortcut items in info.plist if you want it static.
(Refer Apple documentation.)
/**
* #brief config dynamic shortcutItems
* #discussion after first launch, users can see dynamic shortcutItems
*/
- (void)configDynamicShortcutItems {
// config image shortcut items
// if you want to use custom image in app bundles, use iconWithTemplateImageName method
UIApplicationShortcutIcon *shortcutSearchIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
UIApplicationShortcutIcon *shortcutFavoriteIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeFavorite];
UIApplicationShortcutItem *shortcutSearch = [[UIApplicationShortcutItem alloc]
initWithType:#"com.sarangbang.QuickAction.Search"
localizedTitle:#"Search"
localizedSubtitle:nil
icon:shortcutSearchIcon
userInfo:nil];
UIApplicationShortcutItem *shortcutFavorite = [[UIApplicationShortcutItem alloc]
initWithType:#"com.sarangbang.QuickAction.Favorite"
localizedTitle:#"Favorite"
localizedSubtitle:nil
icon:shortcutFavoriteIcon
userInfo:nil];
// add all items to an array
NSArray *items = #[shortcutSearch, shortcutFavorite];
// add the array to our app
[UIApplication sharedApplication].shortcutItems = items;
}
Step 2 : In AppDelegate class application didFinishLaunchingWithOptions method write below code.
// UIApplicationShortcutItem is available in iOS 9 or later.
if([[UIApplicationShortcutItem class] respondsToSelector:#selector(new)]){
[self configDynamicShortcutItems];
// If a shortcut was launched, display its information and take the appropriate action
UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKeyedSubscript:UIApplicationLaunchOptionsShortcutItemKey];
if(shortcutItem)
{
// When the app launch at first time, this block can not called.
//App launch process with quick actions
[self handleShortCutItem:shortcutItem];
}else{
// normal app launch process without quick action
}
}
Step 3 : Write below delegate method and completion handler in AppDelegate class.
/*
Called when the user activates your application by selecting a shortcut on the home screen, except when
application(_:,willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions) returns `false`.
You should handle the shortcut in those callbacks and return `false` if possible. In that case, this
callback is used if your application is already launched in the background.
*/
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler{
BOOL handledShortCutItem = [self handleShortCutItem:shortcutItem];
completionHandler(handledShortCutItem);
}
/**
* #brief handle shortcut item depend on its type
*
* #param shortcutItem shortcutItem selected shortcut item with quick action.
*
* #return return BOOL description
*/
- (BOOL)handleShortCutItem : (UIApplicationShortcutItem *)shortcutItem{
BOOL handled = NO;
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
NSString *shortcutSearch = [NSString stringWithFormat:#"%#.Search", bundleId];
NSString *shortcutFavorite = [NSString stringWithFormat:#"%#.Favorite", bundleId];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if ([shortcutItem.type isEqualToString:shortcutSearch]) {
handled = YES;
SecondViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
}
else if ([shortcutItem.type isEqualToString:shortcutFavorite]) {
handled = YES;
ThirdViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"thirdVC"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
}
return handled;
}
If you look at the sample code provided for apple, you'll see that they suggest that you write a method that handles your shortcut item so that you can handle it in all three places:
application: performActionForShortcutItem,
application: didFinishLaunchingWithOptions and
willFinishLaunchingWithOptions
An example of what I did was:
- (BOOL)handleShortCutItem:(UIApplicationShortcutItem *)shortcutItem {
BOOL handled = NO;
if (shortcutItem == nil) {
return handled;
}
if ([shortcutItem.type isEqualToString:kFavoritesQuickAction]) {
handled = YES;
}
if (handled) {
// do action here
}
return handled;
}
Then you would just call this method in any place where you are getting a shortcut item. This should help you along your way!
I make an objective-c demo project for home screen quick action.
3D touch home quick action demo : https://github.com/dakeshi/3D_Touch_HomeQuickAction
The Demo project implements the static quick action without Info.plist file to avoid unwanted situations before launching the app at first.
You can easily change it to the dynamic quick action.
As mentioned in apple documentation, you can handle quick action in application:didFinishLaunchingWithOptions: method. In that case, you should return NO to block to call application:performActionForShortcutItem:completionHandler: method.
It works on both swift 3 and 4 (only on home screen shortcuts)
//Add plist items as show in image and write following method in Appdelegate
//3D Touch Method shortcuts from home screen
func application(_ application: UIApplication, performActionFor shortcutItem:UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
if shortcutItem.type == "Share" {
//handle action Share
let alert = UIAlertController(title: "3D touch Share", message: "Yahoo!!! 3D touch is working👌", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert,animated: true,completion: nil)
completionHandler(true)
} else if shortcutItem.type == "Logout" {
//handle action Type02
let alert = UIAlertController(title: "3D touch Logout", message: "Yahoo!!! 3D touch is working👌", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert,animated: true,completion: nil)
completionHandler(true)
} else {
completionHandler(false)
}
}

Remove fired Location-Based notification when user exits region

I've setup (default iOS8) location-based notifications for my app.
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.regionTriggersOnce = NO;
notification.userInfo = #{ #"notification_id" : #"someID" };
notification.region = region;
notification.alertBody = alertBody;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
When a user enters the specified region, the notification is shown in the NotificationCenter correctly.
However, I want to remove that notification message when the user exits that region, because it makes little sense for the user to go home and look at the notification center until they recieve a message which looks like so:
"You are at XXXXX!"
Has anyone tried something similar? The documentation is unclear on how this can be done.
CLRegion object has special properties for that: notifyOnEntry and notifyOnExit.
Everything that you need is update code in this way:
CLRegion *region = .... // Configure region here
region.notifyOnEntry = YES;
region.notifyOnExit = NO;
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.regionTriggersOnce = NO;
notification.userInfo = #{ #"notification_id" : #"someID" };
notification.region = region;
notification.alertBody = alertBody;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
Here is Apple documentation that explains it:
#property(nonatomic, copy) CLRegion *region
Assigning a value to this
property causes the local notification to be delivered when the user
crosses the region’s boundary.
The region object itself defines
whether the notification is triggered when the user enters or exits
the region.
I was really tired yesterday and couldn't complete my answer in time.
Based on some answers here I have only a clue what you have to do. I did not tried it myself (geofencing is a really a pain to test, I know that because I'm working on a geofencing project atm.), I also never had to delete a delivered notification from the Notification Center before.
I assume your application will not terminate due the whole process. So we won't use startMonitoringForRegion function here.
Answer written in Swift 2.0 (It's not that hard to translate it to ObjC).
func createAndRegisterSomeNotificationSomewhere() {
let region = CLCircularRegion(center: someCoordinates, radius: someRadius, identifier: someIdentifier)
region.notifyOnEntry = true
region.notifyOnExit = true
let locationNotification = UILocalNotification()
locationNotification.alertBody = "someAlertBody"
locationNotification.userInfo = ["notification_id" : "someID"]
locationNotification.regionTriggersOnce = false
locationNotification.region = region // remember 'presentLocalNotificationNow' will not work if this value is set
UIApplication.sharedApplication().scheduleLocalNotification(locationNotification)
}
/* CLLocationManagerDelegate provides two function */
// func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion)
// func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion)
/* If I'm not mistaken they are only called for monitored regions and not location based local notifications */
/* I mean you will have to use something like: self.locationManager.startMonitoringForRegion(someCircularRegion) */
/* Correct me if I'm wrong. So consider to rebuild the following logic to ease everything if you want to monitor regions. */
/* Now when you receive your location notification */
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
if let region = notification.region {
self.locationManager.requestStateForRegion(region)
/* based on other answers this will remove your noticaiton from NC and cancel from showing it anywhere */
application.cancelLocalNotification(notification)
/* but we need this notification still be scheduled because 'region.notifyOnExit = true' should fire it again later */
application.scheduleLocalNotification(notification)
}
}
func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) {
/* this is not the best solution, because it adds some latency to the dilivery code and CLRegionState can also be Unknown sometimes */
/* I'd go with the two functions above if I only had up to 20 regions to monitor (max region limit per App, read CLLocationManager docs) */
/* the mechanics would be more clear and save */
switch state {
case .Inside:
/* create a new noticiation with the same cpecs as the cancled notification but this time withot the region */
let sameNotificationAsAbove = UILocalNotification()
/* if you really need to know your IDs inside userInfo so create some good mechanics to pass these before canceling */
/* at least I would save the identifier of the region iside the noticiation */
/* save the notification somewhere to delete it later from NC */
self.someArrayToSaveDeliveredNotifications.append(sameNotificationAsAbove)
/* fire the notification */
UIApplication.sharedApplication().presentLocalNotificationNow(sameNotificationAsAbove)
case default:
/* if it is true that notication inside NC can be deleted just by calling 'cancelLocalNotification' function */
/* so find your notification inside someArrayToSaveDeliveredNotifications bases on the region.identier which you saved inside userInfo */
let notificationToCancel = self.getNotificationForIdentifier(region.identifier)
UIApplication.sharedApplication().cancelLocalNotification(notificationToCancel)
/* this should delete your notification from NC based on other answers */
}
}
This is some kind of pseudo mechanics I would build if I had to, so if anything is wrong or not correct I would appreciate to hear your feedback. :)
This will clear all of the app's notifications from Notification Center.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
It appears that you can clear a specific notification if you hold onto the UILocalNotification object. Using your the notification object you created in your example above you can call
[[UIApplication sharedApplication] cancelLocalNotification:notification];
to clear the notification.
It looks like very simple solution. follow the below steps it will help you.
Make one background service which will work continue in the background to check your location.
When you are entering in some region fire local notification which is already done by you. good work.
But when you are leaving that region (check background service with enter location details like now location is matching with the location details when user entered in that region). fire the new local notification with empty data. and than it will clear the notification from the notification window also.
You should trigger a background method which should only be called when the device is going out of the location. This can be achieved by creating layer for region and trigger immediately when it is coming out of boundary.
In the method, you may either clear all the notification of the respective app by
[[UIApplication sharedApplication] cancelLocalNotification:notification];
or may clear only specific notification by calling
UIApplication* application = [UIApplication sharedApplication];
NSArray* scheduledNotifications = [NSArray arrayWithArray:application.scheduledLocalNotifications];
application.scheduledLocalNotifications = scheduledNotifications;
You will receive all the valid notification available for the particular app. Delete the specific notification for the specific region.
CLLocationManagerDelegate delegate has a set of methods that get triggered base on the device location. Such as;
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
In your case what you have to do is when the following callback get triggered;
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
remove your notification from the NotificationCenter

Why is a banner not shown for my local notification in iOS 7

My iOS 7 app is generating local notifications in a method called within an NSOperationQueue block. The notifications are appearing in the Notification Center, but they are not showing a banner at the top of the screen. The notifications are being generated while the app is in the background.
I've tried everything I can think of, and done considerable Google searching, but I still can't get the banners to display.
Here is the code that builds and schedules the notification:
// In the most recent case, I have verified that
// alertText = Why not work? and alertAction = View
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = alertText;
localNotification.alertAction = alertAction;
localNotification.alertLaunchImage = launchImage;
UIApplication *application = [UIApplication sharedApplication];
application.applicationIconBadgeNumber++;
localNotification.applicationIconBadgeNumber = application.applicationIconBadgeNumber;
[self performSelectorOnMainThread:#selector(scheduleNotification:)
withObject:localNotification waitUntilDone:NO];
}
- (void)scheduleNotification: (id)notification
{
UILocalNotification *localNotification = (UILocalNotification *)notification;
// Schedule it with the app
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
I have checked the notification settings for my app, and they are:
Alert Style: Banners
Badge App Icon: On
Sounds: Off
Show in Notification Center: On
Include: 5 Recent Items
Show on Lock Screen: On
The bug was actually in a different part of my code. I was generating the notification in a background thread, and the thread was canceled before the notification went out.
If your app is running you can't have this banners (unless you create your own).
A solution could be:
When the app is running, Notification are handle by
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
Then you can use this project (that I use and which is really good) : TSMessages to create something similar as your banner.
Hope that will help...

Resources