Получение местоположения пользователя каждые n минут после того, как приложение переходит на задний план
Я пытаюсь реализовать предложения, приведенные в этом сообщении.
К сожалению, шаги мне не понятны. Я попытался выполнить эти предложения, но backgroundTimeRemaining продолжает уменьшаться даже после того, как я запускаю и останавливаю locationServices. Вот как я его разработал:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
self.timer = nil;
[self initTimer];
});
}
initTimer:
- (void)initTimer {
// Create the location manager if this object does not
// already have one.
if (nil == self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:@selector(checkUpdates:)
userInfo:nil
repeats:YES];
}
}
checkUpdates:
- (void)checkUpdates:(NSTimer *)timer{
UIApplication* app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0) {
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
DbgLog(@"Reminaing %f", app.backgroundTimeRemaining);
}
Есть ли у кого-нибудь предложение о том, что может быть неправильным в моем коде? Вызываются как initTimer, так и checkUpdates, но только в течение времени выполнения фона (+ - 10 минут). Я хочу, чтобы приложение обновляло местоположение каждые n минут "навсегда".
Мое приложение UIBackgroundModes установлено в местоположение.
UPDATE:
Теперь я перезаписываю таймер на файле doUpdateToLocation и didFailWithError. Но все же backgroundTimeRemaining продолжает уменьшаться:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
[self initTimer];
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
[self.locationManager stopUpdatingLocation];
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
[self initTimer];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
});
}
Я также аннулирую таймер:
- (void)checkUpdates:(NSTimer *)timer{
UIApplication* app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0 && remaining > 570.0) {
[self.timer invalidate];
self.timer = nil;
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
}
DbgLog(@"*************************Checking for updates!!!!!!!!!!! Reminaing %f", app.backgroundTimeRemaining);
}
Ответы
Ответ 1
Через несколько дней, пробовав все возможные решения, я наконец смог заставить его работать. Вот что было не так в моем решении:
- Мне нужно настроить 2 UIBackgroundTaskIdentifiers, один для таймера и еще один для locationManager
В моем решении просто добавьте:
UIApplication *app = [UIApplication sharedApplication];
bgTask2 = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask2];
bgTask2 = UIBackgroundTaskInvalid; }];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
Ответ 2
Для кого-то, у кого есть кошмар времени, пытаясь понять это, у меня есть простое решение.
- Изучите пример из raywenderlich.com. Пример кода работает отлично, но, к сожалению, нет таймера во время фонового местоположения. Это будет работать бесконечно.
-
Добавьте таймер с помощью этого фрагмента кода:
-(void)applicationDidEnterBackground {
[self.locationManager stopUpdatingLocation];
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate
target:self.locationManager
selector:@selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
-
Просто не забудьте добавить "Регистры приложений для обновлений местоположения" в info.plist.
Ответ 3
Как только вы окажетесь в фоновом режиме, у вас будет только 10 минут дополнительного времени. Это невозможно. Вместо этого вы должны использовать фоновые службы местоположения.
Ответ 4
Рабочий код (весь пошаговый код)
Измененный код scurioni
Шаг 1
- Перейти к проекту → Возможности → Режимы фона → выберите Обновления местоположения.
- Перейдите в Project → Info → добавьте ключ NSLocationAlwaysUsageDescription с необязательной строкой.
Шаг 2
Добавьте этот код в AppDelegate.m
@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end
Шаг 3
Добавьте этот код в метод applicationDidEnterBackground в AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTaskId =
[app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTaskId];
bgTaskId = UIBackgroundTaskInvalid;
}];
dispatch_async( dispatch_get_main_queue(), ^{
self.timer = nil;
[self initTimer];
[app endBackgroundTask:bgTaskId];
bgTaskId = UIBackgroundTaskInvalid;
});
}
- (void)initTimer {
if (nil == self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager requestAlwaysAuthorization];
[self.locationManager startMonitoringSignificantLocationChanges];
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:@selector(checkUpdates:)
userInfo:nil
repeats:YES];
}
}
- (void)checkUpdates:(NSTimer *)timer{
UIApplication *app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0) {
[self.locationManager startUpdatingLocation];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
[self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask =
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self initTimer];
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
[self.locationManager stopUpdatingLocation];
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask =
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[self initTimer];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task
});
}
-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}