Can the application know whether the Home button or the Power button was pressed? [duplicate] - ios

I have an app that needs to do something when it’s sent to background using the Home button and something else when the device is locked using the top hardware button. The standard way of solving these requirements are the notifications and delegate methods sent out by UIApplication. On iOS 4 they look like this:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
// Unlocking the device
Did become active.
In other words, it’s quite easy to tell between locking and backgrounding. On iOS 5 the behaviour changed:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
Did enter background.
// Unlocking the device
Will enter foreground.
Did become active.
Notice that the didEnterBackground and willEnterForeground notifications are now sent out even when (un)locking the device, making it impossible to tell between locking and backgrounding. Is this change documented somewhere? Is it a regression? Do you know another way to distinguish the two cases?

iOS 6
In my preliminary testing via the simulator, checking the application state with
[[UIApplication sharedApplication] applicationState]
in either
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
allows you to differentiate between a call to lock the device and just switching back to the homescreen. A lock screen will return 1 (UIApplicationStateInactive), whereas a home button press will register as a 2 (UIApplicationStateBackground).
It seems consistent and should work on an iOS device just as reliably as it does in the simulator.
iOS 7
The iOS 6 method no longer works in iOS 7. In order to do this now, you have to utilize CFNotificationCenter and listen for a darwin notification (labeled: com.apple.springboard.lockcomplete). You can find the github repo with the sample project here: https://github.com/binarydev/ios-home-vs-lock-button
Credit for the iOS 7 fix goes out to wqq

I have looked into this quite a bit so I would love to be wrong here if someone knows something I don't, but technically, there is no documented way to tell the difference between locking the device, and sending to background.
One thing you can check however, is the UIApplicationState during the transition from foreground to background. Locking a device will give UIApplicationStateInactive and moving the App to the background will give UIApplicationStateBackground. But, since this behaviour is not officially documented it may change in the future.
A basic example:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
NSLog(#"Device state: %#", state);
switch (state) {
case UIApplicationStateActive:
/* ... */
break;
case UIApplicationStateInactive:
/* Device was/is locked */
break;
case UIApplicationStateBackground:
/* User pressed home button or opened another App (from an alert/email/etc) */
break;
}
}
UIApplicationState - The running states of an application
typedef enum {
UIApplicationStateActive,
UIApplicationStateInactive,
UIApplicationStateBackground
}
UIApplicationState
Constants
UIApplicationStateActive - The application
is running in the foreground and currently receiving events. Available
in iOS 4.0 and later.
UIApplicationStateInactive - The application is running in the
foreground but is not receiving events. This might happen as a result
of an interruption or because the application is transitioning to or
from the background.
UIApplicationStateBackground - The application is
running in the background.
According to the UIApplicationDelegate Protocol Reference:
applicationWillResignActive:
didEnterBackground:
// ...
willEnterForeground:
applicationDidBecomeActive:
are the only methods that ever get called in both situations.
According to the iOS 4.3 to iOS 5.0 API Diff, these are the ONLY changes regarding UIApplication or UIApplicationDelegate, so I couldn't find where they documented any of these notification changes:
UIApplication.h
Added -[UIApplication setNewsstandIconImage:]
Added UIApplication.userInterfaceLayoutDirection
Added UIApplicationDelegate.window
Added UIApplication(UINewsstand)
Added UIApplicationLaunchOptionsNewsstandDownloadsKey
Added UIRemoteNotificationTypeNewsstandContentAvailability
Added UIUserInterfaceLayoutDirection
Added UIUserInterfaceLayoutDirectionLeftToRight
Added UIUserInterfaceLayoutDirectionRightToLeft

