2015-08-16 6 views
9

La mia app è configurata per supportare push silenzioso (contenuto disponibile) e supporta anche il recupero dello sfondo . Voglio che io abbia bisogno di ottenerlo dopo aver ricevuto una push silenziosa, ho bisogno di inviare una richiesta Ajax al server, recuperare i dati e salvarli (persistono con CoreData).Download async all'interno didReceiveRemoteNotification

Ovviamente tutto ciò accade senza che l'utente apra mai l'app. Quando aprirà l'app, un nuovo dato sarà in attesa. Questa è la chiamata spinta silenziosa indietro:

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) 
    } 
} 

Ora, il problema è che quando una notifica arriva e il delegato si chiama, il codice viene eseguito ajax, effettuare la chiamata al server, e poi esce. Il codice all'interno del callback ajax non verrà eseguito. Ma quando apro l'app e la porto in primo piano, improvvisamente questa sezione di codice si riavvia e continua a funzionare.

Questo non è il comportamento auspicabile perché quando apro l'applicazione ho ancora bisogno di aspettare 1-2 secondi per le quelle operazioni per eseguire (l'aggiornamento dell'interfaccia utente, ecc)

Che cosa sto facendo male qui? Devo aprire un nuovo thread in background per questa operazione?

UPDATE:
mi sono trasferito completionHandler (UIBackgroundFetchResult.NewData) nel callback ajax, ma ancora questa dose non risolve il problema originale, che è che questo blocco di codice AJAX callback non verrà eseguito.

UPDATE 2:
Sembra come se fosse un problema con Alamofire, e che dovrò utilizzare NSURLSession di fare questa chiamata AJAX. Cercando di mettere insieme un po 'di codice.

+0

Ehi, hai risolto il tuo problema? – gersonmendes

risposta

4

Non stai utilizzando il 'completionHandler' in un modo corretto

chiamata a questo gestore di completamento è come dire l'iOS hai finito con il vostro compito e può ora mettere la vostra applicazione di nuovo a dormire o al sfondo. Si sta chiamando immediatamente

Quindi non vi resta che modificare il codice per qualcosa di simile

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) 
    } 
} 

Ecco la descrizione della documentazione di Apple per questo completionHandler

Il blocco da eseguire quando il download l'operazione è completa Quando chiama questo blocco, passa il valore del risultato di recupero che meglio descrive i risultati dell'operazione di download. È necessario chiamare questo gestore e dovrebbe farlo il prima possibile. Per un elenco di possibili valori, vedere il tipo UIBackgroundFetchResult.

E anche this answer potrebbe essere utile

+0

Questo non è ciò che l'OP sta descrivendo. Il metodo supera il gestore di completamento, esegue il comando in basso ed esce - la richiesta * è * in uscita. Semplicemente non è possibile accodare ulteriori lavori in background su un thread sospeso e aspettarsi che venga eseguito. Non avrebbe senso che un metodo venga sospeso nel mezzo dell'esecuzione a meno che non superi il timeout di 30 secondi documentato. – BaseZen

+0

@BaseZen Questa risposta è ancora valida. Un'app non deve semplicemente chiamare il gestore di completamento come mostrato nel codice della domanda. Dovrebbe essere usato correttamente. – rmaddy

+0

@BaseZen non è solo eseguito in basso, viene eseguito dopo che è stata effettuata la richiamata e i dati sono stati scaricati, non capisco il tuo downvote – dGambit

1

REWRITE

Non stai usando AlamoFire correttamente, ma è possibile configurarlo per fare recupera quando in uno sfondo Stato:

AlamoFire Download in Background Session

E il gestore di completamento deve essere chiamato alla fine del blocco di completamento della chiamata AF, non all'inizio del metodo.

1

Per aggiungere a questa risposta. Quando faccio questo, ho ottenuto supporto spotty all'avvio di tale recupero dello sfondo dopo didReceiveRemoteNotification. Posso avviare una connessione Alamofire, ma potrebbe non funzionare, morirà quasi immediatamente.

purga internet e guardando tutorial di notifica silenziosi, nessuno sembra ricordare l'uso di:

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

Tutti dicono:

ya, si ottiene circa 30 secondi per la documentazione per ottenere il vostro lavoro fatto o è necessario utilizzare la sessione Sfondo NSURLSession

Appare nel mio, propri test e strapparsi i capelli, che per comprarti che 30 secondi probabilmente bisogno di invocare i metodi beginBackgroundTask* + endBackgroundTask sulle UIApplicationDelegate

Riferimenti:

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

In particolare, la risposta forum dev mela è fatto da un membro della Developer Relations di Apple, l'Assistenza tecnica sviluppatori, Core OS/hardware

Egli dice espressamente:

Eh? Stai emettendo la richiesta nella sessione condivisa di NSURLSession. Tali richieste verranno eseguite solo finché l'app rimane in esecuzione. Se l'app viene nuovamente sospesa, che in genere è ciò che accade in questo caso, la richiesta verrà interrotta a metà. Hai due opzioni qui:

  • continuare a utilizzare la sessione condivisa e utilizzare un'attività UIApplication sfondo per evitare che la vostra applicazione da sospesa. IMPORTANTE Affinché questo funzioni, la richiesta deve essere completamente veloce, in quanto l'attività in background dell'applicazione UIA in genere fornisce solo circa 30 secondi di tempo di esecuzione.
  • Eseguire la richiesta in una sessione in background NSURLSession, che organizzerà per riprendere l'app quando la richiesta è completa.

Nota la prima opzione continuare a utilizzare il sessione condivisa e utilizzare un compito UIApplication sfondo per evitare che la vostra applicazione da sospesa.