iOS Swift: la function de location de recarga con NSTimer para el background de la aplicación no funciona

Tengo un problema con los services de location. No puedo configurar una function que actualice mis coorderadas de location en segundo plano mediante un NSTimer. Aquí está mi código de appDelegate:

var locationManager = CLLocationManager() func applicationDidEnterBackground(application: UIApplication) { self.locationManager.delegate = self self.locationManager.desinetworkingAccuracy = kCLLocationAccuracyBest self.theTimer = NSTimer(fireDate: NSDate(), interval: 40, target: self, selector: "handleTimer", userInfo: nil, repeats: true) NSRunLoop.currentRunLoop().addTimer(self.theTimer, forMode: NSDefaultRunLoopMode) } func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) { var locValue:CLLocationCoordinate2D = manager.location.coordinate println("dinBack = \(locValue.latitude) \(locValue.longitude)") self.locationManager.stopUpdatingLocation() } func handleTimer(){ println("started") self.locationManager.startUpdatingLocation() } 

PD. – Por supuesto que he importado corelocation. – Cuando vuelvo a la aplicación, la console imprime lo que debería haber impreso en segundo plano.

No puede hacer que un NSTimer funcione de esta manera mientras su aplicación está en segundo plano. NSTimer no son "mecanismos en time real". De la documentation oficial :

Los timeres funcionan en set con loops de ejecución. Para usar un timer de manera efectiva, debe conocer cómo funcionan los loops de ejecución; consulte la Guía de progtwigción de NSRunLoop y Threading . Tenga en count en particular que los loops de ejecución mantienen fuertes references a sus timeres, por lo que no tiene que mantener su propia reference fuerte a un timer después de haberlo agregado a un bucle de ejecución.

Un timer no es un mecanismo en time real; solo se dispara cuando uno de los modos de ciclo de ejecución al que se ha agregado el timer se está ejecutando y puede verificar si el time de disparo del timer ha pasado. Debido a las diversas fonts de input que administra un bucle de ejecución típico, la resolución efectiva del intervalo de time para un timer está limitada al order de 50 a 100 milisegundos. Si el time de disparo del timer se produce durante una llamada larga o mientras el ciclo de ejecución está en un modo que no supervisa el timer, el timer no se dispara hasta la próxima vez que el ciclo de ejecución verifica el timer . Por lo tanto, el time real en que el timer se dispara potencialmente puede ser un período de time significativo después del time de disparo progtwigdo.

Énfasis en el mío.

Lo importante de esto es que mientras su aplicación está en segundo plano, cualquier ciclo de ejecución en el que su cronómetro se haya progtwigdo no se está ejecutando activamente.

Tan pronto como su aplicación regrese al primer plano, este bucle de ejecución dispara una copy de security, ve que su timer está vencido y envía el post al selector.


Con iOS 7 y adelante, si desea realizar operaciones en segundo plano, puede decirle al sistema operativo que desea realizar "capturas de background".

Para configurar esto, primero debemos decirle al sistema operativo con qué frecuencia queremos get datos, por lo que en didFinishLaunching... , agregue el siguiente método:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum) return true } 

Podemos pasar cualquier intervalo de time aquí (por ejemplo, si solo queremos verificarlo una vez al día). Sin embargo, el valor que pasamos solo define una cantidad mínima de time que debe pasar entre cheques. No hay manera de decirle al sistema operativo un time máximo entre cheques.

Ahora, debemos implementar el método que realmente se llama cuando el sistema operativo nos da la oportunidad de hacer un trabajo en segundo plano:

 func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // do background work } 

Podemos hacer lo que queramos dentro de este método. Sin embargo, hay dos capturas.

  1. Este método se llama mientras nuestra aplicación está en segundo plano. El sistema operativo nos limita (creo) treinta segundos. Después de treinta segundos, nuestro time ha terminado.
  2. Debemos llamar a completionHandler() (o el sistema operativo pensará que usamos todo nuestro time).

El completionHandler que se pasa tiene una enumeración, UIBackgroundFetchResult . Deberíamos transmitirlo .Failed , .NewData o .NoData , dependiendo de cuáles fueron nuestros resultados reales (este enfoque se utiliza normalmente para verificar que un server tenga datos nuevos).

Entonces, nuestro método podría verse así:

 func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // do stuff if let _ = error { completionHandler(.Failed) } else if results.count > 0 { completionHandler(.NewData) } else { completionHandler(.NoData) } } 

Tenga en count que tenemos absolutamente ningún control sobre la frecuencia con la que el sistema operativo nos permitirá ejecutar este código en segundo plano. El sistema operativo utiliza varias métricas para optimizar la experiencia del usuario.

Creo que si su aplicación informa. .Failed al manejador de finalización, el sistema operativo podría darle una segunda oportunidad pronto, sin embargo, si está abusando. .Failed , el sistema operativo probablemente podría .Failed su aplicación en la list negra para usar búsquedas de background (y Apple podría negar su aplicación )

Si su aplicación no informa .NewData , el sistema operativo permitirá que su aplicación funcione en segundo plano con less frecuencia. No digo esto porque recomiendo que siempre informe .NewData . Definitivamente deberías informar con precisión. El sistema operativo es muy inteligente sobre el trabajo de progtwigción. Si pasa .NewData cuando no hay datos nuevos, el sistema operativo permitirá que su aplicación funcione con más frecuencia de la que pueda necesitar, lo que agotará la batería del usuario más rápido (y puede llevar a desinstalar la aplicación por completo).

Hay otras métricas involucradas en cuando su aplicación puede hacer trabajos de background sin embargo. Es muy poco probable que el sistema operativo permita que ninguna aplicación funcione en segundo plano mientras el usuario usa activamente su dispositivo y es más probable que permita que las aplicaciones funcionen en segundo plano mientras el usuario no usa su dispositivo. Además, es más probable que el sistema operativo funcione en segundo plano mientras está en WiFi y mientras está enchufado a un cargador de algún tipo.

El sistema operativo también analizará la frecuencia con que el usuario usa su aplicación o cuando la usa regularmente. Si el usuario usa su aplicación todos los días a las 6 p.m. y nunca en cualquier otro momento, lo más probable es que su aplicación siempre tenga la oportunidad de trabajar en segundo plano entre las 5:30 p.m. y las 6 p.m. (justo antes de que el usuario use la aplicación) y nunca durante ninguna otra parte del día. Si el usuario muy raramente usa su aplicación, puede ser días, semanas o meses entre las oportunidades para trabajar en segundo plano.