I'm making an app that keeps track of some reminders that repeats with an user defined interval.
I've made it so when the alert displays, the action title says "Renew". When you click this, the app opens, and here I want to create the next reminder, but the problem is that I don't know if the app opens because the notification button was tapped or if the notification fired while the app was running.
Anyone got any ideas?
The 'correct' way to do this is to examine your NSApplication's applicationState property in the application:didReceiveRemoteNotification: method of your delegate.
From the documentation for handling local notifications:
iOS Note: In iOS, you can determine whether an application is launched
as a result of the user tapping the action button or whether the
notification was delivered to the already-running application by
examining the application state. In the delegate’s implementation of
the application:didReceiveRemoteNotification: or
application:didReceiveLocalNotification: method, get the value of the
applicationState property and evaluate it. If the value is
UIApplicationStateInactive, the user tapped the action button; if the
value is UIApplicationStateActive, the application was frontmost when
it received the notification.
This is similar to your solution using flags set in applicationWillEnterForeground and applicationDidBecomeActive but with system support.
I don't know if my question was unclear but it seems that I got 4 different answers that all misinterpreted my question :P
However, I discovered that the didReceiveLocalNotivication happens between willEnterForeground and didBecomeActive, so I did this to determine if the app was already open or not:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"Opened from notification? %#", wasInactive ? #"yes!" : #"no!");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
wasInactive = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
wasInactive = NO;
}
Look up the documentation for UIApplication launch option keys. The last parameter to your application:didFinishLaunchingWithOptions: delegate method contains the information you need.
Also, look at application:didReceiveLocalNotification.
You're looking for
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
or
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
Documentation
If your application is already running you'll get this delegate message on the app delegate
application:didReceiveLocalNotification:
If it wasn't running you'll have to use
application:didFinishLaunchingWithOptions:
You need to respond appropriately in both methods to cover all cases
UPDATED
To detect if the user activated the action button requires a little more complexity. We can tell that application:didFinishLaunchingWithOptions: will have the local notification as a launch option, but it's more difficult with the application:didReceiveLocalNotification:.
Since the application becomes active after the user taps the button, we have to defer until we see that message (or not). Set an NSTimer in application:didReceiveLocalNotification and cancel it in didBecomeActive. That means the user pressed the action button. If the timer isn't cancelled the user was inside the app when it fired.
In your app delegate in this method:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You have to examine the launchOptions looking at this key:
UIApplicationLaunchOptionsLocalNotificationKey
When you are already active this will be called:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
Related
I have some UILocalNotifications in my app. When I receive one while I am in the app or if I press on the notification banner from the block screen or when it appears, the didReceiveLocalNotification works just fine. However, if I do not use the application and if I press on the icon of it (not on the banner) after notification banner appeared, the didReceiveLocalNotification method is not being called.
What should I do in this case, if I still want receive information about local notifications? Can I get it somehow in the applicationDidBecomeActive: method for example?
If you're app is completely shut down and you open it with a local notification, your local notification info will be passed into application:didFinishLaunchingWithOptions:.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification)
{
// Do whatever
}
// ...
}
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.
I already use the:
(void)applicationDidEnterBackground:(UIApplication *)application {}
method, but I can't differentiate if is because press the home button or the on/off button.
Thanks in advance,
For the on/off button(or an incoming call or SMS):
- (void)applicationWillResignActive:(UIApplication *)application
For the Home button:
- (void)applicationDidEnterBackground:(UIApplication *)application
With the notification of applicationWillResignActive, applicationDidBecomeActive will still enter while you are entering in background. But there is a way to differentiate by getting the state of the app, so try this in applicationDidEnterBackground.
- (void)appHasGoneInBackground {
bool inBackground = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
// lockScreen state
if (!inBackground) {
// do something
}
}
Apple's UIApplication-class reference
Use - (void)applicationDidEnterBackground:(UIApplication *)application {} when your app is entering the background (home button) and - (void)applicationWillTerminate:(UIApplication *)application when it's about to be closed (on/off button or iOS call to close after a random time in background).
My understanding is that when you lock or unlock your iOS device your application delegate will call - (void)applicationWillResignActive:(UIApplication *)application and - (void)applicationDidBecomeActive:(UIApplication *)application, respectively. Locking and unlocking are similar to receiving an interruption like a phone call. Sending your application to the background by hitting the home button calls different methods, namely - (void)applicationDidEnterBackground:(UIApplication *)application and - (void)applicationWillEnterForeground:(UIApplication *)application.
I am sending Push Notifications to my iPhone app, and I'd like a different set of instructions to execute depending on whether the app is already launched or not. I'm new to iPhone development, and while I suspect UIApplication or my project's AppDelegate class has the solution, I haven't found a good answer. Is there an easy way to check for this?
Here's the more appropriate way of handling active/inactive state of the app.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// check for the app state
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
//the app is in the foreground, so here you do your stuff since the OS does not do it for you
//navigate the "aps" dictionary looking for "loc-args" and "loc-key", for example, or your personal payload)
}
application.applicationIconBadgeNumber = 0;
}
didReceiveRemoteNotification: is called when the app is running, yes, but when it is suspended, the iOS takes care of putting up the badge, etc. If the app is in the foreground, the OS does nothing, and just calls your didReceiveRemoteNotification:.
Depending upon what you mean by "launched", you are either looking for:
Kevin's answer above (differentiates between launched or not launched)
or this (differentiates between suspended or active, but already launched):
Use a flag that is set true when the application becomes active, and false when the application is not active.
Flag (in header file [.h]):
BOOL applicationIsActive;
Code (in implementation file [.m]):
- (void)applicationDidBecomeActive:(UIApplication *)application {
applicationIsActive = YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
applicationIsActive = NO;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if (applicationIsActive) {
// Handle notification in app active state here
}
else {
// Handle notification in app suspended state here
}
This works because when the application is suspended, the OS calls "applicationDidReceiveRemoteNotification" before it calls "applicationDidBecomeActive" during the "wake-up" process.
The "complete" answer is actually Kevin's answer plus this answer.
Hope this helps.
The UIApplication delegate has the method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
which you need to implement. This receives the notification when the app is running.
If your app is not currently running and a notification is received then your app can be launched with
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
with the notification details held in the launchOptions dictionary. if the dictionary is nil then the user tapped the application icon as normal.
If you are going to check applicationState on iOS less than 4, you'll need to check that applicationState is supported:
if ([application respondsToSelector:#selector(applicationState)] ){
// Safe to check applicationState
UIApplicationState state = [application applicationState];
}
The Apple documentation for push notifications explains this:
However, there are two situations where applicationDidFinishLaunching: is not a suitable implementation site:
The application is running when the notification arrives.
The notification payload contains custom data that the application can use.
In the first case, where the application is running when iPhone OS receives a remote notification, you should implement the application:didReceiveRemoteNotification: method of UIApplicationDelegate if you want to download the data immediately. After downloading, be sure to remove the badge from the application icon. (If your application frequently checks with its provider for new data, implementing this method might not be necessary.)
This means that if your application:didReceiveRemoteNotification: delegate method is called, your app is running.
I'm trying to handle phone calls and standby and things of that nature. I added the function
- (void)applicationWillResignActive:(UIApplication *)application
and
- (void)applicationDidBecomeActive:(UIApplication *)application
to my UIApplicationDelegate. When coming out of standby, "applicationDidBecomeActive" always gets called. However the issue is "applicationWillResignActive" never gets called.
I was wondering if anyone has run into this issue and whether or not anyone found a reason.
EDIT
More info, I discovered that my engine's update loop that gets run from applicationDidFinishLaunching was causing me to miss the message. I call
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, .002, FALSE) == kCFRunLoopRunHandledSource);
to catch all iphone messages but it doesn't seem to catch the resignActive message before becoming inactive.
Attempting to fork a thread for my update loop is causing weird crash bugs. Anyone have any quick fix suggestions?
Its getting called in iOS 4.0 , when the Home button is hit.
The following delegate methods are called when the Home button is hit in iOS 4.0
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"Application Did Resign Active");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Application Did Enter Background");
}
And when you double tap the home button and again relaunch the App , the following delegate methods are called .
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Application Will Enter Foreground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"Application Did Become Active");
}
I don't think
- (void)applicationWillResignActive:(UIApplication *)application
is called when a phone call is received. I think the OS waits for the user to either answer or declines the phone call. If it i declined, then the app says alive and
- (void)applicationDidBecomeActive:(UIApplication *)application
is called. If it is answered, then your app is told to exit and it will receive
- (void)applicationWillTerminate:(UIApplication *)application
Be sure to allow
- (void)applicationDidFinishLaunching:(UIApplication *)application
to return before running your game loop. One technique is to use the function
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
on the application delegate and run your game loop after a delay of say ".01" After you do this, the message should be able to fire properly. I believe the reason for the message stomping was because the run loop was stuck on applicationDidFinishLaunching and wasn't able to push any other UIApplicationDelegate messages onto the queue.
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active state 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.
*/
}