I am using UILocalNotification. When the notification shows, and the user clicks my alertAction, how can I direct them to a specific view when my app loads? (Similar to how the calendar app shows you the event that just was alerted).
I am using:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOption {
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ItemListKey];
// [viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
NSLog(#"has localNotif %#",itemName);
}
return YES;
}
You need to structure your application view controllers so that you can show a particular view from within the applicationLaunch. That may mean, for example, programmatically re-creating your entire view controller structure without the benefit of any user interactions. You may have to manually select tab bar tabs, manually create navigation controller stacks, etc.
Edit: additionally, there are three cases to handle with local notifications:
the app receives the local notification and was brought from background to foreground (so all your view controller structures are already intact, but you still have to manually "navigate" to the right place)
the app receives the local notification and was already running in the foreground
the app was just launched
Related
The problem:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
is not called sometimes with iOS7.
It doesn't mater how we schedule the notification:
alarm.fireDate = [[NSDate date] dateByAddingTimeInterval:0.1];
[app scheduleLocalNotification:alarm];
or:
[app presentLocalNotificationNow:alarm];
My thoughts:
This happens in the case when the user slides before the notification alert animation is finished.
And if he waits just a half second before he slides - the notification is fired and the App proceeds as expected.
The problem probably is that application enters foreground before the notification is received.
Did anyone meet this? Is it a bug? Any solution? Thank you!
Is your application in the background or foreground? If it's in the foreground, I'm pretty sure that method is called. If it isn't, maybe you aren't putting that method in your application delegate.
If it's on the background, here's a few possible scenarios:
Your app has been killed by the user or the OS. In this case when the user wake up your app by tapping on the notification on the notification centre (or swiping in lock screen), your application delegate will have the application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method called. You can get the notification from this method by:
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
Your app is in background, and the user tap on the notification in notification centre or lock screen. In this case, no delegate methods will be called. The documentation specifically said that didReceiveLocalNotification: is for when the app is in the foreground:
If the app is running in the foreground, there is no alert, badging,
or sound; instead, the application:didReceiveLocalNotification: method
is called if the delegate implements it.
So hopefully you can make an informed decision about what to do when you receive the notification. I personally find it a little bit weird that we don't get the notification object when the user launches the app by tapping the icon (not the notification). But I currently just write my logic around it.
Apple explicitly mentions in their documentation (Local and Remote Notification Programming Guide) that different methods get called depending on what state the app is in and what action the user takes (as Enrico mentioned).
Summary:
The user taps a custom action button in an iOS 8 notification.
In this case, iOS calls either application:handleActionWithIdentifier:forRemoteNotification:completionHandler: or application:handleActionWithIdentifier:forLocalNotification:completionHandler:. In both methods, you get the identifier of the action so that you can determine which button the user tapped. You also get either the remote or local notification object, so that you can retrieve any information you need to handle the action.
The user taps the default button in the alert or taps (or clicks) the app icon. ... 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). ...
The notification is delivered when the app is running in the foreground. The app calls the UIApplicationDelegate method application:didReceiveLocalNotification: or application:didReceiveRemoteNotification:fetchCompletionHandler:.
So didReceiveLocalNotification is only fired when the app is already running and is in the foreground. You should also handle the second scenario where the isn't running, for which apple has the following code example:
Objective-C:
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
[viewController displayItem:itemName]; // custom method
app.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
}
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
Swift (approximation provided by myself):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let localNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let itemName = localNotification.userInfo?[ToDoItemKey] as? String {
handleNotification(localNotification)
application.applicationIconBadgeNumber = localNotification.applicationIconBadgeNumber - 1
}
}
.
.
.
}
I'm writing a game for iOS and I was wondering how to make the instructions View Controller open every time the app is opened. I want to have a switch that says "Show me this every time." and if they switch it to no the instructions will no longer show up when the app is opened.
You can use NSUserDefaults to store the switch value, then check for it every time app launches in Your app delegate, applicationDidBecomeActive method.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
BOOL switchState = [[NSUserDefaults standardUserDefaults] boolForKey:#"switchKey"];
if(switchState) {
//If switch is on create the instance of InstructionViewController
//you can call any of InstructionViewController methods on it.
InstructionViewController* intructionsViewController = [[InstructionViewController alloc] init];
//Present the instance of instruction view on top of your current view
[self.window.rootViewController presentViewController:controller animated:YES completion:nil];
}
}
I receive a pust notification and push a view controller based on the data i get in push.
I do it like this:
UINavigationController *navVc=(UINavigationController *) self.window.rootViewController;
PictureTakeVC *pvc=[[PictureTakeVC alloc] init];
[navVc pushViewController:pvc animated:NO];
It works, but the view controller that was opened before i pressed home button shows for a moment.
I also tried this but it happens the same:
PictureTakeVC *pvc=[[PictureTakeVC alloc] init];
NSArray *vcs=[[NSArray alloc] initWithObjects: pvc, nil];
UINavigationController *navVc=(UINavigationController *) self.window.rootViewController;
navVc.viewControllers=vcs;
self.window.rootViewController = navVc;
How to push vc didReceiveRemoteNotification so that it opens immediatly and no other vc is shown for a moment?
As you said "but the view controller that was opened before i pressed home button shows for a moment.",
The reason is not the code, but the iOS system.
It captures screenshot of the screen when app goes in background. As documented:
Remove sensitive information from views before moving to the
background. When an app transitions to the background, the system
takes a snapshot of the app’s main window, which it then presents
briefly when transitioning your app back to the foreground. Before
returning from your applicationDidEnterBackground: method, you should
hide or obscure passwords and other sensitive personal information
that might be captured as part of the snapshot.
So, if snapshot is removed then it will directly show the new pushed view controller.
(not sure about apple guideline for snapshot)
Update:
One approach can be:
Add a black colored background on the app screen when it goes in background(in applicationDidEnterBackground method). So, it will show black screen while coming back to application.{ They store it for showing launch image for already started application.}
Where it helps?
When we delete snapshots from 'Preference' directory of application sandbox(after application goes in background) and come back to application it shows black for a while as it does not have any snapshot.
The outcome of our approach and removing snapshot are same.
Since PictureTakeVC is a view controller just use this instead
PictureTakeVC *pvc = [[PictureTakeVC alloc] initWithNibName:#"PictureTakeVC" bundle:nil];
[self.navigationController pushViewController:pvc animated:YES];
Before your app goes into background mode remove the view that is there.
Are you missing something? The didReceiveRemoteNotification fires when your application is active and running.
...but anyway if you want to achieve what you are asking you have to detect there is a pushnotification from didFinishLaunchingWithOptions (or may be from applicationDidBecomeActive or applicationWillEnterForeground) and push viewcontroller on rootviewcontroller from one of these methods.
To detect whether there were remote notification:
NSString *params = [[launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"] objectForKey:#"View"];
if (params)
{
// push view
}
Use following methods to push view, you won't see any other vc when application gets activated. Let me know if any issues.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
I have done similar for push notification so let me know what are you trying to achieve exactly if I understood wrong.
With iOS 5, push notifications can appear as banner and disappear after a few seconds.
I understand that didReceiveRemoteNotification will be called when user taps on the banner.
My question is, if the banner has disappeared and my user sees that there is a badge number on the app, they will tap on the app icon to start the app. Now if the app is running in the background, how do I check that the app is brought to foreground and there has been a notification, and do the necessary?
The purpose of my notification is basically to inform user there has been an update to the app content and encourage them to run the app to get the latest contents. My app only checks for latest contents at launch time and doesn't check for updates periodically.
This question is a bit old, but I'll pop what I've found in here anyway.
There are two methods you need to implement in your app delegate to check if your app was either launched from the remote notification (From when the app is not running on your device), or received the remote notification while running (in the background or foreground).
First is a method that is already in your App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
To check if this was launched from a remote notification, have some code similar to this:
// Check to see if launched from notification
if (launchOptions != nil)
{
NSDictionary* dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (dictionary != nil)
{
NSLog(#"Launched from push notification: %#", dictionary);
// DO SOMETHING HERE
}
}
The other method you will need to implement is specifically for the case that your application for when your application is running:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"Received notification: %#", userInfo);
}
How you handle the notification from there is up to you, but that's how your app knows about it!
In this second method, you can check the UIApplicationState of the passed application to find out if you were in the foreground or background.
I am scheduling a local notification which is firing fine. I need to know how to allow a user to launch my app after the notification has been displayed on screen.
I also need to know how to identify this type of launch to direct the user to a specific view, relevant only to users who have arrived as a result of the notification.
I have been looking at the UIApplicationDelegate protocol reference and feel it could be in the area of the launchOptions within -didFinishLaunchingWithOptions:, but need a little pointer.
Your application will launch automatically, you don't have to do anything in the app itself for that to happen.
If that's not already happening, check that you've specified an alertAction for the notification when you create it - that's the label of the button in the notification alert that opens your app and if you don't set it, the notification alert won't launch the app.
To open a specific view, use the launchOptions and the application:didReceiveRemoteNotification method.
didFinishLaunchingWithOptions: would do the trick.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
// .. etc
};
Keep in mind you also need to implement
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
if you receive a push notification while the app is running (fore- or background)
The launch options should contain the key UIApplicationLaunchOptionsLocalNotificationKey which in turn gives you the UILocalNotification associated with the notification. Additionally, when scheduling the notification your UILocalNotification has the properties alertBody, alertAction and optionally alertLaunchImage which control the information being displayed. The action decribes the button text that launches your app while the body is the information being deisplayed above the buttons. You can supply a specific launch image for this launch to mimic the app already being run.
Also note Bogatyr's answer concerning the cases where your app is not being launched but already there, just suspended.