puedo - ¿Cómo solucionar problemas de la aplicación de fondo iOS no funciona?
no puedo descargar aplicaciones en mi iphone 4 (2)
Estoy tratando de hacer que la aplicación iOS de fondo funcione en mi aplicación. Mientras se prueba en Xcode funciona, cuando se ejecuta en el dispositivo no funciona.
- Mi dispositivo de prueba ejecuta iOS 9.3.5 (mi objetivo de implementación es 7.1)
- He habilitado "Recuperación en segundo plano" en "Modos de fondo" en "Capacidades" en el destino en Xcode
En aplicación: didFinishLaunchingWithOptions He intentado varios intervalos con setMinimumBackgroundFetchInterval, incluido UIApplicationBackgroundFetchIntervalMinimum
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// tell the system we want background fetch
//[application setMinimumBackgroundFetchInterval:3600]; // 60 minutes
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
//[application setMinimumBackgroundFetchInterval:1800]; // 30 minutes
return YES;
}
He implementado la aplicación: performFetchWithCompletionHandler
void (^fetchCompletionHandler)(UIBackgroundFetchResult);
NSDate *fetchStart;
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletionHandler = completionHandler;
fetchStart = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[[NSUserDefaults standardUserDefaults] synchronize];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] < 0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentContentDate = item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
}
He probado (¡con éxito!) Con el simulador y el dispositivo utilizando Debug / SimulateBackgroundFetch de Xcode
He probado con éxito un nuevo esquema como se muestra en otra respuesta SO ( https://stackoverflow.com/a/29923802/519030 )
- Mis pruebas muestran el código que se ejecuta en el método performFetch en aproximadamente 0,3 segundos (por lo que no lleva mucho tiempo)
- He verificado que el dispositivo tiene habilitada la actualización de fondo dentro de la configuración.
- Por supuesto, he mirado las otras preguntas de SO con la esperanza de que alguien más haya experimentado lo mismo. :)
Cuando se ejecuta en el dispositivo y no está conectado a Xcode, mi código no se está ejecutando. Abrí la aplicación, cerré la aplicación (¡no maté a la aplicación!), Esperé horas y días. He intentado iniciar sesión en los gestores de acceso y también he escrito un código para enviar notificaciones locales.
Una vez vi con éxito mi prueba de notificaciones local en el dispositivo, y de hecho, iOS pareció disparar el alcance tres veces, cada una con una diferencia de aproximadamente quince minutos, pero luego nunca volvió a ocurrir.
Sé que el algoritmo utilizado para determinar con qué frecuencia permitir que se produzca la recuperación en segundo plano es un misterio, pero espero que se ejecute al menos ocasionalmente en un lapso de días.
No sé qué más probar, o cómo solucionar los problemas por los que parece funcionar en el simulador pero no en el dispositivo.
Aprecio cualquier consejo!
Su problema es que está regresando de performFetchWithCompletionHandler
antes de llamar al controlador de finalización, ya que la operación de recuperación de red se está ejecutando en segundo plano y usted llama al controlador de finalización en su método de delegado. Ya que iOS cree que no estás siguiendo las reglas, negará tu capacidad de usar la recuperación de fondo.
Para solucionar el problema, debe llamar a beginBackgroundTaskWithExpirationHandler
y luego finalizar esa tarea después de haber llamado al controlador de finalización.
Algo como:
UIBackgroundTaskIdentifier backgroundTask
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletionHandler = completionHandler;
fetchStart = [NSDate date];
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] < 0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentContentDate = item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
[[UIApplication sharedApplication] application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
Mi aplicación de prueba que utiliza este enfoque ha estado ejecutando una recuperación cada 15 minutos inicialmente, pero con el tiempo se vuelve menos frecuente. Sin la tarea de fondo exhibió el mismo problema que está viendo.
Descubrí que configurar el intervalo de recuperación en segundo plano en algo distinto a UIApplicationBackgroundFetchIntervalMinimum
también ayuda. Mi aplicación de prueba se está ejecutando con un intervalo de búsqueda en segundo plano de 3600 (una hora) y se ha activado de manera confiable durante varios días; Incluso después de reiniciar el teléfono y no volver a ejecutar la aplicación. Sin embargo, el intervalo de activación real es de 2-3 horas.
Mi aplicación de muestra está here
Para implementar la búsqueda de fondo, hay tres cosas que debes hacer:
- Marque la casilla Recuperación de fondo en los modos de fondo de las capacidades de su aplicación.
- Use setMinimumBackgroundFetchInterval (_ :) para establecer un intervalo de tiempo apropiado para su aplicación.
- Implemente la aplicación (_: performFetchWithCompletionHandler :) en el delegado de su aplicación para manejar la recuperación en segundo plano.
Frecuencia de búsqueda de fondo
La frecuencia con la que nuestra aplicación puede realizar la obtención en segundo plano está determinada por el sistema y depende de:
- Si la conectividad de red está disponible en ese momento en particular
- Si el dispositivo está despierto, es decir, en ejecución
- Cuánto tiempo y datos ha consumido su aplicación en la búsqueda en segundo plano anterior
En otras palabras, su aplicación está totalmente a merced del sistema para programar la obtención de antecedentes para usted. Además, cada vez que su aplicación utiliza la búsqueda en segundo plano, tiene como máximo 30 segundos para completar el proceso.
Inclúyalo en su AppDelegate (cambie el siguiente código según sus necesidades de búsqueda)
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"Background fetch started...");
//---do background fetch here---
// You have up to 30 seconds to perform the fetch
BOOL downloadSuccessful = YES;
if (downloadSuccessful) {
//---set the flag that data is successfully downloaded---
completionHandler(UIBackgroundFetchResultNewData);
} else {
//---set the flag that download is not successful---
completionHandler(UIBackgroundFetchResultFailed);
}
NSLog(@"Background fetch completed...");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
Para verificar si la actualización en segundo plano está habilitada para su aplicación, use el siguiente código:
UIBackgroundRefreshStatus status = [[UIApplication sharedApplication] backgroundRefreshStatus];
switch (status) {
case UIBackgroundRefreshStatusAvailable:
// We can do background fetch! Let''s do this!
break;
case UIBackgroundRefreshStatusDenied:
// The user has background fetch turned off. Too bad.
break;
case UIBackgroundRefreshStatusRestricted:
// Parental Controls, Enterprise Restrictions, Old Phones, Oh my!
break;
}