0

Currently using Alamofire synchronously within cellForRowAtIndexPath that queries a JSON array from Heroku, and within a for loop, creates a struct from each JSON object within the JSON array with image and text properties and then appends each struct in an array property within the table view controller. Not surprising that this is really slow. On app launch, the initial VC is a container VC that either shows a navigation controller or page VC based on if the user is "logged in." The initial VC in the page VC is a container VC that holds the table VC in question.

I'm totally new to GCD and the concept of concurrency. Was wondering how I can populate my array that serves as the foundational data for each of the table view cells.

Here's my current code - changing some variable names because I signed an NDA for this project:

import UIKit
import Alamofire
import Alamofire_Synchronous

final class PopularPearsTableViewController: UITableViewController {

    let screenSize: CGRect = UIScreen.main.bounds

    var pears: [Pear] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(PopularPearTableViewCell.self, forCellReuseIdentifier: "popularPear")
        tableView.rowHeight = (screenSize.height) * 0.3
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

// MARK: - Table View Data Source
extension PopularShopsTableViewController {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // just a stub, will be replaced with dynamic code later on
        return 5
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print(#function)
        let cell = tableView.dequeueReusableCell(withIdentifier: "popularPear", for: indexPath) as! PopularPearTableViewCell
        let userDefaults = UserDefaults.standard
        guard let pearUUID = userDefaults.string(forKey: "pearUUID"),
            let pearToken = userDefaults.string(forKey: "pearToken")
        else {
            return cell
        }
        if indexPath.row == 0 {
            let header = createAuthenticatedHeader(user: pearUUID, password: pearToken)
            let pearResponse = Alamofire.request("url", headers: header).responseJSON()
            if let pearsFromResponse = (pearResponse.result.value! as! JSON)["data"] as? [JSON] {
                for pear in pearsFromResponse {
                    let name = pear["name"] as! String
                    let pictureURL = pear["picture_url"] as! String
                    let imageURL = URL(string: pictureURL)
                    let imageData = NSData(contentsOf: imageURL!)
                    let image = UIImage(data: imageData as! Data)!
                    let newPear = Pear(name: name, image: image)
                    self.pears.append(newPear)
                }
            }
        }
        cell.giveCell(pearImage: pears[indexPath.row].image, pearName: pears[indexPath.row].name)
        return cell
    }
}
srinitude
  • 43
  • 7
  • 3
    You need to load your data before loading the table view. `cellForRowAt` is the worst possible place to do any loading of data. – rmaddy Mar 15 '17 at 22:23
  • @maddy I tried doing it in `viewDidLoad`, `viewWillAppear`, `viewDidAppear`, and even in the container VC. In each case, `cellForRowAt` was called pretty quickly and got an index error. – srinitude Mar 15 '17 at 22:26
  • You should [edit] your question to include relevant code and details about any issues. – rmaddy Mar 15 '17 at 22:27
  • Possible duplicate of [AlamoFire asynchronous completionHandler for JSON request](http://stackoverflow.com/questions/29852431/alamofire-asynchronous-completionhandler-for-json-request) –  Mar 15 '17 at 22:27
  • @maddy Edited it! – srinitude Mar 15 '17 at 22:35
  • Your code is unreadable. At least replace the secret names with other, readable names. And post the code that doesn't load data in `cellForRowAt` since that it what you need to do. And why do you hard code the number of rows in the table view? Very, very bad. – rmaddy Mar 15 '17 at 22:45
  • @maddy Sorry about that! Replaced blanks and I'm hardcoding that for now because that's just a stub that I'll later be replacing. – srinitude Mar 15 '17 at 22:51
  • @Sneak Not the same question, edited it! – srinitude Mar 15 '17 at 23:09
  • Quite obviously, if you hard code the number of rows, you'll get index errors if you haven't loaded the data. Just load the data asynchronously (probably in `viewDidLoad`, possibly in `viewWillAppear`), and once you have received a response, trigger a reload of the table. And use the length of your array for the number of rows. Voilà, done. – jcaron Mar 15 '17 at 23:25
  • @jcaron It is actually the same. Since the question has been asked many many times, I can't flag all the duplicates. However, the post I flagged as duplicate shows you in perfect detail how to load data asynchronously without blocking. And as you have received the answers here in the comments by others, **after** you received the data response you reload the data into the cell. You don't need to wait for the data to load and block the entire UI. Imagine if you want to reload the data later with new values, you can run the same request again without any problems if you follow the guidance. –  Mar 16 '17 at 01:15

0 Answers0