web-dev-qa-db-fra.com

Comment obtenir une mise à jour d'emplacement d'arrière-plan toutes les n minutes dans mon application iOS?

Je cherche un moyen d'obtenir une mise à jour de l'emplacement d'arrière-plan toutes les n minutes dans mon application iOS. J'utilise iOS 4.3 et la solution devrait fonctionner pour les iPhones non jailbreakés.

J'ai essayé/envisagé les options suivantes:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: Cela fonctionne en arrière-plan comme prévu, en fonction des propriétés configurées, mais il ne semble pas possible de le forcer à mettre à jour l'emplacement toutes les n minutes.
  • NSTimer: Fonctionne lorsque l'application est exécutée au premier plan mais ne semble pas être conçue pour les tâches en arrière-plan
  • Notifications locales: les notifications locales peuvent être programmées toutes les n minutes, mais il n'est pas possible d'exécuter du code pour obtenir l'emplacement actuel (sans que l'utilisateur n'ait à lancer l'application via la notification). Cette approche ne semble pas non plus être propre, car les notifications ne doivent pas être utilisées.
  • UIApplication:beginBackgroundTaskWithExpirationHandler: Autant que je sache, cela devrait être utilisé pour terminer certains travaux en arrière-plan (également limité dans le temps) lorsqu'une application est déplacée en arrière-plan plutôt que de mettre en œuvre des processus d'arrière-plan "longs".

Comment puis-je implémenter ces mises à jour régulières d'emplacement d'arrière-plan?

201
wjans

J'ai trouvé une solution pour l'implémenter à l'aide des Apple Forums du développeur:

  • Spécifiez location background mode
  • Créez une NSTimer en arrière-plan avec UIApplication:beginBackgroundTaskWithExpirationHandler:
  • Quand n est plus petit que UIApplication:backgroundTimeRemaining cela fonctionnera très bien. Lorsque n est plus grand, le location manager doit être activé (et désactivé) à nouveau avant qu'il ne reste plus de temps pour éviter que la tâche en arrière-plan ne soit supprimée.

Ceci fonctionne car emplacement est l'un des trois types d'exécution en arrière-plan autorisés .

Remarque: j'ai perdu un peu de temps en testant ceci dans le simulateur où cela ne fonctionne pas. Cependant, cela fonctionne très bien sur mon téléphone.

114
wjans

Sur iOS 8/9/1 pour mettre à jour l'emplacement de l'arrière-plan toutes les 5 minutes, procédez comme suit:

  1. Allez dans Projet -> Capacités -> Modes d’arrière-plan -> sélectionnez Mises à jour de lieu

  2. Allez dans Projet -> Info -> ajoutez une clé NSLocationAlwaysUsageDescription avec une valeur vide (ou éventuellement n'importe quel texte)

  3. Pour que la localisation fonctionne lorsque votre application est en arrière-plan et envoyez les coordonnées au service Web ou faites quoi que ce soit avec elles toutes les 5 minutes, implémentez-les comme indiqué dans le code ci-dessous.

Je n'utilise pas de tâches d'arrière-plan ou de minuteries. J'ai testé ce code avec mon appareil avec iOS 8.1 qui était resté sur mon bureau pendant quelques heures avec mon application en arrière-plan. L'appareil était verrouillé et le code fonctionnait correctement tout le temps.

@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@"Location services are disabled in settings.");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@"Sending current location to web service.");
    }
}

@end
53
Leszek Szary

Je l'ai fait dans une application que je développe. Les minuteries ne fonctionnent pas lorsque l'application est en arrière-plan, mais l'application reçoit en permanence les mises à jour d'emplacement. J'ai lu quelque part dans la documentation (je n'arrive pas à le trouver maintenant, je posterai une mise à jour quand je le ferai) qu'une méthode peut être appelée uniquement sur une boucle d'exécution active lorsque l'application est en arrière-plan. Le délégué de l'application a une boucle d'exécution active même dans le bg, vous n'avez donc pas besoin de créer votre propre boucle pour que cela fonctionne. [Je ne suis pas sûr que ce soit l'explication correcte mais c'est comme ça que j'ai compris ce que j'ai lu]

