22

I recently submitted a new binary to the app store and sent it in for review and it was immediately rejected with the following message. "Unsupported operation - Apps are not allowed to listen to device lock notifications.". After some digging around I found out that we can't use "com.apple.springboard.lockstate" to figure out the lock state.

Essentially, my app needs to know how the user left my app. Whether it was pressing the home button, lock button, leaving the app through hitting another app's notification, etc. Is there any way of achieving this? I started investigating to see if my app were to run in the background, maybe we could check the application state to figure it out. That's as far as I got, I was wondering if anyone had anymore insight on this

TNguyen
  • 1,041
  • 9
  • 24
  • 3
    I doubt this is possible in a legal way (there are just a lot of different ways you can leave an app…) I'd look back to the source of your requirement. How is the user benefiting from this, and how can you provide that to the user in another way? Can you explain the underlying user problem you're solving? We may be able to help with a solution to that. – Rob Napier May 19 '17 at 13:14
  • The app is a productivity app that challenges users to stay on our app. Thus, being able to know how the user leaves our app is vital because if the user stays on our app and only presses the lock button, that person is technically still on our app. However, if the press the home button or what not, they are leaving our app, which ends the challenge or what not. – TNguyen May 19 '17 at 13:22
  • I believe (and haven't tested in a long time, but I thought it was true) that `applicationDidEnterBackground` would not be called in the case of the lock button. In fact, I would expect that all the cases you consider invalidating would call `applicationDidEnterBackground`, while those you would consider non-invalidating (such as receiving a phone call) would only call `applicationWillResignActive`. You will have to test; I haven't explored the details of that in several years. – Rob Napier May 19 '17 at 13:33
  • 1
    @RobNapier unfortunately, even hitting the lock button will call applicationDidEnterBackground. – TNguyen May 19 '17 at 14:18
  • @TPN1994 Knowing this will not solve your problem. Users can navigate to apps from the lock screen (through notifications, camera, today widgets, etc.) and your app will never be re-activated. – GetSwifty Sep 01 '17 at 21:41
  • @PEEJWEEJ yes unfortunately you are correct but at least it's one step forward in solving it. However, I believe that when coupled with `applicationProtectedDataDidBecomeAvailable` it is actually possible to cover those cases that you mentioned as well (obviously the device needs to have a passcode in order for this to work). – TNguyen Sep 01 '17 at 21:56
  • Either way, you're going to be constantly fighting with the APIs to accomplish something Apple doesn't want you to do. I would instead recommend only counting active time and turning on proximity monitoring if battery life is a concern: https://stackoverflow.com/questions/3950194/turn-off-display-in-iphone-os-ios – GetSwifty Sep 01 '17 at 22:04

2 Answers2

17

After searching through the documentation from apple and digging through a ton of threads I think I might have stumbled upon the solution.

As far as I know this is currently the only way of detecting whether a user left through the home button or lock button (I don't believe this works on the simulator you have to try it on an actual phone).

Inside of this delegate (And it will only work when called in this delegate)

func applicationDidEnterBackground(_ application: UIApplication) {

}

You can call this little snippet here:

func DidUserPressLockButton() -> Bool {
    let oldBrightness = UIScreen.main.brightness
    UIScreen.main.brightness = oldBrightness + (oldBrightness <= 0.01 ? (0.01) : (-0.01))
    return oldBrightness != UIScreen.main.brightness
}

Usage:

func applicationDidEnterBackground(_ application: UIApplication) {
    if (DidUserPressLockButton()) {
        //User pressed lock button
    } else {
        //user pressed home button
    }
}

EXPLANATION:

It seems that apple only lets you change the screen brightness from applicationDidEnterBackground when the user left through the lock button and not the home button. So the idea is to change the screen brightness with a miniscule amount and check to see if it was able to be changed. This seems kinda hacky but I've heard that this is actually working as intended. As far as testing it goes it seems to be working 100% of the time. I could not find any issues with this except for users that really do want to change the brightness of the screen. I hope someone else can find something less hacky and more concrete.

TNguyen
  • 1,041
  • 9
  • 24
  • What happens if the user left the app by answering to the phone call? – Andrej Sep 07 '17 at 07:58
  • 1
    @Andrej you can import CoreTelephony and see if a phone call was answered/received/declined.. etc – TNguyen Sep 07 '17 at 14:45
  • I was having issues with your approach setting the brightness to 0.0099 so I came up with my own version. Just call `didEnterBackground` and `didBecomeActive` on my class in `AppDelegate` and it should work: https://gist.github.com/andrewcampoli/2b1f748fecf42288cc09f10a72485b36 – Andrew Campoli Apr 24 '18 at 18:03
  • When I used this in my app it works perfectly however if the lock button is pressed and then the user goes back to their phone the brightness has been set to almost 0 when they unlock it. After it has been unlocked the brightness is back to normal. Not a major problem but a bit annoying. – Matt Apr 21 '19 at 11:08
  • This no longer seems a working solution to detect which button was pressed. Brightness is a scale from 0-1, so the conditional check will be incorrect. This will result in never knowing if the lock button was pressed. – Micah Montoya Jul 02 '19 at 17:10
  • This doesnt work if the user went to the background then pressed the lock button. – FH- Feb 26 '20 at 12:20
1

This cannot be done in a way that Apple will accept it in other words any notification key that’s not explicitly documented by Apple is considered private API.

And it´s true com.apple.springboard.lockstate cannot be used anymore and is considered private.

You have a few ways to detect if the user has left the application:

applicationWillResignActive:

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.

applicationWillEnterForeground:

Called when transitioning out of the background state

applicationDidEnterBackground:

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.

None of these functions tells you how the user left the app except the applicationWillResignActive which tells you that the app is about to go from active to inactive state for certain types of temporary interruptions.

Rashwan L
  • 38,237
  • 7
  • 103
  • 107
  • I would have to disagree, I have actually seen a few apps on the app store (and have been published after the date that apple started rejecting apps that listen to darwin notifications) that were able to accomplish this. – TNguyen Sep 01 '17 at 14:23
  • @ TPN1994 - can you provide a couple of examples of such apps? – BonanzaDriver Sep 01 '17 at 15:01
  • 1
    @BonanzaDriver of course! I'm not sure if its allowed to link apps on SO but the name of an app that accomplishes this off the top of my head would be "Forest" by Seekrtech – TNguyen Sep 01 '17 at 22:00
  • @TPN1994, sure there probably are but if you use undocumented notification strings your application will get rejected. Surely there is probably som hack out there to check this but if Apple would have let you done this there would have been some built in function for this. Hopefully they won´t reject your app because of the hack you added in your answer. Good luck! – Rashwan L Sep 03 '17 at 05:25