I'm new to Swift. When the API requests are finished, I want to show them on the UI, but I don't know how to use these 3 functions (getMovieDeatails, loadImage, updateUI) in order. I can transfer the imdbID variable from the previous page without any problems and do both the api query and the image upload process without any problems. But I'm getting an error because I couldn't make the function order correctly when displaying it in the ui. The error I'm getting is:
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. 2022-10-25 03:43:53.276551 0300 Invio_Movie_World[9944:288248] [Invio_Movie_World] seek:413: *** IIOScanner::seek reached EOF Invio_Movie_World/DetailViewController.swift:51: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 2022-10-25 03:44:04.101415 0300 Invio_Movie_World[9944:287942] Invio_Movie_World/DetailViewController.swift:51: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value (lldb)
Could you please help?
This is DetailViewController.swift :
class DetailViewController: UIViewController {
//MARK: UI Elements
@IBOutlet var imageMovie: UIImageView!
@IBOutlet var titleLabel: UILabel!
@IBOutlet var yearLabel: UILabel!
@IBOutlet var releasedLabel: UILabel!
@IBOutlet var runtimeLabel: UILabel!
@IBOutlet var genreLabel: UILabel!
@IBOutlet var directorLabel: UILabel!
@IBOutlet var writerLabel: UILabel!
@IBOutlet var actorsLabel: UILabel!
@IBOutlet var plotLabel: UILabel!
@IBOutlet var languageLabel: UILabel!
@IBOutlet var countryLabel: UILabel!
@IBOutlet var awardsLabel: UILabel!
@IBOutlet var imdbRatingLabel: UILabel!
@IBOutlet var imdbIDLabel: UILabel!
var imdbID = ""
private var omdbApi = OmdbApi(apiKey: "31dd4179")
private var selectedMovie : MovieDetail!
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func getMovieDetails(completion : @escaping (_ movieDetail: MovieDetail) ->()) {
self.omdbApi.getMovieDetail(imdbID: self.imdbID) { (movieDetail) in
if let movieDetail = movieDetail {
completion(movieDetail)
self.selectedMovie = movieDetail
print("getmoviedetails ici \(self.selectedMovie!)")
}
}
}
func loadImage (completion : @escaping ()->()){
self.imageMovie.load(urlString: self.selectedMovie.poster)
}
func updateUI(){
getMovieDetails { (movieDetail) in
self.loadImage {
self.titleLabel.text = "\(self.selectedMovie.title)"
self.yearLabel.text = "Year : \(self.selectedMovie.year)"
self.releasedLabel.text = "Released : \(self.selectedMovie.released)"
self.runtimeLabel.text = "Runtime : \(self.selectedMovie.runtime)"
self.genreLabel.text = "Genre : \(self.selectedMovie.genre)"
self.directorLabel.text = "Director : \(self.selectedMovie.director)"
self.writerLabel.text = "Writer : \(self.selectedMovie.writer)"
self.actorsLabel.text = "Actors : \(self.selectedMovie.actors)"
self.plotLabel.text = "Plot : \(self.selectedMovie.plot)"
self.languageLabel.text = "Language : \(self.selectedMovie.language)"
self.countryLabel.text = "Country : \(self.selectedMovie.country)"
self.self.awardsLabel.text = "Awards : \(self.selectedMovie.awards)"
self.imdbRatingLabel.text = "IMDB Rating : \(self.selectedMovie.imdbRating)"
self.imdbIDLabel.text = "IMDB ID : \(self.selectedMovie.imdbID)"
}
}
}
}
This is the load extension in loadimage:
import Foundation
import UIKit
extension UIImageView {
func load(urlString : String) {
guard let url = URL(string: urlString)else {
return
}
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}
This is MovieDetail struct
import Foundation
struct MovieDetail: Codable {
let title, year, rated, released: String
let runtime, genre, director, writer: String
let actors, plot, language, country: String
let awards: String
let poster: String
let metascore, imdbRating, imdbVotes, imdbID: String
enum CodingKeys: String, CodingKey {
case title = "Title"
case year = "Year"
case rated = "Rated"
case released = "Released"
case runtime = "Runtime"
case genre = "Genre"
case director = "Director"
case writer = "Writer"
case actors = "Actors"
case plot = "Plot"
case language = "Language"
case country = "Country"
case awards = "Awards"
case poster = "Poster"
case metascore = "Metascore"
case imdbRating, imdbVotes, imdbID
}
}
CodePudding user response:
It looks to me like it is failing on:
self.imageMovie.load(urlString: self.selectedMovie.poster)
I would guess this is because it references self.selectedMovie
which is implicitly unwrapped. You call the completion for getMovieDetail
which calls loadImage
before you set selectedMovie
. If you flip the lines in getMovieDetail
like this it should fix this issue:
self.selectedMovie = movieDetail // do this first
completion(movieDetail) // then call this
I would recommend not implicitly unwrapping your data like this as it will let the compiler help you find these bugs easier (you'll get warnings/errors instead of crashes). I would also say that if you want to have nested layers of closures like this, you should pass the data directly as an argument instead of referencing properties on self. This will help make the flow of the logic a little clearer and ensure you're not working with out of date data. Something like this:
func getMovieDetails(completion : @escaping (_ movieDetail: MovieDetail) ->()) {
omdbApi.getMovieDetail(imdbID: self.imdbID) { (movieDetail) in
if let movieDetail = movieDetail {
self.selectedMovie = movieDetail
completion(movieDetail)
print("getmoviedetails ici \(self.selectedMovie!)")
}
}
}
func loadImage (for movieDetail: MovieDetail, completion : @escaping ()->Void) {
imageMovie.load(urlString: movieDetail.poster)
}
func updateUI(){
getMovieDetails { movieDetail in
self.loadImage(for: movieDetail) {
self.titleLabel.text = "\(movieDetail.title)"
self.yearLabel.text = "Year : \(movieDetail.year)"
self.releasedLabel.text = "Released : \(movieDetail.released)"
self.runtimeLabel.text = "Runtime : \(movieDetail.runtime)"
self.genreLabel.text = "Genre : \(movieDetail.genre)"
self.directorLabel.text = "Director : \(movieDetail.director)"
self.writerLabel.text = "Writer : \(movieDetail.writer)"
self.actorsLabel.text = "Actors : \(movieDetail.actors)"
self.plotLabel.text = "Plot : \(movieDetail.plot)"
self.languageLabel.text = "Language : \(movieDetail.language)"
self.countryLabel.text = "Country : \(movieDetail.country)"
self.self.awardsLabel.text = "Awards : \(movieDetail.awards)"
self.imdbRatingLabel.text = "IMDB Rating : \(movieDetail.imdbRating)"
self.imdbIDLabel.text = "IMDB ID : \(movieDetail.imdbID)"
}
}
}