This is more of a workaround/hack, but according to my experience it's very reliable.
When the device is screen-locked (not just home button-ed, if that's a word :)) - bound network (UDP) sockets are broken.
I was using GCDAsyncUDPSocket (also AsyncUDPSocket before) and they both fire a network/broken pipe error reliably when the device is turned off.
In my case I need the UDP socket anyway, for other apps it might be a bit smelly, however, just binding/listening on a UDP socket without any action is not too terrible if you really need to differentiate here.
This note will [self destruct]; is 5 minutes (so Apple won't find out).

There’s a thread about this issue on Apple Developer Forums (registered developers only, sorry). The gist is that the new behaviour is by design. There are requests for a new API feature to distinguish between the two use cases, but nothing working yet.

Here is what Apple's iOS Programming Guide says:
Pressing the Sleep/Wake button is another type of interruption that
causes your app to be deactivated temporarily. When the user presses
this button, the system disables touch events, moves the app to the
background but sets the value of the app’s applicationState property
to UIApplicationStateInactive (as opposed to
UIApplicationStateBackground), and finally locks the screen.
http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
So, you should check the UIApplication's applicationState property in applicationDidEnterBackground:. If it is UIApplicationStateBackground the user pressed the home button. But if it is UIApplicationStateInactive the user locked the device.

Related

iBeacon-App: Custom code when launched from lockscreen

Since iOS 8, the operating system is indicating an iBeacon-enabled application, which is inside a defined region, with an icon in the left bottom corner of the lockscreen (see http://appleinsider.com/articles/14/06/03/apples-ios-8-uses-ibeacon-tech-brings-location-aware-app-access-to-lock-screen for reference).
I want to make use of this feature in my app by running custom code if the app is launched from the lockscreen (e.g. switch to specific view).
How to check in the Application Delegate if the app was launched from the lockscreen? I couldn't find any documentation on this.
AFAIK, it's not possible to detect whether your app was brought into the foreground via the "Suggested apps" feature (that's what Apple calls the icon on the lock screen), or by any other means (app switcher, app icon on the home screen).
You could do some heuristics. If the app came into the foreground and there are no beacons in range (you can check that with ranging), then there's no way it was via the lock screen icon.
There is a Excellent article on this topic by Matt Coneybeare
Below I have pointed out some of the important points from that article , for more info check out that article.
From Article of Matt Coneybeare
On iOS 6 and lower it could be detected using the current UIApplicationState in applicationWillEnterForeground
UIApplicationState state = [[UIApplication sharedApplication] ApplicationState];
if (UIApplicationStateInactive == state)
// Coming from locked screen (iOS 6)
else
// Coming from Springboard, another App, etc...
But From iOS 7 onwards, the state value is UIApplicationStateBackground in both scenarios.
It seems that there is no possible way to detect where the App is being launched from on iOS 7, but there is a way to detect if you are going to the Lockscreen vs Homescreen (or something else).
The trick is to read the screen brightness in applicationDidEnterBackground.
When the App hits the background due to the lock button being pressed or an auto-lock timeout, the brightness will be 0.0 on iOS 7.
Otherwise, it will be > 0 when the home button is pressed or another App launched from the multitask selector or notification center.
When the app is launched by beacon detection, your AppDelegate's (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region method will be invoked.
You can put any custom code inside that method, perhaps to set a flag indicating this is how the app was launched.
The following code shows how to detect that tapping a local notification launched the app from the lock screen. For details, see: https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
// do something
}
...
return YES;
}
This is not the same as launching the app by tapping the icon on the bottom of the lock screen, but it is similar. If you present a local notification on beacon detection it will work as described above if tapped.
I believe the app icon shows up on the bottom of the lock screen only if your app is launched into the background by beacon detection and does not present a local notification. If this is the case you are looking for, you might examine the launchOptions above and see if a different key is present in this case.

Determining the event when the iOS App is in Closed State to send a LocalNotification on seeing a Beacon

I'm Developing a App using a iBeacon functionality. I want to fire an event when the App is in background & when it is closed. These have different Alert messages. I was able to determine when the App goes to Background. But was finding it difficult when the App gets closed.
I set a checkpoint for the App when it is in background state. But how should we find if the App is closed completely.
Thanks,
i don't know what exactly you want but you can check you app state by following line of code
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
UIApplicationStateBackground when application is in background
UIApplicationStateInactive when app is not in background
Take a look at UIApplicationDelegate's - (void)applicationWillTerminate:(UIApplication *)application method. Seems like what you need. It will be called before application is terminated (for example as a result of user removing it from the list of running apps).

Detecting when app is becoming active from lockscreen vs other on iOS7

