ios iphone xcode background nstimer

ios - Comportamiento NSTimer en segundo plano(addTimer:, beginBackgroundTaskWithExpirationHandler:)



iphone xcode (1)

Xcode 6.3.1 ARC habilitado, para iOS 8.3

Necesito ayuda para entender un comportamiento extraño que encuentro al tratar de mantener un temporizador compartido singleton en mi aplicación después de que ingresa en segundo plano.

Anteriormente no me importaba este NSTimer, ya que se actualizó con la ubicación del usuario en segundo plano utilizando los servicios de ubicación en segundo plano.

Sin embargo, me gustaría abordar el caso en el que el usuario rechaza las actualizaciones de ubicación mientras se encuentran todas las actualizaciones de fondo o ubicación. La ubicación y el temporizador van de la mano para mi aplicación.

Trabajé bastante información en los siguientes temas y publicaciones en el sitio web ...

Apple''s - Ejecución en segundo plano

http://www.devfright.com/ios-7-background-app-refresh-tutorial/

http://www.infragistics.com/community/blogs/stevez/archive/2013/01/24/ios-tips-and-tricks-working-in-the-background.aspx

¿Cómo hago que mi aplicación ejecute un NSTimer en segundo plano?

¿Cómo creo un NSTimer en un hilo de fondo?

¿NSTimer programado cuando la aplicación está en segundo plano?

http://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial

iPhone - Antecedentes para sondeos de eventos

Con esta información, pude derivar (2) métodos para mantener el temporizador en funcionamiento durante ~ 8 horas sin que iOS finalice la aplicación después de los 180 segundos asignados. (3 min.) De tiempo de ejecución según lo prometido por la documentación de Apple. Aquí está el código:

AppDelegate.h (sin cambios para el Método n. ° 1 o n. ° 2)

#import <UIKit/UIKit.h> @interface MFAppDelegate : UIResponder <UIApplicationDelegate> { UIBackgroundTaskIdentifier bgTask; NSInteger backgroundTimerCurrentValue; } @property (retain, nonatomic) NSTimer *someTimer; @end

AppDelegate.m (Método n. ° 1)

<...

- (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. NSLog(@"applicationDidEnterBackground"); backgroundTimerCurrentValue = 0; [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true]; [[NSRunLoop currentRunLoop] addTimer:self.someTimer forMode:NSRunLoopCommonModes]; } - (void)timerMethod { NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining]; if (backgroundTimeRemaining == DBL_MAX) { NSLog(@"Background Time Remaining = Undetermined"); } else { NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining); } if (!someBackgroundTaskStopCondition) { [self endBackgroundTask]; } else { nil; } }

...>

AppDelegate.m (Método n. ° 2)

<...

- (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. NSLog(@"applicationDidEnterBackground"); backgroundTimerCurrentValue = 0; bgTask = UIBackgroundTaskInvalid; self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ [application endBackgroundTask:bgTask]; }]; self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true]; } - (void)timerMethod { NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining]; if (backgroundTimeRemaining == DBL_MAX) { NSLog(@"Background Time Remaining = Undetermined"); } else { NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining); } if (backgroundTimerCurrentValue >= 500) { backgroundTimerCurrentValue = 0; [self.someTimer invalidate]; [[UIApplication sharedApplication] endBackgroundTask:bgTask]; } else { NSLog(@"backgroundTimerCurrentValue: %ld", backgroundTimerCurrentValue); backgroundTimerCurrentValue++; } }

...>

He simulado los (2) métodos durante la noche durante ~ 8 h. (28,800 seg.) Ajustando la sentencia if de (backgroundTimerCurrentValue> = 500). Pero para recrear los resultados de esta publicación de pregunta, utilicé 500 sec. que es claramente más de 180 segundos Estos son los resultados de ambos métodos:

... después de 500 iteraciones del self.someTimer, es decir, 500 seg.

Ahora, esto fue genial, pero después de hacer pruebas con el iPhone 5s desconectado de Xcode, etc. Pasa algo extraño ... Hice una aplicación de vista única para verificar el valor del self.someTimer en una etiqueta.

Hasta 180 seg. la etiqueta se actualiza con el valor correcto del temporizador. Después de 180 seg. y, a veces, la aplicación parece ser seleccionable desde la diapositiva de aplicaciones abiertas, sin embargo, cuando toco la pantalla para poner la aplicación en primer plano, la aplicación se reinicia rápidamente.

Este comportamiento es lo que prometió Apple, es decir, después de 180 segundos. y no tener un método endBackgroundTask válido. La parte del método es lo que creo que es inválido de tipo UIBackgroundTaskIdentifier como en el método n. ° 2 y nulo para el método n. ° 1.

Entonces mis preguntas son las siguientes:

1) ¿Qué es diferente de cuando el iPhone está conectado a mi Mac con Xcode desde que se desconecta? ¿El sistema permite que se den ciertas condiciones, como esta ejecución de fondo aparentemente interminable?

2) Siempre puedo complicarme con los servicios de ubicación para iniciar / detenerlos, de modo que el valor del temporizador continúe actualizándose a partir de una acumulación de tiempo expirada desde la última actualización de ubicación, pero ¿alguien ha desarrollado este código para ejecutar realmente en un iPhone desconectado?

3) ¿Y si es así en la pregunta 2, entonces es algo así como legítimo y será aceptado cuando envíe mi aplicación para la App Store?

Gracias de antemano por tu ayuda. ¡Aclamaciones!


Sí, ejecutar la aplicación a través del depurador lo mantendrá con vida cuando de lo contrario se daría por terminado después de 3 minutos. Entonces, obviamente no puede confiar en esa característica en una aplicación de producción.

No, los intentos de mantener la aplicación activa más allá de este período no son legítimos (a menos que haya solicitado una operación de fondo adecuada porque su aplicación tiene una necesidad legítima y convincente de funcionamiento en segundo plano, por ejemplo, un reproductor de música, VOIP, una aplicación de navegación, etc. .). Es probable que Apple rechace cualquier aplicación que no cumpla con la sección 2.16 de las Pautas de revisión de la tienda de aplicaciones .

Consulte el capítulo Ejecución en segundo plano de la Guía de programación de aplicaciones para iOS para obtener una descripción más completa de la operación en segundo plano válida.