2016-11-21 3 views
0

Я довольно новичок в Swift, (новичок), но у меня хороший опыт работы с C#.Swift3 загружает данные JSON с помощью словаря Alamofire и возврата

То, что я пытаюсь сделать, это использовать Alamofire для загрузки данных JSON из API и возврата словаря к вызывающему методу. У меня возникают проблемы с функцией, не возвращающей данные к вызывающей функции. Я предполагаю, что моя проблема в том, что загрузка является асинхронной.

Вот мой код функции, которая предназначена для загрузки данных.

import UIKit 
import Alamofire 

class SharedFunctions { 


    class func downloadWeatherData(url: String, completed: @escaping DownloadComplete) -> Dictionary<String, AnyObject>? { 


     let currentWeatherURL = URL(string: url)! 

     var workingDict: Dictionary<String, AnyObject>? = nil 

     Alamofire.request(currentWeatherURL).responseJSON { 
      response in 
      let result = response.result 

      workingDict = result.value as? Dictionary<String, AnyObject> 

      completed() 

     } 

     return workingDict 

    } 
} 

У меня есть этот тип псевдонима, хранящийся в отдельном классе констант.

Моя вызывающая функция, когда она попадает в if let dict, код будет продолжаться, не делая многого. Эта функция вызывается из кода контроллера просмотра.

func downloadWeatherDetails(completed: @escaping DownloadComplete) 
{ 
     if let dict = SharedFunctions.downloadWeatherData(url: CURRENTWEATHERURL, completed: completed) { 

      if let name = dict["name"] as? String { 
       self._cityName = name.capitalized 
      } 

     } 
    completed() 
} 

Мой старый код, который работал, у меня был вызов Alamofire вызывается из функции downloadWeatherDetails и имел его работать нормально, данные возвращаются обратно в VC, когда загрузка была завершена.

ответ

0

Вам необходимо внести изменения в функции, как этот путь -

typealias DownloadComplete = Dictionary<String, Any> 

    func downloadWeatherData(url: String, completion: (DownloadComplete) -> Void) { 

     //Do your process here 
     Alamofire.request(url).responseJSON { 
     response in 
      // Finish process here 
      switch response.result { 
      case .success: 
      completion(response.result.value as? Dictionary<String, Any>) 
      case .failure(let error): 
      completion([:]) 
      print(error) 
      } 

     } 

    } 

    //MARK: - Call function 
    downloadWeatherData(url: "your url here") { (DownloadComplete) in 
     //Do something with data 
     print(DownloadComplete) 
    } 
2

Есть два вопроса:

  1. downloadWeatherData The пытается вернуть данные, возвращаемые в асинхронном режиме. Он ничего не должен возвращать, а просто передает данные обратно в закрытие.

  2. 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)) 
} 

Но не заблудитесь в деталях моего примера, а скорее сосредоточимся на принятом домашнем сообщении о том, что при работе с асинхронными методами не пытайтесь немедленно возвращать данные или обновлять свойства, а скорее передавать данные через закрытие. И пусть конечный абонент позаботится об обновлении модели и пользовательского интерфейса.