Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Call async func in another function and update UI

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

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
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading