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

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;
}

Related

Understanding app background launch behavior

In apple Doc Understanding When Your App Gets Launched into the Background
Apps that support background execution may be relaunched by the system
to handle incoming events...
I am doing region monitoring and when I get that I am popping a UILocalNotification, but when I tap on UILocalNotification my app didReceiveLocalNotification is called. May be because my app is launched in background.
Second thing I did is I did not tap UILocalNotification and left for few minutes, means my app will terminate by iOS. I drag the notification center and then tap the UILocalNotification still my app enter in didReceiveLocalNotification.
The behavior I am expecting that app now launch in action of delayed UILocalNotification tap must enter in this method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification)
{
NSLog(#"notification caused app to launch, alert body = %#", notification.alertBody);
// do what ever action you want to do
// you could just copy the code from "didReceiveLocalNotification" and paste it here
}
return YES;
}
What is actually happening can please any one explain in detail?
Even after a delay when I tap UILocalNotification app do not enter in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Your app isn't terminated after a few minutes of inactivity.It becomes idle, and only terminated if memory is needed.Remember that it is launched to process the region change in the background, and it sends the notification in the first place, so it's resident in the device memory in this stage.Try to explicitly quiting it using the multitasking interface or launch several memory consuming apps (games usually do the trick) to force it to quit.A few minutes of waiting in the background won't make it terminate.

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.

Displaying an UIAlertView When A User Unlocks iPhone

I have recently been having some trouble with iOS 7 and unlocking. I am trying to get an UIAlertView to display every time a user unlocks his/her device.
I have gotten the same thing to work with respringing. I am using iOSOpenDev with the logos template. Does anyone know how to achieve an Alert right after unlocking with iOS 7's SBLockscreenViewController, or with something else?
Check out these methods from SBLockScreenViewController:
-(void) prepareForUIUnlock;
-(void) finishUIUnlockFromSource:(int)source;
Both should work fine.
Every time when your application becomes active the following action will be called:
- (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.
}

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

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.

Need clarification about UIApplicationState

I need your help in clarifying my understanding of the various states of an app. I am going to state my interpretation - but please feel free to correct me.
1) App is launched and running in the foreground: state = UIApplicationStateActive
2) User pushes home button: state = UIApplicationStateBackground (????).
Debug stmt in my app shows it to be Active
3) User double-taps Home and kills the app: state = UIApplicationStateInactive
If the value for case 2 is indeed Active, then when is the value set to Background?
My location-based app relies on this values to take appropriate action for the user.
if ( ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) ||
([UIApplication sharedApplication].applicationState == UIApplicationStateInactive) ) {
// isInBackground = YES;
// ------ UI not available
}
else {
// isInBackground = NO;
// ------ UI is available
}
Regards,
Sam.
When the user taps on the app icon, the app briefly goes through a transitional state of UIApplicationStateInactive on its way to becoming UIApplicationStateActive. This is where the app gets itself ready to display to the user.
When the app is open, the application state is UIApplicationStateActive.
If the user presses the home button, and is returned to the springboard (home screen), or the application is interrupted by something, such as a phone call, the application state transitions back to UIApplicationStateInactive.
For the application state of your app to become UIApplicationStateBackground, your application would have to register for a background process. Look into how to background your location services.
Apple documentation:
UIApplicationState Constants Reference
Swift: .Active | Objective-C: UIApplicationStateActive
The app is running in the foreground and currently receiving events.
Available in iOS 4.0 and later.
Swift: .Inactive | Objective-C: UIApplicationStateInactive
The app is running in the foreground but is not receiving events. This
might happen as a result of an interruption or because the app is
transitioning to or from the background.
Available in iOS 4.0 and later.
Swift: .Background | Objective-C: UIApplicationStateBackground
The app is running in the background.
Available in iOS 4.0 and later.
Some examples:
UIApplicationStateActive - App is running in foreground. Simple.
UIApplicationStateInactive - E.g. App was in the background and is opening through a push notification (transitioning atm). Or the control/notification center is presented above your app. You kind of see it, is in foreground.
UIApplicationStateBackground - App is in the background, but still running. E.g. playing music. Then - this can take a while or not (depending on process you are running in background), but in one point your app is killed. You will see app's snapshot and icon between minimized apps, but the app will be launch again first.

Resources