2016-06-03 12 views
0

Ich mache eine App, wo Sie nach Filmen mit der API von http://www.omdbapi.com/ suchen können.Daten mit Anfrage Abschlusshandler nicht immer

Das Problem, das ich habe, ist mit dem Abschlusshandler von DataTaskWithRequest. Wenn Sie auf eine der collectionView-Zellen klicken, gelangen Sie zur Detailansicht des ausgewählten Films. Es funktioniert jedoch nicht die ganze Zeit. Ich erhalte eine Fehlermeldung, die besagt, dass beim Entpacken unerwarteterweise NULL gefunden wurde. Das liegt daran, dass es nicht in den Completion-Handler von dataTaskWithRequest geht, sondern direkt zum DetailVC geht und versucht, Daten im Titel-Label, Genre-Label usw. zu übergeben, aber es gibt keine Daten.

Ich hoffe, Sie wissen, was das Problem ist, weil ich es versucht habe und ich sehe nicht, was das Problem ist.

Oder tritt dieses Problem wegen etwas vor? Weil ich zuerst Daten von http://www.omdbapi.com/ mit "durch Suche" statt "nach ID" abrufe. Und von dort erhalte ich die ID und von dieser ID erhalte ich Daten für mein detailVC.

Hier ist mein Code:

// Go to detail view of selected movie 
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 

    let selectedMovieId = self.IDs[indexPath.row] 

    chosenMovieId = selectedMovieId 

    self.performSegueWithIdentifier("showDetail", sender: self) 
} 

// Preparations before going to the detail view of selected movie 
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 

    if segue.identifier == "showDetail" { 

     _ = self.movieInfoFrom(searchMovie: chosenMovieId, segue: segue) 
    } 
} 


func movieInfoFrom(searchMovie movieId: String, segue: UIStoryboardSegue) { 

    let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json" 
    let url = NSURL(string: movieUrlString) 
    print(movieUrlString) 

    let urlRequest = NSURLRequest(URL: url!) 
    let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) 

    let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in 

     if error == nil { 

      // Convert data to JSON 
      let swiftyJSON = JSON(data: data!) 

      let title = swiftyJSON["Title"].string! 
      let runTime = swiftyJSON["Runtime"].string! 
      let genre = swiftyJSON["Genre"].string! 
      let plot = swiftyJSON["Plot"].string! 
      let rating = swiftyJSON["imdbRating"].string! 
      let year = swiftyJSON["Year"].string! 
      let poster = swiftyJSON["Poster"].string 

      self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"] 
      print("\(self.infoResult)") 

      let destinationVC = segue.destinationViewController as! MovieDetailController 

      destinationVC.movieDetails = self.infoResult 
      destinationVC.moviePoster = poster 
     } 
    } 

    urlTask.resume() 
} 

Antwort

1

Ich habe versucht, den Code zu beheben und mit einigen Kommentaren erklären:

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 

    let selectedMovieId = self.IDs[indexPath.row] 

    chosenMovieId = selectedMovieId 

    self.movieInfoFrom(searchMovie: chosenMovieId) 
} 

// Preparations before going to the detail view of selected movie 
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 

    if segue.identifier == "showDetail" { 

     let destinationVC = segue.destinationViewController as! MovieDetailController 

      destinationVC.movieDetails = self.infoResult 
      destinationVC.moviePoster = poster 
    } 
} 


func movieInfoFrom(searchMovie movieId: String) { 

    let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json" 
    let url = NSURL(string: movieUrlString) 
    print(movieUrlString) 

    let urlRequest = NSURLRequest(URL: url!) 
    let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) 

    // This is asynchronously, you can put a loading here 
    let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in 
     // Got response, stop loading here 
     if error == nil { 

      // Convert data to JSON 
      let swiftyJSON = JSON(data: data!) 

      let title = swiftyJSON["Title"].string! 
      let runTime = swiftyJSON["Runtime"].string! 
      let genre = swiftyJSON["Genre"].string! 
      let plot = swiftyJSON["Plot"].string! 
      let rating = swiftyJSON["imdbRating"].string! 
      let year = swiftyJSON["Year"].string! 

      // You can save the poster as local variable 
      let poster = swiftyJSON["Poster"].string 

      self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"] 
      print("\(self.infoResult)") 

      // This should be call on main thread 
      dispatch_async(dispatch_get_main_queue()) { 
       self.performSegueWithIdentifier("showDetail", sender: self) 
      } 


     } 
    } 

    urlTask.resume() 
} 
+0

Vielen Dank für Eure Hilfe. Es funktionierte, indem einige Codes und Funktionen neu angeordnet wurden. Können Sie mir erklären, warum diese Lösung das Problem löst? Liegt es daran, dass ich perfomSegue zu früh anrufe? – Kelvin

+0

Das liegt daran, dass Ihre URL-Aufgabe asynchron ist, was bedeutet, dass sie sofort zurückkehrt und die Vorbereitung für den Übergang beendet ist, bevor Ihre Aufgabe beendet wurde. – Nati

+0

Ich verstehe nicht, was Sie genau meinen. Wenn ich in meinem Code vor allen Änderungen Haltepunkte verwende, kann ich sehen, dass es zur Funktion prepareForSegue geht. Und von dort geht es zur Funktion movieInfoFrom und wenn die URL-Aufgabe beendet ist, geht es in den Vervollständigungshandler und danach zeigt es die Detailansicht auf dem Simulator. Bedeutet dies nicht, dass prepareForSegue fertig ist, wenn es den Code in der Funktion movieInfoFrom gemacht hat? – Kelvin

0

diesen Code Versuchen Sie, mit sicheren optionals

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { 

    chosenMovieId = self.IDs[indexPath.row] 
    movieInfoFrom(searchMovie: chosenMovieId) 

} 

func movieInfoFrom(searchMovie movieId: String) { 

    let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json" 
    let url = NSURL(string: movieUrlString) 
    print(movieUrlString) 

    let urlRequest = NSURLRequest(URL: url!) 
    let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()) 

    let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in 

     if error == nil && data != nil { 

      // Convert data to JSON 
      let swiftyJSON = JSON(data: data!) 

      let title = swiftyJSON["Title"].string ?? "" 
      let runTime = swiftyJSON["Runtime"].string ?? "" 
      let genre = swiftyJSON["Genre"].string ?? "" 
      let plot = swiftyJSON["Plot"].string ?? "" 
      let rating = swiftyJSON["imdbRating"].string ?? "" 
      let year = swiftyJSON["Year"].string ?? "" 
      let poster = swiftyJSON["Poster"].string 

      self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"] 
      print("\(self.infoResult)") 

      dispatch_async(dispatch_get_main_queue()) { 
       self.performSegueWithIdentifier("showDetail", sender: poster) 
      } 
     } 
    } 
    urlTask.resume() 
} 

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 

    if segue.identifier == "showDetail", let destinationVC = segue.destinationViewController as? MovieDetailController { 

     destinationVC.movieDetails = self.infoResult 
     destinationVC.moviePoster = sender as? String 
    } 
} 
+0

Danke, dass du mir geholfen hast. Durch die Umordnung einiger Codezeilen und Funktionen funktionierte es. Können Sie mir erklären, warum diese Lösung das Problem löst? Ist es, weil ich performedSegue zu früh aufgerufen habe? – Kelvin