简体   繁体   中英

Is there a way to check if the iOS device is locked/unlocked?

I have used GPS location updates in my application. I want to detect if the iOS device is in sleep mode so that I can turn off the GPS location updates and optimize the battery use. I have already tried pausesLocationupdates in iOS 6, but it does not work as desired. I want to turn off the GPS location updates as soon as the device goes to sleep mode. I want to detect the lock/unlock event in the device.

Is there any way to achieve this functionality ?

so far I got the darwin notifications as given below

-(void)registerForall
{
    //Screen lock notifications
    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.iokit.hid.displayStatus"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);


    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.lockstate"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.hasBlankedScreen"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                    NULL, // observer
                                    displayStatusChanged, // callback
                                    CFSTR("com.apple.springboard.lockcomplete"), // event name
                                    NULL, // object
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

}
//call back
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSLog(@"IN Display status changed");
    NSLog(@"Darwin notification NAME = %@",name);


}

I am able to get the darwin notifications when device is locked/unlocked, but the real problem is how identify between if the notification has come from locking or the unlocking of the device. Console logs are:

 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.apple.springboard.lockcomplete
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.apple.springboard.lockstate
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.apple.springboard.hasBlankedScreen
 LockDetectDemo[2086] <Warning>: IN Display status changed
 LockDetectDemo[2086] <Warning>: Darwin notification NAME = com.apple.iokit.hid.displayStatus

Any private API would also suffice. Thanks in advance.

I solved it like this:

//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
    CFStringRef nameCFString = (CFStringRef)name;
    NSString *lockState = (NSString*)nameCFString;
    NSLog(@"Darwin notification NAME = %@",name);
    
    if([lockState isEqualToString:@"com.apple.springboard.lockcomplete"])
    {
        NSLog(@"DEVICE LOCKED");
        //Logic to disable the GPS
    }
    else
    {
        NSLog(@"LOCK STATUS CHANGED");
        //Logic to enable the GPS
    }
}

-(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);
}

Note: the " com.apple.springboard.lockcomplete " notification will always come after the "com.apple.springboard.lockstate" notification

Update

The order of the two notifications can no longer be relied upon, as of recent versions of iOS

Apps are not allowed to listen to device lock notifications now!.

I had receive this:

Dear developer,

We have discovered one or more issues with your recent submission for "xxxx". To process your submission, the following issues must be corrected:

Unsupported operation - Apps are not allowed to listen to device lock notifications.

Once these issues have been corrected, use Xcode or Application Loader to upload a new binary to iTunes Connect. Choose the new binary on the app's Details page in My Apps on iTunes Connect, and click Submit for Review.

Regards,

The App Store team
April 26 2017 at 10:56

