'How to wait for an API request to finish before storing data from function callback?

In a personal project of mine, I have created an API caller to retrieve a user's saved tracks from the Spotify API. The Spotify endpoint which I am using has a limit (maximum of 50 tracks per request) as well as an offset (starting index of first track in request), which is why I decided to use a FOR loop to get a series of track pages (each 50 tracks) and append them to a global array. The data is loaded from the main thread, and while the data is being requested, I display a child view controller with a spinner view. Once the data request has completed, I remove the spinner view, and transition to another view controller (passing the data as a property).

I have tried many things, but the array of tracks is always empty following the API request. I have a feeling it has to do with the synchronicity of my request, or maybe its possible that I'm not handling it correctly. Ideally, I would like to wait until the request from my API finishes, then append the result to the array. Do you have any suggestions on how I could solve this? Any help is much appreciated!

func createSpinnerView() {

    let loadViewController = LoadViewController.instantiateFromAppStoryboard(appStoryboard: .OrganizeScreen)
    add(asChildViewController: loadViewController)

    DispatchQueue.main.async { [weak self] in
        if (self?.dropdownButton.dropdownLabel.text == "My saved music") {
            self?.fetchSavedMusic() { tracksArray in
                self?.tracksArray = tracksArray
            }
        }
        ...
        self?.remove(asChildViewController: loadViewController)
        self?.navigateToFilterScreen(tracksArray: self!.tracksArray)
    }
}

private func fetchSavedMusic(completion: @escaping ([Tracks]) -> ()) {
    let limit = 50
    var offset = 0
    var total = 200
    for _ in stride(from: 0, to: total, by: limit) {
        getSavedTracks(limit: limit, offset: offset) { tracks in
            //total = tracks.total
            self.tracksArray.append(tracks)
        }
        print(offset, limit)
        offset = offset + 50
    }
    completion(tracksArray)
}

private func getSavedTracks(limit: Int, offset: Int, completion: @escaping (Tracks) -> ()) {
    APICaller.shared.getUsersSavedTracks(limit: limit, offset: offset) { (result) in
        switch result {
        case .success(let model):
            completion(model)
            print("success")
        case .failure(let error):
            print("Error retrieving saved tracks: \(error.localizedDescription)")
            print(error)
        }
    }
}

private func navigateToFilterScreen(tracksArray: [Tracks]) {
    let vc = FilterViewController.instantiateFromAppStoryboard(appStoryboard: .OrganizeScreen)
    vc.paginatedTracks = tracksArray
    show(vc, sender: self)
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source