alamofireswifty-jsonswift3.2

UIView got rendered before data from API returned using SwiftyJSON and Alamofire


I want to fetch data using API request.The data is fetch using SwiftyJson and Alamofire. The problem is that the data is fetched but view gets loaded before the values are fetched.How can I Solve the problem?My code is as below:

func fetchData(){
    Alamofire.request(favUrl, method: .get, parameters: [:]).responseJSON {
        response in
        if response.result.isSuccess{

            let dataFetched : JSON = JSON(response.result.value!)
            //print(dataFetched)
            let titleDisp = dataFetched["title"].arrayObject as? [String]
            //print(titleDisp)
            self.trackList = dataFetched["track_id"].arrayObject as? [String]

            print(self.trackList)

        }else{
            print("Error \(String(describing: response.result.error))")

        }
    }
}


override func viewDidLoad() {
    super.viewDidLoad()
    fetchData()
 }

Solution

  • It is important to understand that apps run multiple threads. The main thread, also called the UI thread, performs the actions for the visible parts of the app, including showing views, reacting to buttons and so on.

    You cannot block the Main thread.

    When you make calls to external resources like API calls or loading external images, those get executed on a separate thread. That thread is independent of the main thread. Neither thread waits for the other. The app will still react to buttons while the data is loading. What you are asking for is to prevent showing the view until the data is loaded. You can do this, but you must understand that this could take time depending on your network connection. There are two approaches you can take.

    1. Transition to the view that shows the data but put a "Loading" element on the screen until the data loads then remove the "Loading" element then redisplay the view with the data.

    2. Load the data before you show the view. Make the previous view load the data then segue to the view that has to show the data.

    You must also decide if this data loads ONCE or every time the view is displayed. By placing the call in viewDidLoad, the API call will only happen once until the app is restarted. If you want the data to load every time the screen is shown, put the call in viewWillAppear.

    // Approach #1
    func fetchData(){
    
        self.showSpinner() 
    
        Alamofire.request(favUrl, method: .get, parameters: [:]).responseJSON {
            response in
            self.hideSpinner() 
            if response.result.isSuccess {
    
                let dataFetched : JSON = JSON(response.result.value!)
                //print(dataFetched)
                let titleDisp = dataFetched["title"].arrayObject as? [String]
                //print(titleDisp)
                self.trackList = dataFetched["track_id"].arrayObject as? [String]
    
                print(self.trackList)
    
                // Actually update the relevant screen elements here.
    
            } else {
                print("Error \(String(describing: response.result.error))")
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //fetchData()
    }
    
    override func viewWillAppear() {
        super.viewWillAppear()
        fetchData()
    }
    
    func showSpinner() {
        // IMPLEMENT ME
    }
    func hideSpinner() {
        // IMPLEMENT ME
    }