var find: MovieMDB?
SearchMDB.movie(query: (movie?.title!.components(separatedBy: "[").first)!, language: "en", page: 1, includeAdult: true, year: nil, primaryReleaseYear: nil, completion: {
data, movies in
guard movies != nil else { return }
find = movies?[0] <-- Thread 1: Fatal error: Index out of range
})
if find != nil {
print(find!.title)
print(find!.overview)
}
Assuming movies
is an array:
let indexWeCareAbout = 0
guard let unwrappedMovies = movies, // Cancels if nil.
unwrappedMovies.indices.contains(indexWeCareAbout) else { // Cancels if the index is out of bounds.
return
}
find = unwrappedMovies[indexWeCareAbout]
To clarify, 0 would be out of bounds if the array is empty.
If you will only ever care about index 0, you could do this instead:
find = movies?.first
It is working perfect, thank you very much!!
but for some reason I still cannot manage to archive my goal.
override func viewDidLoad() {
super.viewDidLoad()
var find: MovieMDB?
SearchMDB.movie(query: (movie?.title!.components(separatedBy: "[").first)!, language: "en", page: 1, includeAdult: true, year: nil, primaryReleaseYear: nil){
data, movies in
let indexWeCareAbout = 0
guard let unwrappedMovies = movies, // Cancels if nil.
unwrappedMovies.indices.contains(indexWeCareAbout) else { // Cancels if the index is out of bounds.
return
}
find = movies?.first
print(find!.title) <- prints well if I use here
print(find!.overview) <- prints well if I use here
}
print(find!.title) <- Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value (If I use it here)
print(find!.overview)
}
I suspect SearchMDB.movie(query: ...)
leaves the main thread and performs its query in the background.
That means when you call it, it starts, but the code does not wait for it to finish, instead continuing to the next line. You can try swapping both print statements for simple strings (print("Running closure...")
and print("Continuing on...")
) so that you can see if that is happening. If so, then the latter lines are asking for the search result before the search in the closure has finished, which is why it finds nil
.
The fix will be to either do your logic inside the closure, or put it in a separate method somewhere that the closure can call. You wonāt be able to use the result in viewDidLoad()
. While you could technically force it to stall and wait for it to complete before continuing, you probably donāt want to freeze the interface while you wait for the response. You will also want to check the documentation for SearchMDB
to see whether it runs the closure on the main thread, or if you have to reāenter the main thread yourself.
You are entering advanced territory though, so it would probably be good to find yourself a tutorial on concurrency if you arenāt already familiar with it.
If you only ever care about the first movie in the collection, you can unwrap first
inside the guard, removing the messy index checks and removing the ?
and !
in the later lines as it will be already unwrapped. The body of the closure can therefore be simplified to:
guard let movie = movies?.first else { return } // if we have at least one movie, continue by setting the variable `movie` to the element at index 0 (the `first` one), else return and escape the scope
print(movie.title) // `movie` is now unwrapped so we do not need to use the ! operator anymore
print(movie.overview)