I am working on an application that uses iOS's UILocalNotifications to alert the user that action needs to be completed on their part. I am able to create my notifications and have them fire properly. However, when returning to the app I am having an issue. After the first time a notification fired my 'application: didRecieveLocalNotification' runs every time, with the 1st notifications data. I can log out all the notifications in the queue, and even if the queue is empty it will still run the notification block. Has anyone else ran into this problem or know how to fix it. I have included my didRecieveLocalNotification code below.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
if (notification) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
[(UITabBarController *)self.window.rootViewController setSelectedIndex:1];
UINavigationController *nav = [[(UITabBarController *)self.window.rootViewController viewControllers] objectAtIndex:1];
IMTUpdateRewardViewController *rvc = [storyboard instantiateViewControllerWithIdentifier:#"updateReward"];
[rvc loadPushNotification:notification];
[nav pushViewController:rvc animated:NO];
}
}
I have ran into a similar problem. Eventually the culprit was a bug on my part, which was related to generating reminders to events. And if upon return from background, there were events which have already began, then a local notification was generated, and fired immediately. In short, when you encounter something like this, put a small debug printout at your UILocalNotification generating methods
Related
In the default - (void)applicationDidBecomeActive:(UIApplication *)application { method in appDelegate, Apple includes the comment:
//This may also be a good place to direct someone
to a certain screen if the app was in background but they just clicked on a notification
I am trying to do this for local notifications, but can't figure out how to direct the user to a certain screen. The docs say that you can check for an URL but even so, I don't know how to specify an NSUrl for a screen within an app.
This seems like a common use case, we click on notifications all the time that take us to specific parts of app, but not finding any good resources.
Thanks in advance for any suggestions.
You have to use didReceiveLocalNotification or didReceiveNotificationResponse delegate method in your AppDelegate.m file based on which Notification you have implemented
For UILocalNotification:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
// Your redirection code goes here like shown below
MyViewController *controller = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.window.rootViewController.navigationController pushViewController:controller animated:YES];
}
For UNNotification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
// Your redirection code goes here like shown below
MyViewController *controller = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.window.rootViewController.navigationController pushViewController:controller animated:YES];
}
For more reference on UNNotification or handling Local Notification, please refer https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SchedulingandHandlingLocalNotifications.html
I write some app that sends local notification with delay and when the notification received i want to decide what to do based on device movement.
if device is in move - set the same notification with new delay and stay in background.
if device not in move - pop specific view controller.
i do succeed with the "no drive" mode but when the device is in move - i don't really know hot to handle this situation.
here is my code for now, hope for some help
thanks!
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"Notification recieved from background...");
//check if device is in move
CLLocationManager *locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
locationManager.distanceFilter = 50;
[locationManager startMonitoringSignificantLocationChanges];
if (locationManager.location.speed > 10) {
NSLog(#"Device is in drive....");
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];//TODO - Debug Set Real Time Before publish
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
NSLog(#"New Notification sent to device");
self.window.rootViewController = nil;
[self.window makeKeyAndVisible];
}
else{
NSLog(#"Device is not in drive....");
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
IGUViewFillDetailsController *vc = (IGUViewFillDetailsController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"IGUViewFillDetailsController"];
vc.dic = notification.userInfo;
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController: vc];
self.window.rootViewController = vc;
//[self.window addSubview:vc.view];
[self.window makeKeyAndVisible];
}
}
Problem1:
You scheduled a notification and it will show up at the fire date, you can cancel the notification 1 second before the fire date and schedule it again. Say you have this notification to be fired in 10 seconds, you use a NSTimer that will be fired in 9 second, in the selector of the timer you check if the user is moving or not, if he is moving, you cancel the notification and delay it by 10 seconds and schedule it again, remember you also have to update the timer to do the check before next fire date of new notification.
Problem2:
It is the default behaviour, iOS will take a snap shot before app enter background, and when app resign background, it will show this snap shot, you can add a empty view in applicationDidEnterBackground, then this empty view will show up before the wanted view.
I was able to implement the below mentioned library in my project.
Library used:: https://github.com/rolandleth/LTHPasscodeViewController
After entering the pin-code and after verifying that it is correct, i am not able to switch to tab-bar activity after the pin-code entry directly but if i use an NSNotification in between i am able to transfer control.
My code:
- (void) receiveTestNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"TestNotification"])
NSLog (#"Successfully received the test notification!");
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
mainTabViewController *objSec=[storyboard instantiateViewControllerWithIdentifier:#"passID"];
[self.navigationController pushViewController:objSec animated:YES];
After putting an NSLog also i am getting the Log output but the tab-view doesn't come.
Is there a way to call the tab-view directly after pin-entry.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
Tabview done using StoryBoard.
I think it happens because callback method from this library are performed in background thread, try to wrap you code with dispatching on main thread, it should help:
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
mainTabViewController *objSec=[storyboard instantiateViewControllerWithIdentifier:#"passID"];
[self.navigationController pushViewController:objSec animated:YES];
}];
When the App enters the background, it logs the person out of the application (per the specifications).
I want to transition back to the first view controller. This is not a navigation or a tab bar controller (although it does transition into those after the first scene).
I tried this in the AppDelegate
- (void)applicationDidEnterBackground:(UIApplication *)application
{
LoginRegisterViewController *controller = [[LoginRegisterViewController alloc] init];
[self.window setRootViewController:controller];
}
but it just transitions me to a black screen.
Any suggestions?
Rather than performing the transition on applicationDidEnterBackground: do it in applicationWillEnterForeground:.
I think you want something like a locked screen whick will show when enter foreground again. Add a sub UIWindow may be better.
Try This code
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidTimeout:)name:kApplicationDidTimeoutNotification object:nil];
return YES;
}
(void) applicationDidTimeout:(NSNotification *) notif {
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
[navigationController popToRootViewControllerAnimated:YES];
}
(void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
[[NSNotificationCenter defaultCenter] postNotificationName:kApplicationDidTimeoutNotification object:nil];
}
Sweet & simple coding......
This was the only one that worked for me,
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
LoginRegisterViewController *myVC = (LoginRegisterViewController *)[storyboard instantiateInitialViewController];
[self.window setRootViewController:myVC];
in the foreground method.
Why do I want to use presentModalViewController in AppDelegate?
- Processing didReceiveLocalNotification, so I can launch a seperate modalView on top of my app to process the notification
What does my view architecture look like?
- Using storyboards
- MainStoryBoard: ->TabBarController->NavigationController
What's happening?
- Nothing, that's the problem :-D
- When I press the action button from the UILocalNotification, the app opens, but just shows the last open view from the tabbarcontroller.
As you can see below my last effort was to present the modalViewController on top of that current view, like so:
[self.window.rootViewController.tabBarController.selectedViewController.navigationController presentModalViewController:navigationController animated:YES];
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateInactive) {
// Application was in the background when notification was delivered.
NSLog(#"Received notification while in the background");
}
else {
NSLog(#"Received notification while running.");
}
MedicationReminderViewController *controller = [[UIStoryboard storyboardWithName:#"ModalStoryBoard" bundle:nil] instantiateViewControllerWithIdentifier:#"MedicationReminderVC"];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
[self.window.rootViewController.tabBarController.selectedViewController.navigationController presentModalViewController:navigationController animated:YES];
}
Update
Seems that this is nil:
self.window.rootViewController.tabBarController.selectedViewController.navigationController
Solution
[self.window.rootViewController presentModalViewController:navigationController animated:YES];
Try this :
[self.window.rootViewController presentModalViewController:controller
animated:YES];
Have you tried the following?
[self.window.rootViewController.tabBarController.selectedViewController presentModalViewController:navigationController animated:YES];
That said, even if this works, I would really urge you to reconsider your design choices to avoid having to do this. Traversing the navigation stack in this way to access stuff can get very messy and I'd strongly advise against it.