21

How can I detect screen lock/unlock events on the iPhone? When the user unlocks it, I want to show a notification alert from my iPhone app. (For Just like Broadcast Receiver for screen unlock in Android.)

James Webster
  • 31,873
  • 11
  • 70
  • 114
Vikas S Singh
  • 1,748
  • 1
  • 14
  • 29

7 Answers7

24

Check this out, I wanted to detect the lock/unlock events, I solved it by Darwin notifications. You can detect the event when the device is locked by "com.apple.springboard.lockcomplete".

//call back
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    // the "com.apple.springboard.lockcomplete" notification will always come after the "com.apple.springboard.lockstate" notification

    NSString *lockState = (NSString*)name;
    NSLog(@"Darwin notification NAME = %@",name);

    if([lockState isEqualToString:@"com.apple.springboard.lockcomplete"])
    {
        NSLog(@"DEVICE LOCKED");
    }
    else
    {
        NSLog(@"LOCK STATUS CHANGED");
    }   
}


-(void)registerforDeviceLockNotif
{
    //Screen lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.lockcomplete"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.lockstate"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);  
}   
Nate
  • 31,017
  • 13
  • 83
  • 207
Rohit Kashyap
  • 934
  • 2
  • 8
  • 23
  • 1
    Actually this code could be problematic in this case. com.apple.springboard.lockstate" will come both when on lock and unlock. So, if you try to do some action only on unlock, this code will require some additional logic. As example tracking your current status (whether device is locked or not). If device is locked and you got "com.apple.springboard.lockstate" then it's real unlock event else it's just prelude to com.apple.springboard.lockcomplete – Victor Ronin Feb 12 '13 at 15:55
  • Now you can track the state completely: int result = notify_get_state(notifyToken, &state); See the link : http://stackoverflow.com/questions/14352228/is-there-a-away-to-detect-the-event-when-ios-device-goes-to-sleep-mode-when-the – Rohit Kashyap Feb 28 '13 at 15:04
  • Please take a look on the pitfalls of notify_get_state method, which I wrote and Nate answered here (http://stackoverflow.com/questions/14820249/getting-state-for-system-wide-notifications-in-ios-and-os-x). – Victor Ronin Feb 28 '13 at 17:20
  • good one , but it works reverse.DEVICE LOCKED call on unlock screen and I want that so get +1 by me. – Prince Kumar Sharma Jun 19 '13 at 06:25
  • Thanks for that solution. I want little bit more help. I want to develop an app to trace phone usages by listening every screen lock/unlock events on iOS devices. I am perfectly receiving those notifications while app is in foreground but for background mode or when the app is not running at all, I fail to listening those notifications. Is there a way that I an listen to them even when the app is not running or the app is in background? – Sauvik Dolui Feb 22 '16 at 06:28
  • 1
    Actually, the comment in the code is not correct. `lockcomplete` may actually come before `lockstate`. I'm observing that right now on iOS 9.3. Best not to rely on order here. – Nate Oct 08 '16 at 01:03
  • 3
    This solution seems no longer available. https://forums.developer.apple.com/message/224401#224401 – Dae KIM Apr 21 '17 at 04:15
  • @SauvikDolui Sauvik - were you able to solve the ability to track phone use by tracking locking and unlocking? – Praxiteles Jun 01 '17 at 20:12
4

To detect lock/unlock inside app in swift 5 only this worked for me:

override func viewDidLoad() {
    super.viewDidLoad()

     NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.willEnterForegroundNotification, object: nil)
     NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
}

@objc func applicationDidBecomeActive(notification: NSNotification) {
    print("ACTIVE")
}
@objc func applicationDidEnterBackground(notification: NSNotification) {
    print("BACKGROUND")
}
Vadim
  • 3,855
  • 2
  • 17
  • 22
2
  1. You can't use com.apple.springboard.lockcomplete or com.apple.springboard.lockstate when submitting your app to App Store, your app will be rejected because it's private API.

  2. The com.apple.springboard.lockcomplete notification NOT always comes after the com.apple.springboard.lockstate notification, it may happen early or later. You need to set a timer to wait for that event.

So here is how you can detect screen lock and unlock status, in Swift 5:

struct NotificationName {
    // Listen to CFNotification, and convert to Notification
    public static let lockComplete = Notification.Name("NotificationName.lockComplete")
    public static let lockState = Notification.Name("NotificationName.lockState")

    // Handle lockComplete and lockState Notification to post locked or unlocked notification.
    public static let locked = Notification.Name("NotificationName.locked")
    public static let unlocked = Notification.Name("NotificationName.unlocked")
}

func addNotificationObservers() {
    let lockCompleteString = "com.apple.springboard.lockcomplete"
    let lockString = "com.apple.springboard.lockstate"

    // Listen to CFNotification, post Notification accordingly.
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                    nil,
                                    { (_, _, _, _, _) in
                                        NotificationCenter.default.post(name: NotificationName.lockComplete, object: nil)
                                    },
                                    lockCompleteString as CFString,
                                    nil,
                                    CFNotificationSuspensionBehavior.deliverImmediately)

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                    nil,
                                    { (_, _, _, _, _) in
                                        NotificationCenter.default.post(name: NotificationName.lockState, object: nil)
                                    },
                                    lockString as CFString,
                                    nil,
                                    CFNotificationSuspensionBehavior.deliverImmediately)

    // Listen to Notification and handle.
    NotificationCenter.default.addObserver(self,
                                            selector: #selector(onLockComplete),
                                            name: NotificationName.lockComplete,
                                            object: nil)

    NotificationCenter.default.addObserver(self,
                                            selector: #selector(onLockState),
                                            name: NotificationName.lockState,
                                            object: nil)
}