Tout d'abord, ajoutez l'objet location pour la clé UIBackgroundModes dans le fichier info.plist de votre application. Désormais, ce que vous devez faire, c'est lancer les mises à jour d'emplacement n'importe où dans votre application:

    CLLocationManager locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location
    [locationManager startUpdatingLocation];

Ensuite, écrivez une méthode pour gérer les mises à jour d'emplacement, par exemple -(void)didUpdateToLocation:(CLLocation*)location, dans le délégué de l'application. Puis implémentez la méthode locationManager:didUpdateLocation:fromLocation de CLLocationManagerDelegate dans la classe dans laquelle vous avez démarré le gestionnaire de sites (puisque nous avons défini le délégué du gestionnaire de sites sur 'self'). Dans cette méthode, vous devez vérifier si l'intervalle de temps après lequel vous devez gérer les mises à jour d'emplacement s'est écoulé. Vous pouvez le faire en sauvegardant l'heure actuelle à chaque fois. Si ce temps s'est écoulé, appelez la méthode UpdateLocation à partir du délégué de votre application:

NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@"New Location: %@", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}

Cela appellera votre méthode toutes les 5 minutes, même lorsque votre application est en arrière-plan. Imp: Cette implémentation décharge la batterie. Si la précision de vos données de localisation n'est pas critique, vous devez utiliser [locationManager startMonitoringSignificantLocationChanges].

Avant d’ajouter cela à votre application, veuillez lire le Guide de programmation de la détection d’emplacement

35
Bushra Shahid

Maintenant que iOS6 est le meilleur moyen d'avoir un service de localisation permanent ...

- (void)applicationWillResignActive:(UIApplication *)application
{
/*
 Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
 Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
 */

NSLog(@"to background");

app.isInBackground = TRUE;

UIApplication *app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{

        if (bgTask != UIBackgroundTaskInvalid)
        {
            [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.

    locationManager.distanceFilter = 100;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];

    NSLog(@"App staus: applicationDidEnterBackground");
    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
});

NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]);

}

Juste testé comme ça:

J'ai démarré l'application, aller en arrière-plan et me déplacer dans la voiture de quelques minutes. Ensuite, je rentre chez moi pendant 1 heure et recommence à bouger (sans ouvrir à nouveau l'application). Les emplacements ont recommencé. Puis arrêté pendant deux heures et recommencé. Tout va bien à nouveau ...

N'OUBLIEZ PAS D'UTILISER LES NOUVEAUX SERVICES DE LOCALISATION DANS iOS6

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{   
    CLLocation *loc = [locations lastObject];

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}
24
Alejandro Luengo

Pour quelqu'un d'autre ayant un cauchemar, essayez de trouver celui-ci. J'ai une solution simple.

  1. regardez cet exemple de raywenderlich.com -> ayez un exemple de code, cela fonctionne parfaitement, mais malheureusement, aucune minuterie n’est détectée à l’arrière-plan. cela fonctionnera indéfiniment.
  2. Ajouter une minuterie en utilisant:

    -(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];
    
    }
    
  3. N'oubliez pas d'ajouter "Registres d'applications pour les mises à jour de localisation" dans info.plist.

13
HelmiB

Voici ce que j'utilise:

import Foundation
import CoreLocation
import UIKit

class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {

    static let instance = BackgroundLocationManager()
    static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
    static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server

    let locationManager = CLLocationManager()
    var timer:NSTimer?
    var currentBgTaskId : UIBackgroundTaskIdentifier?
    var lastLocationDate : NSDate = NSDate()

    private override init(){
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.activityType = .Other;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        if #available(iOS 9, *){
            locationManager.allowsBackgroundLocationUpdates = true
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    func applicationEnterBackground(){
        FileLogger.log("applicationEnterBackground")
        start()
    }

    func start(){
        if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        } else {
                locationManager.requestAlwaysAuthorization()
        }
    }
    func restart (){
        timer?.invalidate()
        timer = nil
        start()
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case CLAuthorizationStatus.Restricted:
            //log("Restricted Access to location")
        case CLAuthorizationStatus.Denied:
            //log("User denied access to location")
        case CLAuthorizationStatus.NotDetermined:
            //log("Status not determined")
        default:
            //log("startUpdatintLocation")
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if(timer==nil){
            // The locations array is sorted in chronologically ascending order, so the
            // last element is the most recent
            guard let location = locations.last else {return}

            beginNewBackgroundTask()
            locationManager.stopUpdatingLocation()
            let now = NSDate()
            if(isItTime(now)){
                //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
            }
        }
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        CrashReporter.recordError(error)

        beginNewBackgroundTask()
        locationManager.stopUpdatingLocation()
    }

