209 votes

Comment obtenir une mise à jour de la localisation en arrière-plan toutes les n minutes dans mon application iOS ?

Je cherche un moyen d'obtenir une mise à jour de la localisation en 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 cassés.

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

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges : Cela fonctionne en arrière-plan comme prévu, sur la base des propriétés configurées, mais il semble impossible de le forcer à mettre à jour l'emplacement toutes les n minutes .
  • NSTimer : Fonctionne lorsque l'appli fonctionne au premier plan mais ne semble pas être conçue pour les tâches d'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 la localisation actuelle (sans que l'utilisateur doive lancer l'application via la notification). Cette approche ne semble pas non plus être une approche propre car ce n'est pas ce à quoi les notifications devraient servir.
  • UIApplication:beginBackgroundTaskWithExpirationHandler : D'après ce que je comprends, cela devrait être utilisé pour terminer certains travaux en arrière-plan (également limités 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 "à long terme".

Comment puis-je mettre en œuvre ces mises à jour régulières de la localisation en arrière-plan ?

0 votes

0 votes

1 votes

Si vous essayez de le faire fonctionner sous iOS 7, vous pouvez essayer cette solution ici : stackoverflow.com/questions/18946881/ Si vous avez des questions, nous vous invitons à nous rejoindre pour une discussion ici : mobileoop.com/background-location-update-programming-for-ios-7

116voto

wjans Points 5141

J'ai trouvé une solution pour mettre cela en œuvre avec l'aide des Forums des développeurs Apple :

  • Précisez location background mode
  • Créer un NSTimer en arrière-plan avec UIApplication:beginBackgroundTaskWithExpirationHandler:
  • Lorsque n es plus petit que UIApplication:backgroundTimeRemaining il fonctionnera très bien. Quand n es plus grand le location manager doit être réactivé (et désactivé) avant qu'il ne reste plus de temps pour éviter que la tâche d'arrière-plan ne soit tuée.

Cela fonctionne parce que la localisation est l'un des trois types d'exécution d'arrière-plan autorisés. .

Note : J'ai perdu du temps en testant ceci dans le simulateur où cela ne fonctionne pas. Cependant, cela fonctionne bien sur mon téléphone.

24 votes

Auriez-vous par hasard le lien vers le forum ? Je cherche à mettre en œuvre le même type de cartographie de localisation et je n'ai pas été en mesure de le faire fonctionner. Ou un exemple de code serait très apprécié.

4 votes

Pouvez-vous s'il vous plaît expliquer pourquoi la tâche d'arrière-plan n'est pas tuée après 10 minutes (temps maximum autorisé) si vous arrêtez et démarrez le gestionnaire d'emplacement ? Est-ce une sorte de fonctionnalité prévue ? cela ressemble plus à un bug dans le SDK d'Apple si cela se produit comme ça. Sur quelle version d'iOS l'avez-vous essayé ?

0 votes

@cwieland : Le post du forum ne mentionne pas d'autres informations mais toutes les "étapes" individuelles sont clairement expliquées dans les docs d'Apple. Si vous rencontrez des problèmes spécifiques, je vous suggère de poster votre propre question.

34voto

xs2bush Points 2891

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

Tout d'abord, ajoutez le location pour la clé UIBackgroundModes dans le fichier info.plist de votre application. Il ne vous reste plus qu'à lancer les mises à jour de la localisation 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 de l'emplacement, disons -(void)didUpdateToLocation:(CLLocation*)location dans le délégué de l'application. Ensuite, implémentez la méthode locationManager:didUpdateLocation:fromLocation de CLLocationManagerDelegate dans la classe dans laquelle vous avez lancé le gestionnaire d'emplacement (puisque nous avons défini le délégué du gestionnaire d'emplacement 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 de l'emplacement s'est écoulé. Vous pouvez le faire en sauvegardant l'heure actuelle à chaque fois. Si ce délai 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 si votre application est en arrière-plan. Imp : Cette implémentation draine la batterie, si la précision de vos données de localisation n'est pas critique, vous devriez utiliser la méthode suivante [locationManager startMonitoringSignificantLocationChanges]

Avant d'ajouter cette fonction à votre application, veuillez lire le document Guide de programmation de la géolocalisation

4 votes

De cette façon, les services de localisation sont constamment activés (ce qui épuise la batterie), ce que je ne veux pas. Je veux activer le service de localisation toutes les n minutes et le désactiver immédiatement lorsque j'ai une bonne solution (je viens de remarquer que je n'ai pas expliqué cela clairement dans ma question). Je peux obtenir ce comportement dans la solution que j'ai décrite.

3 votes

