Sprawdzanie wielu asynchronicznych odpowiedzi z Alamofire i Swift

Piszę aplikację, która zależy od danych z różnych stron/usług i wymaga wykonywania obliczeń na podstawie danych z tych różnych źródeł w celu wytworzenia produktu końcowego.

Napisałem przykładową klasę z dwiema funkcjami poniżej, która zbiera dane z dwóch źródeł. Wybrałem, aby funkcje były różne, ponieważ czasami stosujemy różne metody uwierzytelniania w zależności od źródła, ale w tym przykładzie po prostu rozebrałem je do ich najprostsza forma. Obie funkcje używają Alamofire do odpalania i obsługi żądań.

Następnie mam funkcję inicjalizacji, która mówi, jeśli udało nam się zebrać dane z obu źródeł, następnie załadować inny plik nib, w przeciwnym razie czekać do Sekund, jeśli nie odpowiedź została zwrócona, a następnie załadować plik nib błąd serwera.

Starałem się, aby ten przykład był jak najprostszy. Zasadniczo. To jest rodzaj logiki, którą chciałbym podążać. Niestety wygląda na to, że obecnie nie działa w swojej obecnej realizacji.

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

Wiem, że mógłbym umieścić drugie zamknięcie w ramach pierwszego wewnątrz funkcji init, jednak nie sądzę, że byłoby to najlepszą praktyką i faktycznie wyciągam z więcej niż 2 źródeł, więc zamknięcie byłoby N zamknięć głębokich.

Każda pomoc w znalezieniu najlepszego sposobu na sprawdzenie, czy wiele źródeł danych udzieliło prawidłowej odpowiedzi, i odpowiednie obchodzenie się z tym byłoby bardzo mile widziane.

Author: jonhurlock, 2015-01-23

1 answers

Lepiej niż ten proces zapętlania, który blokuje wątek, możesz użyć Grupy dyspozytorskiej, aby śledzić, kiedy żądania zostały wykonane. Tak więc "wprowadź" grupę przed wydaniem każdego z żądań, "Opuść" grupę po wykonaniu żądania i ustaw blok/zamknięcie" powiadom", który zostanie wywołany po wykonaniu wszystkich zadań grupy.

Na przykład w języku 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")
}

Lub w 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")
}

Innym podejściem jest zawinięcie tych żądań w asynchronous NSOperation subclass (dzięki czemu można je anulować, dając Ci kontrolę nad ograniczeniem stopnia współbieżności itp.), ale jest to bardziej skomplikowane, więc możesz zacząć od grup wysyłkowych, jak pokazano powyżej.

 44
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 15:53:08