Search code examples
swiftapinetworking

Swift return data from 2 URLSessions


I want to retrieve top artists and tracks data using Spotify's APIs. Here is my code for getting top artists data:

func getTopArtistsData(accessToken: String, timeRange: String, completionHandler: @escaping (TopArtistsData?, Error?) -> Void){
        var request = URLRequest(url: URL(string: "https://api.spotify.com/v1/me/top/artists?time_range=\(timeRange)")!)
        request.httpMethod = "GET"
        request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")

        URLSession.shared.dataTask(with: request) { (data, response, error) in
            let result = self.parseJSONTopArtists(topData: data!)
            completionHandler(result, nil)
        
        }.resume()
    }

But I want to do a second request with api.spotify.com/v1/me/top/tracks in the same func, so it the completionHandler will return both artists and tracks data. How to do this?


Solution

  • You have several options here.

    1. Modern technologies -> async/await
    2. Good old GCD -> DispatchGroup as suggested by @Larme in comments to your question. (parallel running)
    3. Chain requests in dirty way inside getTopArtistsData. (serial running)

    To not overcomplicate GCD should work fine here.

    func getTopArtistsData(accessToken: String, timeRange: String, completionHandler: @escaping (TopArtistsData?, TopTracksData?) -> Void) {
    
        var artistsRequest = URLRequest(url: URL(string: "https://api.spotify.com/v1/me/top/artists?time_range=\(timeRange)")!)
        artistsRequest.httpMethod = "GET"
        artistsRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
        var tracksRequest = URLRequest(url: URL(string: "https://api.spotify.com/v1/me/top/tracks")!)
        tracksRequest.httpMethod = "GET"
        tracksRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
        let group = DispatchGroup()
    
        var topArtists: TopArtistsData?
        var topTracks: TopTracksData?
    
        group.enter()
        URLSession.shared.dataTask(with: artistsRequest) { (data, response, error) in
            topArtists = self.parseJSONTopArtists(topData: data!)
            group.leave()
        }.resume()
    
        group.enter()
        URLSession.shared.dataTask(with: tracksRequest) { (data, response, error) in
            topTracks = self.parseJSONTopTracks(topData: data!)
            group.leave()
        }.resume()
    
        group.notify(queue: DispatchQueue.main) {
            completionHandler(topArtists, topTracks)
        }
    }
    

    Please note this is very rough example of what you need, but it should do the trick.

    There are still a lot of things to take into consideration like eliminate force unwrapping, do not capture self strongly in network requests, error handling and so on.

    PS: URLSession data task completion block with (data, response, error) is not called on main thread, so attempt to update UI will lead to problems. In my example DispatchGroup will execute it's block on main thread, so we are safe here.