Vous pouvez régler la précision du gestionnaire de localisation à 1 km, ce qui laisse votre batterie pratiquement intacte. Après 5 minutes, réglez la précision sur 1m. Lorsque vous obtenez une localisation satisfaisante (normalement après 5s), réglez à nouveau la précision sur 1km.

0 votes

Knagode, j'ai essayé la solution que vous avez suggérée pour le problème d'épuisement de la batterie, mais même après avoir augmenté la précision après N minutes, la méthode locationManager : didUpdateLocations n'est pas appelée à nouveau. J'ai essayé startUpdating et stopUpdating, au lieu d'augmenter et de diminuer la précision, il est appelé avec succès délégué locationManager : didUpdateLocations méthode, après N minutes, mais ne fonctionne pas en arrière-plan MODE ...

24voto

Alejandro Luengo Points 433

Maintenant que l'iOS6 est sorti, la meilleure façon d'avoir un service de localisation qui fonctionne toujours est...

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

}

Je l'ai juste testé comme ça :

J'ai lancé l'application, j'ai fait l'arrière-plan et j'ai bougé dans la voiture pendant quelques minutes. Puis je rentre chez moi pendant 1 heure et je recommence à bouger (sans rouvrir l'application). Les emplacements ont recommencé. Puis je m'arrête pendant deux heures et je recommence. 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;
}

1 votes

Si l'application se plante ou est tuée, le système ne redémarre pas, n'est-ce pas ?

1 votes

Pour un code plus lisible, vous pouvez faire un [locations lastObject] ; au lieu de [locations objectAtIndex :[locations count] - 1].

0 votes

Votre méthode ne fonctionne qu'avec ios6 ?

13voto

HelmiB Points 3420

A quelqu'un d'autre qui doit faire des cauchemars pour comprendre ça. J'ai une solution simple.

  1. Regardez cet exemple de raywenderlich.com -> j'ai un exemple de code, cela fonctionne parfaitement, mais malheureusement pas de minuterie pendant la localisation en arrière-plan. cela fonctionnera indéfiniment.

  2. Ajoutez 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 "App registers for location updates" dans info.plist.

0 votes

Cela permet-il d'obtenir l'emplacement pendant plus de 3 minutes ?

0 votes

Il faut définir l'emplacement dans Capacités -> Mode arrière-plan.

0 votes

Ça ne marche pas ! ?j'ai vérifié sur iOS 8 et iOS 10

6voto

Chazbot Points 1492

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

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

Quelques autres éléments qui pourraient aider à trouver d'autres solutions :

  • Le démarrage et l'arrêt des services provoquent le didUpdateToLocation à appeler, mais la méthode de délégué newLocation pourrait avoir un ancien horodatage.

  • Le suivi des régions peut être utile

  • Lorsque vous exécutez le logiciel en arrière-plan, sachez qu'il peut être difficile de faire approuver par Apple la prise en charge "complète" des LocationServices. D'après ce que j'ai vu, ils ont spécifiquement conçu les services LocationServices de façon à ce qu'ils puissent être utilisés par les utilisateurs. startMonitoringSignificantLocationChanges comme une alternative à faible consommation d'énergie pour les applications qui ont besoin d'un support de localisation en arrière-plan, et nous encourageons vivement les développeurs à l'utiliser, sauf si l'application en a absolument besoin.

Bonne chance !

MISE À JOUR : Ces réflexions sont peut-être déjà dépassées. Il semble que les gens ont du succès avec la réponse de @wjans, ci-dessus.

1 votes

Il existe des applications disponibles dans l'AppStore (comme par exemple "My Locus") qui permettent d'obtenir une mise à jour de la localisation en arrière-plan. Elles ne maintiennent pas le service de localisation actif, mais l'activent brièvement selon l'intervalle défini. Comment font-ils ?

2 votes

Dans la situation que vous décrivez, l'application utilise très probablement l'approche startMonitoringSignificantLocationChanges. Dans ce cas, le téléphone est temporairement "réveillé" lorsqu'il reçoit une mise à jour de l'emplacement, mais il n'y a pas d'intervalle que vous pouvez définir pour "appeler" ce service en arrière-plan. Lorsque le téléphone se déplace (ou passe du cellulaire au GPS ou au Wifi), il déclenche une mise à jour. La conférence iTunes U de Stanford sur ce sujet m'a été très utile. J'espère qu'elle pourra vous aider à trouver une solution de contournement : itunes.apple.com/us/itunes-u/iphone-application-development/

2 votes

Merci pour l'indication. Cependant, je ne comprends toujours pas ce que fait l'application. Même si mon téléphone est posé sur mon bureau, sans bouger du tout, je vois le service de localisation se déclencher toutes les 10 minutes (exactement). Si je comprends bien, startMonitoringSignificantLocationChanges ne donnerait pas de nouvelles dans ce cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X