My app has different behavior when becoming active from the lockscreen (locked while active), or becoming active from anything else.
On iOS 6 and lower I could detect this
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (UIApplicationStateInactive == state)
// Coming from locked screen (iOS 6)
else
// Coming from Springboard, another app, etc...
But on iOS 7, the state value is UIApplicationStateBackground in both scenarios. Is this the intended behavior? How can I properly detect whether the app is launching from the lockscreen now?
Registered devs, I already posted this on the devforums before the NDA was lifted, see here
I was able to figure out a hack on this and so far seems to be reliable. It only works on the device, not the simulator, and has been tested on an iPhone 5s, 5 and 4S running iOS 7.
It seems that there is no possible way to detect where the app is being launched from on iOS 7, but there is a way to detect if you are going to the lock-screen vs springboard. The trick is to read the screen brightness in applicationDidEnterBackground. When the app hits the background due to the lock button being pressed or an auto-lock timeout, the brightness will be 0.0 on iOS 7. Otherwise, it will be > 0 when the home button is pressed or another app launched from the multitask selector or notification center.
- (void)applicationDidEnterBackground:(UIApplication *)application {
CGFloat screenBrightness = [[UIScreen mainScreen] brightness];
NSLog(#"Screen brightness: %f", screenBrightness);
self.backgroundedToLockScreen = screenBrightness <= 0.0;
}
Now that I have an ivar holding this info, I can use it in applicationWillEnterForeground to determine my app flow.
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (self.backgroundedToLockScreen) {
... // app was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps.
}
self.backgroundedToLockScreen = NO;
}
It is not the exact same as the iOS 6 behavior though. On iOS 6, you could inspect the UIApplicationState to detect where you were coming from, and this solution answers the similar, but not exactly the same, question of where you were going when the app was backgrounded. For example, perhaps the app was backgrounded due to a screen lock timeout, but then a notification for another app woke the device, and the user went there directly from the lockscreen, then back to my app. My app would have determined on backgrounding that the user went to the lockscreen, but when they come back they are actually coming from an active screen. For my app, this difference is negligible, but your milage may vary.
So what about older OS support? My app also supports iOS 6 so I needed to get the old behavior too. Simple. Just the application state monitoring to the foreground method:
- (void)applicationWillEnterForeground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (UIApplicationStateInactive == state || // detect if coming from locked screen (iOS 6)
self.backgroundedToLockScreen) // detect if backgrounded to the locked screen (iOS 7)
{
... // app is coming from or was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps
}
self.backgroundedToLockScreen = NO;
}
I am not sure how reliable the brightness reading is, or whether or not it will change in future OS builds, but in the meantime, this hack seems to be the best we can get. Hope this helps.
Actually the only proper way to set your app behavior when becoming active is via the app delegate methods.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (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.
}
These two are called when the app is running in the background and becomes active either via multitasking UI or after a call or other interruption.
When the app is opened from the Springboard and is not running in the background this method is called:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}

Determining if a UILocalNotification was fired from scheduling or user interaction

I'm trying to determine different scenarios of UIApplication didReceiveLocalNotification:. If a user clicks the notification the while app is inactive, I should bring them to the corresponding UI page. Otherwise, if user just opened the app without tapping a notification, I should let them stay in the UI page which they left off from.
However, there is a little problem as the UILocalNotification was scheduled by myself.
[[UIApplication sharedApplication] scheduleLocalNotification:scheduledAlert];
So every time it fired by iOS, it will call the same delegate method as the callback when I manually clicked the notification on the status bar:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { ... }
I tried some tricky hack such as counting the fired times while app is inactive mode, counting 1 means iOS firing, and counting 2 means user clicked, and relative counting management code.
But I don't think this could be the best practice. I checked the iOS Messages application, which has the same functionality. If you put the app into inactive mode, such as scroll down the status bar, then it can determine the UILocalNotification is from your touch (and will open the text editing mode) or just you return back to the app (stay in the previous status).
I would appreciate if you guys could let me know what the best solution is here!
I use a custom key-value pair which I set a NSMutableDictionary, which I then assign to UILocalNotification.userInfo, to tell different scenarios.
I check the app state so that I only handle the notification if I'm coming from the background:
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateInactive) {
// Your Code Here
} else {
}

applicationWillEnterForeground vs. applicationDidBecomeActive, applicationWillResignActive vs. applicationDidEnterBackground

