Swift Async call not updating data right away

Viewed 59

I am struggling to get the code below to work in my xcode project. The first half of the code doesn't actually populate my code until around the cellforrowat function (not in the snippet below). Because of this the second half of the code snipit doesnt even run (because the movie array is still empty). So I think my problem is that DispatchQueue.main.async {self.tableView.reloadData()} does not update my tableview right away.

var movies: [MovieDT] = []
var db: Firestore
var uid: String = ""

init(genre:Int, categoryNum:[Int], uid:String){
    db = Firestore.firestore()
    super.init(nibName: nil, bundle: nil)
    
    self.uid = uid
    TMDBConfig.apikey = "xxx"
    
    GenresMDB.genre_movies(genreId: categoryNum[genre], include_adult_movies: true, language: "en") { [weak self] apiReturn, movieList in
        guard let self = self else {return}
        
        if let movies = movieList
        {
            for movie in movies
            {
                self.movies.append(MovieDT(title: movie.title ?? "Missing Title", description: movie.overview ?? " ", releaseDate: movie.release_date ?? " ", stars: movie.vote_average ?? 0, id: movie.id ?? 0))
            }
        }
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    
    //****************************************************************
    //code below does not work because the movies aren't generated yet
    //****************************************************************
    
    for movie in self.movies
    {
        self.db.collection("Movies").document(String(movie.id)).getDocument() { [weak self] (querySnapshot, err) in
        guard let self = self else {return}
            if let err = err {
                    print("Error getting documents: \(err)")
                } else {
                    guard let snap = querySnapshot else {return}
                    let data = snap.data()
                    print(data?["upvoteCount"] as? Int ?? 0)
                    print(data?["downvoteCount"] as? Int ?? 0)
                    print("jhsjsj")
                }
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    } 
}
1 Answers

Since GenresMDB.genre_moves is an asynchronous function, the callback will execute on a separate thread. Meanwhile, init continues executing on the main thread. The API call takes time to complete, so your for loop executes before you've had a chance to update movies. If you add a print right after the guard and another one right before for movie in self.movies, I suspect you'll see the first print after the second one, even though you wrote the first one first.

So, the solution is just to move your for loop into the completion block. I would merge it with your for movie in movies loop. The trick is to make your code execute synchronously and deterministically, to ensure that it will certainly run in the order that you hope.

Related