    func isItTime(now:NSDate) -> Bool {
        let timePast = now.timeIntervalSinceDate(lastLocationDate)
        let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
        return intervalExceeded;
    }

    func sendLocationToServer(location:CLLocation, now:NSDate){
        //TODO
    }

    func beginNewBackgroundTask(){
        var previousTaskId = currentBgTaskId;
        currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
            FileLogger.log("task expired: ")
        })
        if let taskId = previousTaskId{
            UIApplication.sharedApplication().endBackgroundTask(taskId)
            previousTaskId = UIBackgroundTaskInvalid
        }

        timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
    }
}

Je lance le suivi dans AppDelegate comme ça:

BackgroundLocationManager.instance.start()
7
hmitkov

Malheureusement, toutes vos hypothèses semblent correctes et je ne pense pas qu'il y ait moyen de le faire. Afin de sauver la vie de la batterie, les services de localisation de l'iPhone sont basés sur le mouvement. Si le téléphone se trouve au même endroit, il est invisible pour les services de localisation.

La CLLocationManager appellera uniquement locationManager:didUpdateToLocation:fromLocation: lorsque le téléphone recevra une mise à jour de la localisation, ce qui ne se produira que si l'un des trois services de localisation (tour cellulaire, gps, wifi) perçoit un changement.

Quelques autres choses qui pourraient aider à éclairer d'autres solutions:

  • Le démarrage et l’arrêt des services entraînent l’appel de la méthode déléguée didUpdateToLocation, mais il se peut que le newLocation ait un horodatage ancien.

  • La surveillance régionale peut aider

  • Lors de l'exécution en tâche de fond, sachez qu'il peut être difficile d'obtenir le support "complet" de LocationServices approuvé par Apple. D'après ce que j'ai vu, ils ont spécialement conçu startMonitoringSignificantLocationChanges comme solution de remplacement à faible consommation d'énergie pour les applications nécessitant une prise en charge d'emplacement d'arrière-plan, et encouragent vivement les développeurs à l'utiliser sauf si elle en a absolument besoin.

Bonne chance!

MISE À JOUR: Ces idées sont peut-être obsolètes maintenant. On dirait que les gens ont du succès avec la réponse @wjans ci-dessus.

6
Chazbot

J'ai écrit une application en utilisant les services de localisation, l'application doit envoyer la localisation toutes les 10 secondes. Et cela a très bien fonctionné.

Utilisez simplement la méthode " allowDeferredLocationUpdatesUntilTraveled: timeout ", conformément au document d'Apple.

Ce que j'ai fait sont:

Obligatoire: Enregistrez le mode d'arrière-plan pour la mise à jour de l'emplacement.

1. Créez LocationManger et startUpdatingLocation, avec accuracy et filteredDistance comme vous le souhaitez:

-(void) initLocationManager    
{
    // Create the manager object
    self.locationManager = [[[CLLocationManager alloc] init] autorelease];
    _locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    _locationManager.desiredAccuracy = 45;
    _locationManager.distanceFilter = 100;
    // Once configured, the location manager must be "started".
    [_locationManager startUpdatingLocation];
}

2. Pour que l'application soit exécutée indéfiniment à l'aide de la méthode allowDeferredLocationUpdatesUntilTraveled:timeout en arrière-plan, vous devez redémarrer updatingLocation avec le nouveau paramètre lorsque l'application passe à fond, comme ceci:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3. L'application obtient des mises à jour comme d'habitude avec locationManager:didUpdateLocations: callback:

-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//  store data
    CLLocation *newLocation = [locations lastObject];
    self.userLocation = newLocation;

   //tell the centralManager that you want to deferred this updatedLocation
    if (_isBackgroundMode && !_deferringUpdates)
    {
        _deferringUpdates = YES;
        [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
    }
}

4. Mais vous devez gérer les données dans le callback alors locationManager:didFinishDeferredUpdatesWithError: pour votre usage

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}

