Могу ли я установить порядок выполнения функций в swift?

Я хочу выполнить функцию «getWeatherInformation», когда функция «getLocation» заканчивается сохранением широты/долготы

Выполняя приведенный ниже код, «getWeatherInformation» запускается до того, как функция «getLocation» сохраняет широту/долготу.

Я думал, что он будет работать синхронно, используя синхронизацию в «DispatchQueue.main.sync», но это не так.

Как я могу это решить? Благодарю вас.

// get lat/lon of cityName
func getLocation(cityName: String) {
    guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=\(keyValue)") else {return}
    let session = URLSession(configuration: .default)
    session.dataTask(with: url) { [weak self] data, response, error in
        let successRange = (200..<300)
        guard let data = data, error == nil else {return}
        let decoder = JSONDecoder()
        if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
            guard let location = try? decoder.decode(Location.self, from: data) else {return}
            DispatchQueue.main.sync {
                self?.findLocation(location: location)  // save lat&lon
            }
        } else {
            print("error")
        }
    }.resume()
}

// get weatherInformation using lat/lon
func getWeatherInformation(lat: Double, lon: Double) {
    guard let url = URL(string: "https://api.openweathermap.org/data/2.5/onecall?lat=\(lat)&lon=\(lon)&exclude=minutely&appid=\(keyValue)") else {return}
    let session = URLSession(configuration: .default)
    session.dataTask(with: url) { [weak self] data, response, error in
        let successRange = (200..<300)
        guard let data = data, error == nil else {return}
        let decoder = JSONDecoder()
        if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
            guard let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else {return}
            DispatchQueue.main.sync {
                self?.configureWeather(weatherInformation: weatherInformation)
                self?.hourlyCollectionView.reloadData()
            }
        } else {
            print("error")
        }
    }.resume()
}

Вы знакомы с OperationQueue? Вы можете настроить эту очередь как синхронно работающую очередь во время создания.

Kozmotronik 17.05.2022 11:13

Используйте обработчики завершения или, что удобнее, async/await.

vadian 17.05.2022 11:20
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
0
2
33
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Добавьте параметр обработчика завершения в функцию getLocation и вызовите его, когда задача данных завершится.

При вызове getLocation передайте туда замыкание, которое, в свою очередь, вызовет getWeatherInformation.

Несколько других улучшений, которые я бы предложил:

  • Переместите побочные эффекты, такие как сохранение местоположения, из тела метода в его обработчик завершения. Тогда его ответственность будет заключаться в том, чтобы просто получить местоположение, проанализировать его и передать обработчику завершения. Гораздо больше многоразового использования.
  • Создайте новый тип для различных ошибок, которые могут возникнуть при выполнении сетевого запроса. Это позволит вам быстрее находить проблемы и соответствующим образом информировать пользователя в пользовательском интерфейсе в зависимости от того, что произошло. Мой пример ниже может быть слишком подробным для этой причины, но надеюсь, что он даст вам представление.
  • Используйте Swift Result для типа возвращаемого значения обработчика завершения, чтобы сообщить вызывающей стороне, была ли выборка по сети успешной или неудачной.

Вот как я предлагаю написать это:

enum LocationFetchError: Error {
    case malformedUrl
    case networkError(innerError: Error)
    case noData
    case unexpectedResponse
    case parsingError
}

func getLocation(cityName: String, completion: @escaping (Result<Location, Error>) -> Void) {
    guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=\(keyValue)") else {
        completion(.failure(LocationFetchError.malformedUrl))
        return
    }
    let session = URLSession(configuration: .default)
    session.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(LocationFetchError.networkError(innerError: error)))
            return
        }
        guard let data = data else {
            completion(.failure(LocationFetchError.noData))
            return
        }

        let successRange = (200..<300)
        guard let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) else {
            completion(.failure(LocationFetchError.unexpectedResponse))
            return
        }

        let decoder = JSONDecoder()
        guard let location = try? decoder.decode(Location.self, from: data) else {
            completion(.failure(LocationFetchError.parsingError))
            return
        }

        completion(.success(location))
    }.resume()
}

И вот как бы вы назвали этот метод:

getLocation(cityName: "Springfield", completion: { [weak self] result in
    switch result {
    case .success(let location):
        DispatchQueue.main.async {
            self?.findLocation(location: location)
        }
        self?.getWeatherInformation(lat: location.lat, lon: location.lon)

    case .failure(let error):
        print(error.localizedDescription)
        // TODO: show alert if needed, don't forget to dispatch to the main queue
    }
})

Я бы рекомендовал применить те же методы к getWeatherInformation. В этом примере я этого не делал, но надеюсь, вы поняли идею.

Другие вопросы по теме