2016-11-21 6 views
0

Ich bin ziemlich neu in Swift, (Anfänger), habe gute Erfahrungen mit C# obwohl.Swift3 herunterladen JSON-Daten mit Alamofire und zurück Wörterbuch

Was ich versuche zu tun ist, Alamofire zu verwenden, um JSON-Daten von einer API herunterzuladen und ein Wörterbuch zurück an die aufrufende Methode zurückzugeben. Ich habe Probleme damit, dass die Funktion die Daten nicht an die aufrufende Funktion zurückgibt. Ich nehme an, dass mein Problem ist, dass der Download asynchron ist.

Hier ist mein Code der Funktion, die Daten herunterladen soll.

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 

    } 
} 

Ich habe diesen Typ Alias ​​in einer separaten Konstanten-Klasse gespeichert.

typealias DownloadComplete =() ->() 

Meine Berufung Funktion, wenn es die trifft, wenn dict lassen, wird der Code weiter auf, ohne viel zu tun. Diese Funktion wird vom View-Controller-Code aus aufgerufen.

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() 
} 

Mein alter Code, der arbeitet, hatte ich den Anruf zu Alamofire aus der Funktion downloadWeatherDetails genannt zu werden und hatte es gut funktioniert, Daten wurden die Rückkehr in den VC zurück, wenn der Download abgeschlossen ist.

Antwort

0

Sie müssen Änderungen in Ihrer Funktion wie auf diese Weise machen -

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

Es gibt zwei Probleme:

  1. Die downloadWeatherData versucht, Daten zurückzugeben, die asynchron zurückgegeben wird. Es sollte nichts zurückgeben, sondern nur die Daten in der Schließung zurückgeben.

  2. Die downloadWeatherDetails hat eine verwirrende Kombination von Verschlussparametern, übergibt sie direkt an downloadWeatherData, aber auch (a) versucht, ihre eigene Schließung zu dieser Methode zu liefern; und (b) Aktualisieren einer Eigenschaft auch.

    Es sollte wahrscheinlich nur ein Modellobjekt erstellen, und das wiederum über einen Abschluss zurückgeben. Es sollte auch keine Eigenschaft aktualisieren.

Also, lasst uns zurücktreten. Um die Wörterbuch Ergebnisse zurück übergeben, aktualisieren Sie die typealias Schließung entsprechend:

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

Als Nebenwirkung, verwende ich die [KeyType: ValueType] Syntax, die prägnanter ist als Dictionary<KeyType, ValueType>, aber Sie können, dass andere Syntax verwenden, wenn Sie wirklich wollen. Beachten Sie auch, dass ich diesen Parameter des Abschlusses optional mache (damit Sie zwischen einem erfolgreichen Anruf und einem Fehler unterscheiden können).

Und dann downloadWeatherData ist einfach:

/// 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]) 
    } 
} 

Die Absicht Ihrer anderen Methode, downloadWeatherDetails, ist weniger klar. Sie scheinen zu versuchen, sowohl eine Eigenschaft zu aktualisieren als auch eine Schließung aufzurufen. Nur um die Dinge sauber zu halten, würde ich vorschlagen, dass Sie das eine oder andere tun, aber nicht beides.

Zum Beispiel habe ich einige Modellobjekt vorstellen konnte:

:

struct WeatherReport { 
    let city: String 
    let low: Float 
    let high: Float 
} 

Und downloadWeatherDetails könnte einige wichtige Informationen extrahiert, vielleicht Gebäude, das Modellobjekt (ich habe die Methode, entsprechend umbenannt)

/// 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)) 
    } 
} 

Nun, ich weiß nicht, ob dieses Modellobjekt alles erfasst, was Sie wollen. Ebenso weiß ich nicht, was die Schlüssel zu deinem Wörterbuch sind. Aber es veranschaulicht die Idee: Extrahieren Sie die Informationen, die Sie benötigen, um ein Modellobjekt zu erstellen, und übergeben Sie das über den Abschluss (keine return-Anweisung).

Wie auch immer, mit meinem theoretischen Beispiel fort, kann ich so etwas wie dies dann tun, die die Benutzeroberfläche aktualisiert, vielleicht in viewDidLoad oder dem Wasserhahn auf eine Taste aufgerufen:

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

Aber nicht verloren gehen in den Details meines Beispiels, sondern konzentrieren Sie sich auf die Nachricht von zu Hause, dass im Umgang mit asynchronen Methoden nicht versuchen, sofort Daten zurückzugeben oder Eigenschaften zu aktualisieren, sondern Daten über Schließungen zurückgeben. Und der ultimative Anrufer kümmert sich um die Aktualisierung des Modells und der Benutzeroberfläche.

Verwandte Themen