// nil means don't know; ture or false means we did or did not received such notification.
var receiveLockStateNotification: Bool? = nil
// when we received lockState notification, use timer to wait 0.3s for the lockComplete notification.
var waitForLockCompleteNotificationTimer: Timer? = nil
var receiveLockCompleteNotification: Bool? = nil

// When we received lockComplete notification, invalidate timer and refresh lock status.
@objc
func onLockComplete() {
    if let timer = waitForLockCompleteNotificationTimer {
        timer.invalidate()
        waitForLockCompleteNotificationTimer = nil
    }

    receiveLockCompleteNotification = true
    changeIsLockedIfNeeded()
}

// When we received lockState notification, refresh lock status.
@objc
func onLockState() {
    receiveLockStateNotification = true
    changeIsLockedIfNeeded()
}

func changeIsLockedIfNeeded() {
    guard let state = receiveLockStateNotification, state else {
        // If we don't receive lockState notification, return.
        return
    }

    guard let complete = receiveLockCompleteNotification else {
        // If we don't receive lockComplete notification, wait 0.3s.
        // If nothing happens in 0.3s, then make sure we don't receive lockComplete, and refresh lock status.
        waitForLockCompleteNotificationTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in
            self.receiveLockCompleteNotification = false
            self.changeIsLockedIfNeeded()
        })
        return
    }

    // When we determined lockState and lockComplete notification is received or not.
    // We can update the device lock status by 'complete' value.
    NotificationCenter.default.post(
        name: complete ? NotificationName.locked : NotificationName.unlocked,
        object: nil
    )

    // Reset status.
    receiveLockStateNotification = nil
    receiveLockCompleteNotification = nil
}
Alen Liang
  • 529
  • 5
  • 13
1

May be you need to implement following methods in AppDelegate:

Tells the delegate that the application is now in the background.

- (void)applicationDidEnterBackground:(UIApplication *)application

Tells the delegate that the application has become active.

- (void)applicationDidBecomeActive:(UIApplication *)application

Tells the delegate that the application is about to become inactive.

- (void)applicationWillResignActive:(UIApplication *)application
Nekto
  • 17,837
  • 1
  • 55
  • 65
  • @ Nekto Actually i want the if i exit the application and lock the iPhone , and after some time i have unlock the iPhone , then exit Applications show the notifications or Alert the Start-up the Applications. For Example just like Window in Start-up options , skype , gtalk etc. – Vikas S Singh Oct 25 '11 at 11:29
  • I don't understand what you want. May be it is not possible. – Nekto Oct 25 '11 at 12:10
  • 1
    @ Nekto i want to simply say That: how can i make my application start on the iPhone start up ? – Vikas S Singh Oct 25 '11 at 12:35
  • UIApplication.shared.isIdleTimerDisabled = true – Gauri Shankar May 04 '20 at 10:35
1

Actually I want the if I exit the application and lock the iPhone , and after some time I have unlock the iPhone , then exit Applications show the notifications or Alert the Start-up the Applications.

You can't do that on the iPhone.

Mihir Oza
  • 2,768
  • 3
  • 35
  • 61
Oliver
  • 23,072
  • 33
  • 138
  • 230
  • 6
    I realize that you are trying to respond to a **comment** the poster made below, but **answers** should be answers to the *question* posed above, and as such, this answer is not really correct. If you have a response to a comment, it should be made as another comment where the original one was made. – Nate Jan 19 '13 at 00:47
  • @Nate : You're right, this seems to be a post on my beginnings when I didn't perfectly masterize the way SO was working. – Oliver Jan 19 '13 at 01:32
  • No problem. You can delete an old answer if you like, and simply move the content ("You can't do that on the iPhone") to the comment section where it is best located. Of course, it looks like Nekto also made the same comment, so maybe just upvoting his comment is enough :) – Nate Jan 19 '13 at 01:47
  • I don't think this answer is correct. There are several apps on my phone that seem to show a notification exactly after I unlock my phone. (Foursquare, I'm looking at you.) – Carl G Mar 04 '17 at 19:20
0

The original question is quite old, so for anybody who stumbles on this:

On modern devices with Face ID and Touch ID, you can use the AppDelegate methods applicationProtectedDataDidBecomeAvailable(_:) (docs) and applicationProtectedDataWillBecomeUnavailable(_:) (docs).

These are called when the device storage is decrypted or encrypted. They are intended to let the app know when data from the storage can be read or becomes unavailable. However, because device encryption is active on almost any iPhone today, you can also use these to detect screen lock and unlock events.

However, this of course only works as long as your app is actively running.

codingFriend1
  • 6,487
  • 6
  • 45
  • 67
-2

From the current view controller your should add an observer for UIApplicationDidEnterBackgroundNotification and remove the observer during dismissing the view controller [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];

  • 2
    This is not wha the OP asked. They asked about detecting locks/unlocks of the iPhone and your solution only fires when the app goes to background, there are multiple reasons an app can go to background, user hit home button, user tapped a notification that took them to another app, user answered a call, etc. – esttorhe Mar 06 '16 at 00:39