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

coneybeare picture coneybeare · Oct 18, 2013 · Viewed 11.9k times · Source

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

Answer

coneybeare picture coneybeare · Oct 28, 2013

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.