Descarga asincrónica dentro de didReceiveRemoteNotification

Mi aplicación está configurada para admitir la inserción silenciosa (disponible para el contenido) y también admite la búsqueda de background . Lo que quiero lograr es recibir un impulso silencioso, tengo que enviar una request de ajax al server, recuperar los datos y savelos (persévelos con CoreData).

Por supuesto, todo esto sucede sin que el usuario abra la aplicación. Cuando abra la aplicación, se le enviarán nuevos datos. Esta es la respuesta silenciosa:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // Use Alamofire to make an ajax call: //... let mutableURLRequest = NSMutableURLRequest(URL: URL) let requestBodyData : NSMutableData = NSMutableData() mutableURLRequest.HTTPBody = body mutableURLRequest.HTTPMethod = "POST" mutableURLRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") request(mutableURLRequest) .responseJSON { (req, res, data, error) in //We do not reach this code block ! // Save the incoming data to CoreData completionHandler(UIBackgroundFetchResult.NewData) } } 

Ahora, el problema es que cuando llega una notificación y se llama al delegado, el código ajax se ejecuta, realiza la llamada al server y luego sale. El código dentro de la callback ajax no se ejecutará . Pero, cuando abro la aplicación y la traigo al primer plano, de repente esta sección de código se despierta y continúa ejecutándose .

Este no es el comportamiento deseable porque cuando abro la aplicación todavía necesito esperar 1-2 segundos para que se ejecuten esas operaciones (actualizando la interfaz de usuario, etc.)

¿Qué estoy haciendo mal aquí? ¿Debería abrir un nuevo subprocess de background para esta operación?

ACTUALIZAR:
Moví completionHandler (UIBackgroundFetchResult.NewData) en la callback ajax, pero esta dosis no corrige el problema original, que es que este bloque de código de callback de Ajax no se ejecutará.

ACTUALIZACIÓN 2:
Parece que es un problema con Alamofire, y que tendré que usar NSURLSession para realizar esta llamada de ajax. Tratando de juntar un poco de código.

No estás utilizando 'completionHandler' de manera adecuada.

Llamar a este controller de finalización es como decirle al iOS que ha terminado su tarea y ahora puede volver a poner la aplicación en modo de suspensión o en segundo plano. Lo llamas de inmediato

Entonces, solo necesitas cambiar tu código a algo como esto

 func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // Use Alamofire to make an ajax call: let mutableURLRequest = NSMutableURLRequest(URL: URL) let requestBodyData : NSMutableData = NSMutableData() mutableURLRequest.HTTPBody = body mutableURLRequest.HTTPMethod = "POST" mutableURLRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") request(mutableURLRequest) .responseJSON { (req, res, data, error) in // if there was an error then { // completionHandler(UIBackgroundFetchResult.Failed) // return // } // Save the incoming data to CoreData completionHandler(UIBackgroundFetchResult.NewData) } } 

Aquí está la descripción de la documentation de Apple para esa completionHandler

El bloque que se debe ejecutar cuando se completa la operación de descarga. Cuando llame a este bloque, pase el valor del resultado de búsqueda que mejor describa los resultados de su operación de descarga. Debe llamar a este controller y debe hacerlo tan pronto como sea posible. Para get una list de posibles valores, consulte el tipo UIBackgroundFetchResult.

Y también esta respuesta podría ser útil.

VOLVER A ESCRIBIR

No está utilizando AlamoFire correctamente, pero puede configurarlo para que se recupere cuando está en estado de background:

Descargar AlamoFire en session de background

Y el manejador de finalización debe recibir una llamada al final del bloque de finalización de la llamada AF, no al comienzo del método.

Para agregar a esta respuesta. Cuando hago esto, he recibido soporte irregular al iniciar esa búsqueda de background después de didReceiveRemoteNotification . Puedo iniciar una connection Alamofire, pero puede que no funcione, morirá casi de inmediato.

Recorriendo Internet y mirando tutoriales de notificación silenciosa, nadie parece mencionar el uso de:

  • beginBackgroundTaskWithExpirationHandler:
  • beginBackgroundTaskWithName (_: expirationHandler 🙂
  • endBackgroundTask (_ 🙂

Todo el mundo dice:

ya, obtienes unos 30 segundos por cada documento para realizar tu trabajo o necesitas utilizar la session de background NSURLSession

Aparece en mi propia testing y en el tirón del cabello, para poder comprarte esos 30 segundos es probable que necesites invocar los beginBackgroundTask* + endBackgroundTask en UIApplicationDelegate

Referencias:

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithName:expirationHandler :

https://forums.developer.apple.com/message/96731#96731

Específicamente, la respuesta del Apple Dev Forum es realizada por un miembro de Apple Developer Relations, Developer Technical Support, Core OS / Hardware

Él dice específicamente:

Huh? Está emitiendo la request en la session compartida de NSURLSession. Estas requestes solo se ejecutarán mientras la aplicación siga funcionando. Si la aplicación se suspende nuevamente, que suele ser lo que sucede en este caso, la request se detendrá a la mitad. Tiene dos opciones aquí:

  • Continúe utilizando la session compartida y use una tarea en segundo plano UIApplication para evitar que su aplicación se suspenda. IMPORTANTE Para que esto funcione, la request debe ser completamente rápida, porque la tarea de background UIApplication generalmente solo le dará unos 30 segundos de time de ejecución.
  • Ejecute la request en una session en segundo plano NSURLSession, que se organizará para reanudar su aplicación cuando se complete la request.

Tenga en count la primera opción Continuar utilizando la session compartida y utilice una tarea en segundo plano UIApplication para evitar que su aplicación se suspenda.