Search code examples
iosswiftuitableviewalamofire

By Alamofire, would like to get data and insert it to TableView, but before completing of inserting, TableView is loaded


As I'm writing at the title, I'm using Alamofire to get data and then decode it and append it to the list "articleList" and then try to insert it to TableView, but it seems that TableView is loaded at first, and then the data is collected and inserted to the list. I would like to make the insertion first, then loading TableView but I cannot find a solution. I just tried it by putting defer into viewDidLoad and make tableView.realodData, but it doesn't work... Could anybody give me any idea for this situation?

import UIKit
import Alamofire

class NewsViewController: UITableViewController {
    var urlForApi = "https://newsapi.org/v2/top-headlines?country=jp&category=technology&apiKey=..."

    var articleList = [Article]()

    override func viewDidLoad() {
        super.viewDidLoad()
        updateNewsData()
        defer {
            tableView.reloadData()
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print(articleList.count)
        return articleList.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = articleList[indexPath.row].title

        return cell
    }

    //    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    //
    //    }

    func updateNewsData() {
        getNewsData() { (articles) in
            guard let articleArray = articles?.articles else
            {fatalError("cannot get articles.")}

            for article in articleArray {
                self.articleList.append(article)
            }

            print("insert is done")
        }
    }

    func getNewsData(completion: @escaping (ArticlesListResult?) -> Void) {
        Alamofire.request(urlForApi, method: .get)
            .responseJSON { response in
                if response.result.isSuccess {
                    if let data = response.data {
                        let articles = try? JSONDecoder().decode(ArticlesListResult.self, from: data)
                        completion(articles)
                    }
                } else {
                    print("Error: \(String(describing: response.result.error))")
                }
        }
    }
}

Solution

  • Code inside defer is executed after "return" of method or for example after return of current cycle of some loop

    Since viewDidLoad returns Void, this method returns immediately after updateNewsData() is called and it doesn't wait then another method which was called from inside returns or after code inside some closure is executed (defer isn't executed after some closure is executed since inside closure you can't return value for method where closure was declared).

    To fix your code, just reload table view data after you append articles

    for article in articleArray {
        self.articleList.append(article)
    }
    tableView.reloadData()