/* Register app for detecting lock state */

 -(void)registerAppforDetectLockState {

     int notify_token;
     notify_register_dispatch("com.apple.springboard.lockstate",     &notify_token,dispatch_get_main_queue(), ^(int token) {
     uint64_t state = UINT64_MAX;
     notify_get_state(token, &state);
     if(state == 0) {
        NSLog(@"unlock device");
     } else {
        NSLog(@"lock device");
     }

     NSLog(@"com.apple.springboard.lockstate = %llu", state);
     UILocalNotification *notification = [[UILocalNotification alloc]init];
     notification.repeatInterval = NSDayCalendarUnit;
     [notification setAlertBody:@"Hello world!! I come becoz you lock/unlock your device :)"];
     notification.alertAction = @"View";
     notification.alertAction = @"Yes";
     [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
     notification.soundName = UILocalNotificationDefaultSoundName;
     [notification setTimeZone:[NSTimeZone  defaultTimeZone]];

     [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

  });

 }

Here's a better solution

#import <notify.h>

#define kNotificationNameDidChangeDisplayStatus                 @"com.apple.iokit.hid.displayStatus"

@interface YourClass ()
{    
    int _notifyTokenForDidChangeDisplayStatus;
}

@property (nonatomic, assign, getter = isDisplayOn) BOOL displayOn;
@property (nonatomic, assign, getter = isRegisteredForDarwinNotifications) BOOL registeredForDarwinNotifications;

@end

- (void)registerForSomeNotifications
{
    //
    // Display notifications
    //

    __weak YourClass *weakSelf = self;

    uint32_t result = notify_register_dispatch(kNotificationNameDidChangeDisplayStatus.UTF8String,
                                               &_notifyTokenForDidChangeDisplayStatus,
                                               dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l),
                                               ^(int info) {
                                                   __strong YourClass *strongSelf = weakSelf;

                                                   if (strongSelf)
                                                   {
                                                       uint64_t state;
                                                       notify_get_state(_notifyTokenForDidChangeDisplayStatus, &state);

                                                       strongSelf.displayOn = (BOOL)state;
                                                   }
                                               });
    if (result != NOTIFY_STATUS_OK)
    {
        self.registeredForDarwinNotifications = NO;
        return;
    }

    self.registeredForDarwinNotifications = YES;
}

- (void)unregisterFromSomeNotifications
{
    //
    // Display notifications
    //

    uint32_t result = notify_cancel(_notifyTokenForDidChangeDisplayStatus);
    if (result == NOTIFY_STATUS_OK)
    {
        self.registeredForDarwinNotifications = NO;
    }
}

For your particular use case checking the screen brightness can be useful.

var isScreenLocked: Bool {
    return UIScreen.main.brightness == 0.0
}

I got a solution for check lock button was press or put app in background mode.

App cycle for lock press and put app in background mode-

WHEN LOCK PRESS

applicationWillResignActive

applicationDidEnterBackground

WHEN UNLOCK PRESS

applicationWillEnterForeground

applicationDidBecomeActive

///////////////////// WHEN PUT IN BACKGROUND

applicationWillResignActive

applicationDidEnterBackground

WHEN FORGROUND

applicationWillEnterForeground

applicationDidBecomeActive

you can observe in both scenario same method are calling. In this case this is difficult task to get pressed lock button or put app in background. A small hack is there when we press lock button

applicationWillResignActive

applicationDidEnterBackground

these methods will called immediately but when we put app in background there is a time interval of millisecond between both methods. we can get the time difference and put condition on it. like....

var dateResignActive : Date?
var dateAppDidBack : Date?
func applicationWillResignActive(_ application: UIApplication) {

    dateResignActive = Date()

}
func applicationDidEnterBackground(_ application: UIApplication) {

    dateAppDidBack = Date()

}
func applicationDidBecomeActive(_ application: UIApplication) {

    let el1 = getCurrentMillis(date: dateResignActive!)
    let el2 = getCurrentMillis(date: dateAppDidBack!)
    let diff = el2 - el1

    if diff < 10 { //// device was locked // 10 is aprox
        // device was locked

    }
    else {
        let elapsed = Int(Date().timeIntervalSince(date!))
        if elapsed > 15 { // put app in background
        }
    }

}
func getCurrentMillis(date : Date)->Int64 {
    return Int64(date.timeIntervalSince1970 * 1000)
}

**This code is tested in iPhone X(Notch) and iPhone 6(Home button device). Because notch device and home button device have small difference in above two method calling.**  

Jimmy provided a great solution but it's safer to pass in (__bridge const void *)(self) as an observer.

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                (__bridge const void *)(self),
                                displayStatusChanged,
                                CFSTR("com.apple.springboard.lockcomplete"),
                                NULL,
                                CFNotificationSuspensionBehaviorDeliverImmediately);

This allows you to properly remove the observer.

On a device that uses content protection, protected files are stored in an encrypted form and made available only at certain times, usually when the device is unlocked. This notification lets your app know that the device is about to be locked and that any protected files it is currently accessing might become unavailable shortly.

You can subscribe to notifications for applicationProtectedDataWillBecomeUnavailable which is very likely triggered when user has just locked their iPhone.

I have never done it myself, but it's just an alternate solution...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM