2016-04-06 9 views
1

ich zur Zeit den Aufbau einer Wetter-Anwendung, wo ich einen Container ViewController Verwaltung mehrerer Kind ViewControllers, deren Hauptansichten leben im Inneren des Behälters ViewController ‚s UIScrollView haben.Swift NSURLSession: Probleme, wenn mehrere Anforderungen gleichzeitig laufen

Jedes untergeordnete VC verfügt über eine Standorteigenschaft. Dies ist die Klasse, die alle Wetterinformationen enthält. Diese Klasse hat die folgende Funktion für die Daten aus dem OpenWeatherMaps API Laden:

func refresh(completionHandler:() ->()) { 
    let session = NSURLSession.sharedSession() 
    session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in 
     if let data = responseData { 
      do { 
       let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) 
       // parse json 
       completionHandler() 
      } catch let err as NSError { 
       print(err.debugDescription) 
      } 
     } 
    }.resume() 
} 

Dann wird das Kind VC hat eine Funktion, die andere Funktion mit der completionHandler läuft auf dem Haupt-Thread aufrufen (weil ich die Benutzeroberfläche bin Aktualisierung):

func refreshWeatherData() { 
    location.refresh { 
     dispatch_async(dispatch_get_main_queue(), { 
      print("\(self.location.name)") 
      self.forecastCollectionView.reloadData() 
     }) 
    } 
} 

Und schließlich vom ContainerVC ich rufe die refreshWeatherData Funktion auf allen meiner LocationVCs.

override func viewDidLoad() { 
    super.viewDidLoad() 
    let zurich = WALocation(name: "Zürich", id: "7287650", country: "CH", longitude: 8.53071) 
    let shanghai = WALocation(name: "Shanghai", id: "1796236", country: "CN", longitude: 121.45) 
    let boston = WALocation(name: "Boston", id: "4183849", country: "US", longitude: -83.78) 
    let vancouver = WALocation(name: "Vancouver", id: "6173331", country: "CA", longitude: -123.11) 

    addLocationControllerForLocation(shanghai) 
    locationControllers[0].refreshWeatherData() 
    addLocationControllerForLocation(boston) 
    locationControllers[1].refreshWeatherData() 
    addLocationControllerForLocation(zurich) 
    locationControllers[2].refreshWeatherData() 
    addLocationControllerForLocation(vancouver) 
    locationControllers[3].refreshWeatherData() 
} 

Nun ist die Frage, die ich ist begegnen, dass manchmal (nicht immer), ein zwei- oder dreimal, die JSONSerialisation einen Fehler wirft (in der Refresh-Funktion des Ortes): Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo ``{NSDebugDescription=No value.} Wenn stattdessen in meinem ContainerVC I tun Sie dies:

override func viewDidLoad() { 
    super.viewDidLoad() 
    let zurich = WALocation(name: "Zürich", id: "7287650", country: "CH", longitude: 8.53071) 
    let shanghai = WALocation(name: "Shanghai", id: "1796236", country: "CN", longitude: 121.45) 
    let boston = WALocation(name: "Boston", id: "4183849", country: "US", longitude: -83.78) 
    let vancouver = WALocation(name: "Vancouver", id: "6173331", country: "CA", longitude: -123.11) 

    addLocationControllerForLocation(shanghai) 
    locationControllers[0].refreshWeatherData() 
    sleep(1) 
    addLocationControllerForLocation(boston) 
    locationControllers[1].refreshWeatherData() 
    sleep(1) 
    addLocationControllerForLocation(zurich) 
    locationControllers[2].refreshWeatherData() 
    sleep(1) 
    addLocationControllerForLocation(vancouver) 
    locationControllers[3].refreshWeatherData() 
} 

NSJSONSerialisierung schlägt nie fehl. Dies lässt mich glauben, dass es nur fehlschlägt, wenn mehrere Anforderungen asynchron ausgeführt werden.

Jede Hilfe würde sehr geschätzt werden.

+0

Wo (eigentlich ** wann **) speichern Sie die Wetterdaten? – vadian

+0

Ich speichere es, wenn ich es parse, wo ich den Kommentar in den Do-Block der Refresh-Funktion – Nico

Antwort

0

Das klingt nach einem Threading-Problem. Haben Sie versucht, die Auffrischungsanrufe in einen GCD-Aufruf einzuschließen (oder sich selbst zu aktualisieren)? z.B .:

func refresh(completionHandler:() ->()) { 
    dispatch_async(dispatch_get_main_queue()){ 
     let session = NSURLSession.sharedSession() 
     session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in 
      if let data = responseData { 
       do { 
        let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) 
        // parse json 
        completionHandler() 
       } catch let err as NSError { 
        print(err.debugDescription) 
       } 
      } 
      }.resume() 
    } 
} 

Wenn Notwendigkeit, sein Sie dispatch_sync nutzen könnten.

So oder so, stellen Sie sicher, dass die completionHandler zurückrufen, was auch immer es auf dem rechten Thread sein sollte. Wenn es ein UI-Update ist, dann ist es der Hauptthread.

bearbeiten Als Fortsetzung von meinem Beitrag unten, hier ist das Update für die Aktualisierungsdaten in den completionHandler zurückzukehren.

func refresh(completionHandler: (AnyObject) ->()) { //Anyobject being your JsonData or whatever so could be NSString, String, etc... 
    let session = NSURLSession.sharedSession() 
    session.dataTaskWithURL(self._apiUrl) { (responseData: NSData?, response:NSURLResponse?, error: NSError?) -> Void in 
     if let data = responseData { 
      do { 
       let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) 
       // parse json 
       completionHandler(json) 
      } catch let err as NSError { 
       print(err.debugDescription) 
      } 
     } 
    }.resume() 
} 
+0

setze, habe ich das schon probiert, und es hilft nicht. Der einzige Weg, wie ich es funktionierte, ist die Aktualisierung des nächsten Orts im CompletionHandler-Block des Standorts vor ... – Nico

+0

Sieht aus, als ob etwas mit dem 'self.location'-Aufruf zu tun hat. Wäre es nicht besser, Werte über den Completion-Handler zurück zu geben? Ich gebe ein Beispiel in meinem Antwort-Post. –

+0

mit diesem stellt sicher, dass was auch immer zurückgegeben wird, was auch immer gesendet wird, an den Complete-Handler ... Also gibt es keine Notwendigkeit, wirklich Threading zu kümmern –