Есть два вопроса:
downloadWeatherData
The пытается вернуть данные, возвращаемые в асинхронном режиме. Он ничего не должен возвращать, а просто передает данные обратно в закрытие.
downloadWeatherDetails
имеет запутанную комбинацию взятия параметра закрытия, передавая его непосредственно на downloadWeatherData
, но также (а) пытается обеспечить свое закрытие этим способом; и (б) обновление имущества.
Возможно, он должен просто скомпоновать некоторый объект модели и, опять же, передать это через закрытие. Он также не должен обновлять свойство.
Итак, давайте вернемся. Чтобы передать словарные результаты назад, обновить закупоривающий typealias
соответственно:
typealias DownloadComplete = ([String: Any]?) -> Void
как в стороне, я использую [KeyType: ValueType]
синтаксис, который является более кратким, чем Dictionary<KeyType, ValueType>
, но вы можете использовать этот другой синтаксис, если вы действительно хотите. Также обратите внимание, что я делаю этот параметр закрытия необязательным (чтобы вы могли различать успешный вызов и сбой).
downloadWeatherData
А потом просто:
/// Return the dictionary returned by web service.
///
/// - Parameters:
/// - url: URL of the web service.
/// - completed: The closure that's called when the asynchronous call finishes. If there was an error, the dictionary will be `nil`.
class func downloadWeatherData(url: String, completed: @escaping DownloadComplete) {
Alamofire.request(urlString).responseJSON { response in
completed(response.result.value as? [String: Any])
}
}
Цель вашего другого метода, downloadWeatherDetails
, менее ясна. Кажется, вы пытаетесь как обновить некоторое свойство, так и вызвать закрытие. Просто, чтобы сохранить чистоту, я предлагаю вам сделать то или другое, но не то, и другое.
Например, я мог себе представить, какой объект модели:
struct WeatherReport {
let city: String
let low: Float
let high: Float
}
И downloadWeatherDetails
может извлеченный некоторые ключевые части информации, возможно, строит что модель объекта (я переименовал метод, соответственно):
/// Build `WeatherReport` object and pass it back in the closure.
///
/// - Parameter completed: The closure that will be called when the method finishes.
func downloadWeatherReport(completed: @escaping (WeatherReport?) -> Void) {
SharedFunctions.downloadWeatherData(url: currentWeatherUrl) { dictionary in
guard let city = dictionary?["name"] as? String, let low = dictionary?["low"] as? Float, let high = dictionary?["high"] as? Float else {
completed(nil)
return
}
completed(WeatherReport(city: city, low: low, high: high))
}
}
Теперь, я не знаю, захватил ли этот объект модели все, что вы хотите. Аналогично, я не знаю, какие ключи относятся к вашему словарю. Но это иллюстрирует идею: Извлеките информацию, необходимую для построения объекта модели, и передайте ее обратно через закрытие (а не заявление return
).
Во всяком случае, продолжая мой теоретический пример, я мог бы сделать что-то вроде этого, который обновляет пользовательский интерфейс, возможно, вызывается в viewDidLoad
или нажатии кнопки:
downloadWeatherReport { weatherReport in
guard let weatherReport = weatherReport else {
// handle error here
return
}
// update some model property
self.weatherReport = weatherReport
// and update the UI, too
let formatter = NumberFormatter()
self.cityLabel.text = weatherReport.city
self.lowLabel.text = formatter.string(from: NSNumber(value: weatherReport.low))
self.highLabel.text = formatter.string(from: NSNumber(value: weatherReport.high))
}
Но не заблудитесь в деталях моего примера, а скорее сосредоточимся на принятом домашнем сообщении о том, что при работе с асинхронными методами не пытайтесь немедленно возвращать данные или обновлять свойства, а скорее передавать данные через закрытие. И пусть конечный абонент позаботится об обновлении модели и пользовательского интерфейса.