5. REMARQUE: Je pense que nous devrions réinitialiser les paramètres de LocationManager à chaque fois que l'application bascule entre les modes fond/arrière-plan.

5
samthui7
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

Cela est nécessaire pour le suivi de l'emplacement en arrière-plan depuis iOS 9.

5
Nilesh

J'ai utilisé la méthode de xs2bush pour obtenir un intervalle (en utilisant timeIntervalSinceDate) et l'ai développé un peu. Je voulais m'assurer que je obtenais la précision requise et que je ne manquais pas de batterie en laissant la radio GPS allumée plus que nécessaire.

Je garde l'emplacement en cours d'exécution en permanence avec les paramètres suivants:

locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locationManager.distanceFilter = 5;

il s’agit d’une consommation relativement faible de la batterie. Lorsque je suis prêt à obtenir ma prochaine lecture périodique d'emplacement, je vérifie d'abord si l'emplacement correspond à la précision souhaitée. Si c'est le cas, j'utilise ensuite l'emplacement. Si ce n'est pas le cas, j'augmente la précision avec ceci:

locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter = 0;

obtenir ma position et puis une fois que j'ai la position, je redéfinis la précision pour minimiser la consommation de batterie. J'ai écrit un exemple complet de travail et j'ai aussi écrit la source du code côté serveur pour collecter les données de localisation, les stocker dans une base de données et permettre aux utilisateurs d'afficher les données gps en temps réel ou de récupérer et d'afficher les itinéraires précédemment stockés. J'ai des clients pour iOS, Android, Windows Phone et Java moi. Tous les clients sont écrits de manière native et fonctionnent correctement en arrière-plan. Le projet est MIT sous licence.

Le projet iOS est ciblé pour iOS 6 à l'aide d'un SDK de base iOS 7. Vous pouvez obtenir le code ici .

Merci de signaler un problème sur github si vous constatez des problèmes. Merci.

4
nickfox

Il semble que stopUpdatingLocation déclenche le minuteur de surveillance en arrière-plan. Je l'ai donc remplacé dans didUpdateLocation par:

     [self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
     [self.locationManager setDistanceFilter:99999];

qui semble éteindre efficacement le GPS. Le sélecteur pour l'arrière-plan NSTimer devient alors:

- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}

Tout ce que je fais, c'est d'inverser périodiquement la précision pour obtenir une coordonnée très précise toutes les quelques minutes et parce que le gestionnaire de localisation n'a pas été arrêté, backgroundTimeRemaining reste à sa valeur maximale. Cette consommation de batterie a été réduite d’environ 10% par heure (avec une constante kCLLocationAccuracyBest en arrière-plan) à environ 2% par heure sur mon appareil.

2
Amit Shelgaonkar

Il existe un cocoapod APScheduledLocationManager qui permet d'obtenir des mises à jour d'emplacement d'arrière-plan toutes les n secondes avec la précision d'emplacement souhaitée.

let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)

Le référentiel contient également un exemple d'application écrit en Swift 3.

2
sash

Dans iOS 9 et watchOS 2.0, une nouvelle méthode sur CLLocationManager vous permet de demander l'emplacement actuel: CLLocationManager: requestLocation (). Cela se termine immédiatement, puis renvoie l'emplacement au délégué CLLocationManager.

Vous pouvez utiliser un NSTimer pour demander un emplacement toutes les minutes avec cette méthode maintenant et vous ne devez pas travailler avec les méthodes startUpdatingLocation et stopUpdatingLocation.

Toutefois, si vous souhaitez capturer des emplacements en fonction d'une modification de X mètres par rapport au dernier emplacement, il suffit de définir la propriété distanceFilter de CLLocationManger et d'appeler X startUpdatingLocation ().

0
Malcolm

Code de travail (code pas à pas complet)

étape 1

  • Aller au projet -> Capacités -> Modes de fond -> sélectionnez Mises à jour de lieu.
  • Allez dans Projet -> Info -> ajoutez une clé NSLocationAlwaysUsageDescription avec une chaîne facultative.

étape 2

Ajouter ce code à AppDelegate.m

@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end

Étape Ajoutez ce code dans la méthode d'applicationDidEnterBackground dans 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
}
0
Hari R Krishna