Я пытаюсь создать приложение, которое может запускаться iBeacon для пробуждения (от момента его увольнения/приостановления/завершения) для записи второй по времени информации GPS. Затем запись GPS должна прекратиться, когда телефон выходит за пределы диапазона маяка. Я успешно получил приложение, чтобы узнать методы didEnterRegion и didExitRegion, когда он входит и выходит за пределы iBeacon. В методе didEnterRegion я хочу в основном сказать что-то вроде [locationManager startUpdatingLocation]
, чтобы я мог начать отслеживать местоположение пользователя. Однако, когда я пытаюсь добавить эту строку кода, обновления местоположения останавливаются примерно через 10 секунд.Обновление фонового местоположения iOS 8, вызванное iBeacon
Позже я нашел article об обновлении местоположения фона, которые пришли с этим Github project. Я добавил файлы BackgroundTaskManager, LocationShareModel и LocationTracker в свой проект. В принципе, идея этого решения заключается в том, чтобы постоянно перезапускать диспетчер местоположений, чтобы у него не было возможности для истечения фоновой задачи и прекращения отправки обновлений. Однако даже с этим решением я получаю обновления местоположения чуть более 3 минут.
У меня есть опции «Обновления местоположения» и «Использовать аксессуары Bluetooth LE». «Фоновая выборка» (Refresh Background Refresh) не включена в соответствии с этой цитатой от Apple: «В iOS 8 и более поздних версиях отключить настройку обновления фонового приложения для текущего приложения или для всех приложений не помешает доставка местоположения события в фоновом режиме ". Мое приложение запрашивает разрешение «Всегда» для обновления местоположения.
Я не могу понять, как решить эту проблему, несмотря на обзор, казалось бы, бесконечных статей и учебников StackOverflow. Я тестирую его на iPhone 5S под управлением iOS 8.3.0. Любое понимание было бы оценено. См. Фрагменты кода ниже.
В AppDelegate.m:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"Start recording trip";
notification.soundName = @"Default";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
self.recording = YES;
[self startAutoTrip];
}
}
- (void) startAutoTrip {
self.locationTracker = [[LocationTracker alloc]init];
[self.locationTracker startLocationTracking];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = @"Stop recording trip";
notification.soundName = @"Default";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
[self stopAutoTrip];
self.recording = NO;
}
}
- (void)stopAutoTrip {
// stop recording the locations
CLSLog(@"Trying to stop location updates");
[self.locationTracker stopLocationTracking:self.managedObjectContext];
CLSLog(@"Stop location updates");
}
В LocationTracker.m (из учебника цитированной выше, изменить интервалы времени 60 сек и 10 сек до 5 сек и 2 сек). В основном это методы startLocationTracking, didUpdateLocations и stopLocationTracking.
- (void)startLocationTracking {
NSLog(@"startLocationTracking");
if ([CLLocationManager locationServicesEnabled] == NO) {
NSLog(@"locationServicesEnabled false");
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:@"Location Services Disabled" message:@"You currently have all location services for this device disabled" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
} else {
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if(authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted){
NSLog(@"authorizationStatus failed");
} else {
NSLog(@"authorizationStatus authorized");
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = 10; //meters
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
locationManager.pausesLocationUpdatesAutomatically = NO;
if(IS_OS_8_OR_LATER) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
}
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(@"locationManager didUpdateLocations");
for(int i=0;i<locations.count;i++){
CLLocation * newLocation = [locations objectAtIndex:i];
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (fabs(howRecent) < 10.0 && newLocation.horizontalAccuracy < 20 && locations.count > 0) {
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
self.myLastLocation = theLocation;
self.myLastLocationAccuracy= theAccuracy;
CLLocationCoordinate2D coords[2];
coords[0] = ((CLLocation *)locations.lastObject).coordinate;
coords[1] = newLocation.coordinate;
[self.shareModel.myLocationArray addObject:newLocation];
}
}
//If the timer still valid, return it (Will not run the code below)
if (self.shareModel.timer) {
return;
}
self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
[self.shareModel.bgTask beginNewBackgroundTask];
//Restart the locationMaanger after 1 minute (5 sec)
self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self
selector:@selector(restartLocationUpdates)
userInfo:nil
repeats:NO];
//Will only stop the locationManager after 10 seconds, so that we can get some accurate locations
//The location manager will only operate for 10 seconds to save battery
// 2 sec
if (self.shareModel.delay10Seconds) {
[self.shareModel.delay10Seconds invalidate];
self.shareModel.delay10Seconds = nil;
}
self.shareModel.delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:2 target:self
selector:@selector(stopLocationDelayBy10Seconds)
userInfo:nil
repeats:NO];
}
- (void) restartLocationUpdates
{
NSLog(@"restartLocationUpdates");
if (self.shareModel.timer) {
[self.shareModel.timer invalidate];
self.shareModel.timer = nil;
}
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = 10; //meters
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
locationManager.pausesLocationUpdatesAutomatically = NO;
if(IS_OS_8_OR_LATER) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
- (void)stopLocationTracking:(NSManagedObjectContext *)managedObjectContext {
NSLog(@"stopLocationTracking");
CLSLog(@"stopLocationTracking");
CLSLog(@"set managedObjectContext %@", managedObjectContext);
self.managedObjectContext = managedObjectContext;
if (self.shareModel.timer) {
[self.shareModel.timer invalidate];
self.shareModel.timer = nil;
}
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
[locationManager stopUpdatingLocation];
[self saveRun];
[self sendRun];
}
_ «Я пытаюсь создать приложение, которое может быть запущено iBeacon, чтобы проснуться (от того, чтобы быть убитым/приостановлено/завершено)». К сожалению, это невозможно в iOS. Приложение должно быть запущено на переднем плане/фон, чтобы он мог общаться с ibeacons. –