Which is the proper delegate to implement when an application is waking up from being in the background and you want it to prep it to be active?
applicationWillEnterForeground vs applicationDidBecomeActive -- What's the difference?
Which is the proper delegate to implement for when an application is going to sleep and you want to prep it to cleanup and save data?
applicationWillResignActive vs. applicationDidEnterBackground -- What's the difference?
Also, I've noticed that applicationWillResignActive gets called when an incoming SMS or call comes in but the user chooses to click Ok and continue. I don't want my app to take any action in these cases. I just want it to keep running without any intermediate cleanup since the user didn't exit the app. So, I would think it makes more sense to do cleanup work just in applicationDidEnterBackground.
I would appreciate your input on best practices to follow on choosing which delegates to implement for waking up and going to sleep as well as considering events like being interrupted by SMS/calls.
Thanks
When waking up i.e. relaunching an app (either through springboard, app switching or URL) applicationWillEnterForeground: is called. It is only executed once when the app becomes ready for use, after being put into the background, while applicationDidBecomeActive: may be called multiple times after launch. This makes applicationWillEnterForeground: ideal for setup that needs to occur just once after relaunch.
applicationWillEnterForeground: is called:
when app is relaunched
before applicationDidBecomeActive:
applicationDidBecomeActive: is called:
when app is first launched after application:didFinishLaunchingWithOptions:
after applicationWillEnterForeground: if there's no URL to handle.
after application:handleOpenURL: is called.
after applicationWillResignActive: if user ignores interruption like a phone call or SMS.
applicationWillResignActive: is called:
when there is an interruption like a phone call.
if user takes call applicationDidEnterBackground: is called.
if user ignores call applicationDidBecomeActive: is called.
when the home button is pressed or user switches apps.
docs say you should
pause ongoing tasks
disable timers
pause a game
reduce OpenGL frame rates
applicationDidEnterBackground: is called:
after applicationWillResignActive:
docs say you should:
release shared resources
save user data
invalidate timers
save app state so you can restore it if app is terminated.
disable UI updates
you have 5 seconds to do what you need to and return the method
if you don't return within ~5 seconds the app is terminated.
you can ask for more time with beginBackgroundTaskWithExpirationHandler:
The official documentation.
Managing Your App's Life Cycle is helpful to your questions. For quick concept, you can see Figures in that document.
You can also read the comment from the code generated by the XCode Wizard. Listed as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return 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.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate
timers, and store enough application state information to restore your
application to its current state in case it is terminated later.
If your application supports background execution, this method is called
instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the active state;
here you can undo many of the changes made on entering the background.
*/
}
- (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.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
For more detailed explanations, please refer to official document for UIApplicationDelegate
I was still a bit confused with Dano's answer so I did a little test to get the flow of events in certain scenarios for my reference, but it might be useful to you too. This is for apps that DO NOT use UIApplicationExitsOnSuspend in their info.plist. This was conducted on an iOS 8 simulator + confirmed with iOS 7 device. Please excuse Xamarin's event handler names. They are very similar.
Initial and all subsequent launches from a not-running state:
FinishedLaunching
OnActivated
Interruption (phone call, top slide-down, bottom slide-up):
Home button double-press listing inactive apps, then reselecting our app:
OnResignActivation
OnActivated
Home button double-press listing inactive apps, selecting another app, then relaunching our app:
Home button single press, then relaunch:
Lock (on/off button), then unlock:
OnResignActivation
DidEnterBackground
WillEnterForeground
OnActivated
Home button double-press, and terminate our app: (subsequent relaunch is first case)
OnResignActivation
DidEnterBackground
DidEnterBackground (iOS 7 only?)
Yes, DidEnterBackground is called twice on iOS7 device. Both times UIApplication state is Background. However, iOS 8 simulator does not. This needs testing on iOS 8 device. I will update my answer when I get my hand on it, or someone else could confirm.
applicationWillEnterForeground is called:
when app is relaunched(comes from background to foreground)
This method is not invoked when app starts for the first time i.e when applicationDidFinishLaunch is called but only when comes from background
applicationDidBecomeActive
applicationDidBecomeActive is called
when app is first launched after didFinishLaunching
after applicationWillEnterForeground if there’s no URL to handle.
after application:handleOpenURL: is called.
after applicationWillResignActive if user ignores interruption like a phone call or SMS.
after disappearing of alertView anywhere from the application
applicationWillResignActive is called when system is asking for permissions. (in iOS 10). Just in case someone hit into the same trouble as me...
In iOS 8+ there is a subtle but important difference for taking phone call.
In iOS 7 if user takes phone call both applicationWillResignActive: and applicationDidEnterBackground: are called. But in iOS 8+ only applicationWillResignActive: is called.
For iOS 13+ the following methods will be executed:
- (void)sceneWillEnterForeground:(UIScene *)scene
- (void)sceneDidBecomeActive:(UIScene *)scene

Resources