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

2

12

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

Extrasystole answered 18/10, 2013 at 14:53 Comment(4)
I don't believe you can do this. In fact, even on iOS 6 you were making assumptions you couldn't just make. The application state is also UIApplicationStateInactive if you were interrupted by a phone call, for example.Viguerie
It has actually been very reliable for my use case, until iOS 7. I simplified the code for the purposes of this question.Extrasystole
I'm trying to say that this code (in iOS 6) may reliably detect when you're coming from the lock screen, but will also detect when the user returns after a phone call. If you somehow were able to distinguish those events, great (and I'm actually interested in how, because as far as I knew, that is/was not possible).Viguerie
For my purposes, the app handled those cases the same. I am only interested in when it comes from springboard which is the behavior that has changedExtrasystole
E
10

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.

Extrasystole answered 28/10, 2013 at 14:46 Comment(10)
Thanks for sharing. I am using iPhone 5 on iOS 7.0. When I press the lock button, I get a reading of 0.9965 or close every time. I am wondering if there is a timing variable as well. Do you have a lot of code that gets executed before you read brightness (hence allowing few previous ms for the screen to dim)....Gerundive
I do not, but I am almost positive it is a race condition here. I have a buddy who implemented this and added a background execution delay of a few ms to make it work. I am still waiting Apple review to get some data points from customers (test in production, yay!) before implementing the same delayExtrasystole
So I tested with an NSTimer on a blank project (and the brightness reading in the selector) and still getting a 1.0 brightness when I press the lock button. I tested with background queue with no luck. Once you have more visibility on the other variables, please update us. Good luck with review.Gerundive
Strange, this works about 99% of the time for everyone I have heard of using it, except on older devices.Extrasystole
I tried this on iPHone 5 with iOS 7.0.4. Doesn't work. [ [UIScreen mainScreen] brightness] always returns value that is set on Settings->Brightness. I even tried to call this method after 5 second delay - same result.Miniskirt
This works fine in iOS 7.1.1 on iPhone 5, but not on iPhone 5S. 5S shows screen brightness = 0.557267.Supply
Works on my iPhone 5S, so it must be some other factor.Extrasystole
If I figure out that factor I'll add it as a comment.Supply
Thank you, it fixed my problem.Portia
Doesn't work. Try this - github.com/binarydev/ios-home-vs-lock-button/blob/master/Test/…Legrand
T
-1

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;
}
Termagant answered 26/10, 2013 at 18:33 Comment(1)
applicationWillEnterForeground is where the above logic is used, and is called the same when coming from from backgrounded on springboard, or when the app is resumed after an auto-lock timeout. The question pertains to where the app is coming from (springboard or unlock) which could be detected in iOS 6 and not in 7 (it seems).Extrasystole

© 2022 - 2024 — McMap. All rights reserved.