0

Here is my async function class:

class MoviesViewModel: ObservableObject {
@Published var topRated: [Movie] = []
@Published var popular: [Movie] = []
@Published var upcoming: [Movie] = []

func getUpcomingMovies() {
    if let movies = getMovies(path: "upcoming") {
        DispatchQueue.main.async {
            self.upcoming = movies
        }
    }
}

func getPopularMovies() {
    if let movies = getMovies(path: "popular") {
        DispatchQueue.main.async {
            self.popular = movies
        }
    }
}

func getTopRatedMovies() {
    DispatchQueue.main.async {
        if let movies = self.getMovies(path: "top_rated") {
            self.topRated = movies
        }
    }
}

func getMovies(path: String) -> [Movie]? {
    var movies: [Movie]?
    let urlString = "https://api.themoviedb.org/3/movie/\(path)?api_key=\(apiKey)&language=en-US&page=1"
    
    guard let url = URL(string: urlString) else { return [] }

    let session = URLSession.shared
    
    let dataTask = session.dataTask(with: url, completionHandler: { data, _, error in
        if error != nil {
            print(error)
        }
        do {
            if let safeData = data {
                let decodedData = try JSONDecoder().decode(NowPlaying.self, from: safeData)
               
                DispatchQueue.main.async {
                    movies = decodedData.results
                }
            }
        }
        catch {
            print(error)
        }
        
    })
    dataTask.resume()
    return movies
}

}

When I printed the movies in getMovies function, I can get movies from api without problem. However, UI does not update itself. I used DispatchQueue.main.async function but it did not solve my problem. What can I do in this situation?

1
  • 1
    return movies runs way before movies = decodedData.results this is mixi g asynchronous actions with synchronous. Do research on completion handles and/or async await Commented Oct 12, 2022 at 14:20

1 Answer 1

1

dataTask works asynchronously. Your code returns nil even before the asynchronous task is going to start. You have to use a completion handler as described in Returning data from async call in Swift function.

I highly recommend to use async/await in this case. You get rid of a lot of boilerplate code and you don't need to care about dispatching threads.

@MainActor
class MoviesViewModel: ObservableObject {
    @Published var topRated: [Movie] = []
    @Published var popular: [Movie] = []
    @Published var upcoming: [Movie] = []
    
    func getUpcomingMovies() async throws {
        self.upcoming = try await getMovies(path: "upcoming")
    }
    
    func getPopularMovies() async throws  {
        self.popular = try await getMovies(path: "popular")
    }
    
    func getTopRatedMovies() async throws  {
        self.topRated = try await getMovies(path: "top_rated")
    }
    
    func getMovies(path: String) async throws -> [Movie] {
        let urlString = "https://api.themoviedb.org/3/movie/\(path)?api_key=\(apiKey)&language=en-US&page=1"
        
        guard let url = URL(string: urlString) else { throw URLError(.badURL) }
        
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode(NowPlaying.self, from: data).results
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.