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

coneybeare
  • 33,113
  • 21
  • 131
  • 183
  • 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. – Scott Berrevoets Oct 18 '13 at 15:45
  • It has actually been very reliable for my use case, until iOS 7. I simplified the code for the purposes of this question. – coneybeare Oct 18 '13 at 15:51
  • 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). – Scott Berrevoets Oct 18 '13 at 16:06
  • 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 changed – coneybeare Oct 18 '13 at 17:13

2 Answers2

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.

coneybeare
  • 33,113
  • 21
  • 131
  • 183
  • 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).... – Khaled Barazi Nov 06 '13 at 11:48
  • 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 delay – coneybeare Nov 06 '13 at 14:49
  • 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. – Khaled Barazi Nov 06 '13 at 17:07
  • Strange, this works about 99% of the time for everyone I have heard of using it, except on older devices. – coneybeare Nov 06 '13 at 18:50
  • 1
    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. – Igor Kulagin Jan 25 '14 at 19:01
  • This works fine in iOS 7.1.1 on iPhone 5, but not on iPhone 5S. 5S shows screen brightness = 0.557267. – MdaG May 27 '14 at 11:34
  • Works on my iPhone 5S, so it must be some other factor. – coneybeare May 27 '14 at 11:59
  • If I figure out that factor I'll add it as a comment. – MdaG May 27 '14 at 12:14
  • Thank you, it fixed my problem. – Dody Rachmat Wicaksono Mar 20 '16 at 00:44
  • 1
    Doesn't work. Try this - https://github.com/binarydev/ios-home-vs-lock-button/blob/master/Test/AppDelegate.m – aqavi_paracha Aug 11 '16 at 11:53
-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;
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Nikos M.
  • 13,685
  • 4
  • 47
  • 61
  • 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). – coneybeare Oct 26 '13 at 19:05