Swift - Возникают проблемы с возвратом нескольких сетевых вызовов в том порядке, в котором они были заданы с помощью gcd - PullRequest
0 голосов
/ 05 марта 2019

Я быстро учусь и решил создать простое приложение погоды.У меня проблема с получением данных о погоде в том порядке, в котором я их запрашивал.В моем контроллере представления у меня есть 2 свойства, weatherData и местоположения, которые являются массивами.Когда приложение открывается, я загружаю все сохраненные местоположения из UserDefaults.Затем я перебираю каждое местоположение и делаю несколько сетевых вызовов для получения данных о погоде для каждого местоположения.Я использую группы рассылки, чтобы убедиться, что я жду, пока все данные не будут загружены, прежде чем я использую данные о погоде для загрузки viewModels, которые его используют.Проблема в том, что weatherData не всегда возвращается в том же порядке, который я запрашивал, поэтому индексы location и weatherData не выровнены / не синхронизированы.Я думал, что использование DispatchQueue.main.async будет последовательным и будет выполнять элементы по порядку, но этого не происходит.Я, очевидно, не правильно понимаю, как работает gcd, и я действительно изо всех сил пытаюсь найти решение этой проблемы.Любое руководство будет высоко ценится, спасибо заранее!

Код в viewController

private func updateWeatherData() {
    let myGroup = DispatchGroup()


    for i in 0..<locations.count {
        myGroup.enter()
        print("Calling fetch Weather for index: ", i )
        self.apiService.fetchWeatherData(for: self.locations[i].location) { (response, error) in
            if let error = error {
                // Unable to retrieve weather data
                self.presentAlert()
            } else if let response = response {
                print("Finished Request: \(i)")
                self.weatherData.append(response)

            }
            myGroup.leave()
        }
    }

    myGroup.notify(queue: .main) {
        print("Finished All requests")
        self.locationsTableViewController.viewModel = LocationsViewModel(locations: self.locations, weatherData: self.weatherData)
    }
}

Код в моем классе apiService

func fetchWeatherData(for location: CLLocation, completion: @escaping WeatherDataCompletion) {
        // Weather request used to return valid api url string based on location 
        let weatherRequest = WeatherRequest(baseUrl: APIConfiguration.authenticatedBaseUrl, location: location)

        URLSession.shared.dataTask(with: weatherRequest.url) { (data, response, error) in
            DispatchQueue.main.async {
                self.didFetchWeatherData(data: data, response: response, error: error, completion: completion)

            }
        }.resume()
    }


    // Error checks and decodes network data response
    private func didFetchWeatherData(data: Data?, response: URLResponse?, error: Error?, completion: WeatherDataCompletion) {
        if let error = error {
            completion(nil, .failedRequest)
            print("No WeatherData Available: ", error)
        } else if let data = data, let response = response as? HTTPURLResponse {
            if response.statusCode == 200 {
                do {
                    // Decode JSON
                    let weatherData = try decoder.decode(DarkSkyResponse.self, from: data)
                    completion(weatherData, nil)
                } catch {
                    print("Unable to Decode JSON: ", error)
                    completion(nil, .invalidResponse)
                }
            } else {
                print("Status Code: ", response.statusCode)
                completion(nil, .failedRequest)
            }
        } else {
            completion(nil, .unknown)
        }
    }

РЕДАКТИРОВАТЬ: Как уже упоминалось, ответЯ узнал, что URLSession не гарантирует порядок.В итоге я просто сохранил каждый результат по индексу в начальном цикле.

1 Ответ

2 голосов
/ 06 марта 2019

С этим

URLSession.shared.dataTask(with: weatherRequest.url)

нет никакой гарантии, что вы получите ответы на запросы по порядку, поэтому вы можете создать модель, содержащую запрос с некоторым свойством number, после того как все ответы завершатся здесь

myGroup.notify(queue: .main) { }

рассортируйте их по номеру или сделайте это

class LocationsViewModel {
   var loc:CLLocation
   var weatherData:DarkSkyResponse?
   init(loca:CLLocation) {
     self.loc = loca
   } 
   func fetchWeatherData(completion: @escaping WeatherDataCompletion) {
    // here do the fetch and assign the weatherData
     weatherData = decodeRes 

   }
} 

еще одна вещь: вы можете связать запросы таким образом, чтобы после возврата ответа был сделан следующий вызов и т. Д., Вы также должны удалить

DispatchQueue.main.async {

как все должно происходить в фоновом потоке

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...