iOS 使用 CoreLocation 进行地理围栏

iOS Geofencing with CoreLocation

我想知道用户何时在我家附近。 我正在使用 CoreLocation,并且希望始终知道用户是否在我家附近(大约 100m),即使应用已关闭。

为此,我在我的代码中成功地知道用户是否在 100 米左右(CLRegionStateInside)。 DidEnterRegionDidExitRegion 也可以正常工作。我正在使用 kCLAuthorizationStatusAuthorizedAlways 来本地化用户。

我想节省电池寿命,所以我添加了 self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; 以在用户移动 3 公里后更新位置。

告诉我如何才能节省更多电池寿命,以及如何在应用程序处于 background/closed 时更新位置(也许我必须更改我的 AppDelegate 中的某些内容)?

我想知道我的代码是否能满足我的需要?

这是我的代码:

#import "ViewController.h"
@import UserNotifications;

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    // Request Notification
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                              if (!error) {
                                  NSLog(@"request authorization succeeded!");
                              }
                          }];
}

- (void)setUpGeofences {
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake(49.451096,
                                                               1.095425);
    CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center
                                                        radius:100.0
                                                    identifier:@"Home"];
    [self.locationManager startMonitoringForRegion:region];
    self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    self.locationManager.allowsBackgroundLocationUpdates = YES;
}

- (void)showSorryAlert {
    UIAlertController *alert =   [UIAlertController
                                  alertControllerWithTitle:@"Info"
                                  message:@"You are using UIAlertController"
                                  preferredStyle:UIAlertControllerStyleAlert];
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    switch (status) {
        case kCLAuthorizationStatusNotDetermined:
            [self.locationManager requestAlwaysAuthorization];
            break;
        case kCLAuthorizationStatusAuthorizedWhenInUse:
            [self.locationManager startUpdatingLocation];
            [self setUpGeofences];
            break;
        case kCLAuthorizationStatusAuthorizedAlways:
            [self.locationManager startUpdatingLocation];
            [self setUpGeofences];
            break;
        case kCLAuthorizationStatusRestricted:
            // restricted by e.g. parental controls. User can't enable Location Services
            break;
        case kCLAuthorizationStatusDenied:
            // user denied your app access to Location Services, but can grant access from Settings.app
            break;
        default:
            break;
    }
}

- (void)locationManager:(CLLocationManager *)manager
         didEnterRegion:(CLRegion *)region {
    NSLog(@"didEnter : %@", region);
    [self displayNotif:@"Bienvenue !" withBody:@"Passez nous voir !"];
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    [self displayNotif:@"Au revoir !" withBody:@"A bientôt !"];
}

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
    NSLog(@"Start monitoring for region: %@", region.identifier);
    [self.locationManager requestStateForRegion:region];
}

- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    NSLog(@"Error: %@", error);
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateLocations:(NSArray *)locations {
    NSLog(@"NEW LOCATION");
    // Stop location updates when they aren't needed anymore
    [self.locationManager stopUpdatingLocation];

    // Disable background location updates when they aren't needed anymore
    self.locationManager.allowsBackgroundLocationUpdates = NO;
}

- (void)locationManager:(CLLocationManager *)manager
      didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
    // When regions are initialized, see if we are already within the geofence.
    switch (state) {
        case CLRegionStateInside: [self displayNotif:@"Bienvenue" withBody:@"Passez nous voir !"];
            break;
        case CLRegionStateUnknown: NSLog(@"Unknown");
        case CLRegionStateOutside: NSLog(@"Outside");
        default: break;
    }
}

- (void)displayNotif:(NSString *)title withBody:(NSString *)body {
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
    content.body = [NSString localizedUserNotificationStringForKey:body
                                                         arguments:nil];
    content.sound = [UNNotificationSound defaultSound];

    /// 4. update application icon badge number
    content.badge = @([[UIApplication sharedApplication] applicationIconBadgeNumber] + 1);
    // Deliver the notification in five seconds.
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger
                                                  triggerWithTimeInterval:1.f repeats:NO];
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"OneSecond"
                                                                          content:content trigger:trigger];
    /// 3. schedule localNotification
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (!error) {
            NSLog(@"add NotificationRequest succeeded!");
        }
    }];
}


@end

关于后台监控的问题,请参考苹果的文档,说明如下:

If a region boundary is crossed while an app isn’t running, that app is relaunched into the background to handle the event. Similarly, if the app is suspended when the event occurs, it’s woken up and given a short amount of time (around 10 seconds) to handle the event.

这意味着,只要您的 authorizationStatus 设置为 .authorizedAlways,其余的将自动处理。

link 到其余文档:https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/RegionMonitoring/RegionMonitoring.html#//apple_ref/doc/uid/TP40009497-CH9-SW1