2016-11-21 7 views
0

Swift(新人)にはかなり新しく、C#で良い経験をしています。Swift3アラモファイアとリターン辞書でJSONデータをダウンロード

私がしようとしているのは、Alamofireを使用してAPIからJSONデータを取り込み、辞書を呼び出し元のメソッドに戻すことです。 呼び出し関数にデータを返さない関数に問題があります。私は自分の問題がダウンロードが非同期であると仮定しています。

ここに、データをダウンロードする機能のコードを示します。

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 

    } 
} 

私は、このタイプのエイリアスを別の定数クラスに格納しています。

typealias DownloadComplete =() ->() 

私の呼び出し関数は、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

    が非同期で返されたデータを返すようにしようとしています。それは何も返されてはいけませんが、データをクロージャーに戻すだけです。

  2. downloadWeatherDetailsは、クロージャパラメータを取ってdownloadWeatherDataに直接渡すだけでなく、(a)そのメソッドに独自のクロージャを提供しようとしています。 (b)プロパティを更新すること。

    おそらくモデルオブジェクトを作成して、それをクロージャを介して戻すだけです。また、プロパティも更新しないでください。

ですので、元に戻してください。バック辞書の結果を渡すには、閉鎖のtypealiasに応じて更新しますよう

typealias DownloadComplete = ([String: Any]?) -> Void 

をさておき、私はDictionary<KeyType, ValueType>よりも簡潔で[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ステートメントではありません)。

とにかく、私の理論上の例を続けると、私は、UIを更新し、このような何か、おそらく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)) 
} 

をしかし、失われた得ることはありません私の例の詳細ではなく、非同期メソッドを扱うときにすぐにデータを返したりプロパティを更新したりするのではなく、クロージャを介してデータを戻すようにするというホームメッセージに注目してください。最終的な呼び出し元がモデルとUIの更新を担当するようにします。