2015-01-22 9 views
10

Sto scrivendo un'applicazione che dipende dai dati di vari siti/servizi e comporta l'esecuzione di calcoli basati su dati provenienti da queste diverse fonti per produrre un prodotto finale.Controllo di più risposte asincrone da Alamofire e Swift

Ho scritto un esempio di classe con due funzioni sotto che raccoglie i dati dalle due fonti. Ho scelto di rendere diverse le funzioni, perché a volte applichiamo diversi metodi di autenticazione a seconda della sorgente, ma in questo esempio li ho semplicemente spogliati nella loro forma più semplice. Entrambe le funzioni usano Alamofire per sparare e gestire le richieste.

Ho quindi una funzione di inizializzazione, che dice se abbiamo raccolto correttamente i dati da entrambe le fonti, quindi caricare un altro file di pennino, altrimenti attendere fino a secondi, se non è stata restituita alcuna risposta, quindi caricare un file di errore del server .

Ho cercato di rendere questo esempio il più semplice possibile. Essenzialmente. Questo è il tipo di logica che vorrei seguire. Purtroppo sembra che questo non funzioni attualmente nella sua attuale implementazione.

import Foundation 

class GrabData{ 
    var data_source_1:String? 
    var data_source_2:String? 

    init(){  
     // get data from source 1 
     get_data_1{ data_source_1 in 
      println("\(data_source_1)") 
     } 

     // get data from source 2 
     get_data_2{ data_source_1 in 
      println("\(data_source_1)") 
     } 

     var timer = 0; 
     while(timer<5){ 
      if((data_source_1 == nil) && (data_source_2 == nil)){ 
       // do nothing unless 4 seconds has elapsed 
       if (timer == 4){ 
        // load server error nib 
       } 
      }else{ 
       // load another nib, and start manipulating data 
      } 
      // sleep for 1 second 
      sleep(1) 
      timer = timer+1 
     }  
    } 

    func get_data_1(completionHandler: (String) ->()) ->() { 
     if let datasource1 = self.data_source_1{ 
      completionHandler(datasource1) 
     }else{ 
      var url = "http://somewebsite.com" 
      Manager.sharedInstance.request(.GET, url).responseString { 
       (request, response, returnedstring, error) in 
       println("getting data from source 1") 
       let datasource1 = returnedstring 
       self.data_source_1 = datasource1 
       completionHandler(datasource1!) 
      } 
     } 
    } 

    func get_data_2(completionHandler: (String) ->()) ->() {  
     if let datasource2 = self.data_source_2{ 
      completionHandler(datasource2) 
     }else{ 
      var url = "http://anotherwebsite.com" 
      Manager.sharedInstance.request(.GET, url).responseString { 
       (request, response, returnedstring, error) in 
       println("getting data from source 2") 
       let datasource2 = returnedstring 
       self.data_source_2 = datasource2 
       completionHandler(datasource2!) 
      } 
     } 
    } 
} 

so che ho potuto mettere la seconda chiusura entro il primo all'interno della funzione init, tuttavia, non credo che questo sarebbe best practice e sto in realtà tirando da più di 2 fonti, così la chiusura sarebbero n chiusure profonde.

Qualsiasi aiuto per capire il modo migliore per verificare se più origini dati hanno dato una risposta valida, e la gestione che sarebbe stata opportunamente apprezzata.

risposta

40

Meglio di quel processo di ciclo, che bloccherebbe il thread, è possibile utilizzare il gruppo di invio per tenere traccia di quando sono state eseguite le richieste. Quindi "inserisci" il gruppo prima di inviare ognuna delle richieste, "lascia" il gruppo quando la richiesta è terminata e imposta un blocco/chiusura "notifica" che verrà chiamato quando tutte le attività del gruppo sono state eseguite.

Per esempio, in Swift 3:

let group = DispatchGroup() 

group.enter() 
retrieveDataFromURL(url1, parameters: firstParameters) { 
    group.leave() 
} 

group.enter() 
retrieveDataFromURL(url2, parameters: secondParameters) { 
    group.leave() 
} 

group.notify(queue: .main) { 
    print("both requests done") 
} 

O, in Swift 2:

let group = dispatch_group_create() 

dispatch_group_enter(group) 
retrieveDataFromURL(url1, parameters: firstParameters) { 
    dispatch_group_leave(group) 
} 

dispatch_group_enter(group) 
retrieveDataFromURL(url2, parameters: secondParameters) { 
    dispatch_group_leave(group) 
} 

dispatch_group_notify(group, dispatch_get_main_queue()) { 
    print("both requests done") 
} 

L'altro approccio è quello di avvolgere queste richieste all'interno di un asincrono NSOperation sottoclasse (che li rende annullabile, dando puoi controllare il vincolo del grado di concorrenza, ecc.), ma è più complicato, quindi potresti iniziare con i gruppi di invio come mostrato sopra.

+0

wow un modo decente per risolvere il problema, grazie amico! – Wilson

+0

Fantastico, mi sono chiesto come meglio farlo per secoli. – toast