I have created a UITableView
and want to show my results from the API to the cell in UITableView
. There is a cell class with the outlet of the label in it but no content is showing in the cell. The result is showing in. the console but not in cell label
I have tried to remove and Add new constraints.
I also added a button to reload the contents of the UITableView
but nothing is helping.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customCellTableViewCell
var result: String!
Alamofire.request(booksUrl, method: .get).responseJSON { (response) in
if response.result.isSuccess{
let bookJSON : JSON = JSON(response.result.value)
let tempResult = bookJSON["items"][0]["volumeInfo"]["title"].stringValue
result = tempResult
print(tempResult)
}
}
cell.bookNameLabel?.text = result
return cell
}
Please note that Alamofire (and most network requests) are asynchronous, which means that at the moment the request is dispatched, the result is unknown.
However, the datasource function of the tableview is a synchronous method that must return the cell immediately.
The end result is that when the cell is requested, the result is unknown and the cell is returned to the tableview to be displayed. Some time later the result from the network request is received, printed on screen but the cell is never updated.
The minimum amount of changes required to get your information to display is:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customCellTableViewCell
Alamofire.request(booksUrl, method: .get).responseJSON { (response) in
if response.result.isSuccess{
let bookJSON : JSON = JSON(response.result.value)
let tempResult = bookJSON["items"][0]["volumeInfo"]["title"].stringValue
print(tempResult)
cell.bookNameLabel?.text = tempResult
}
}
return cell
}
What this updated code does is use the "cell" variable instance within the Alamofire callback to update the cell label. Note that this is NOT the way it should be done, as if you start having many cells many of them will likely get reused and eventually your data would appear all over the place and get very confusing.
You probably want to take your network request outside of that function and perform your network request either within a custom UITableViewCell, making sure to cancel the network request if the cell is reused, or dispatched from the viewDidLoad function of your view controller issuing complete/partial tableView.reloadData().
Given that your API call seems to retrieve a list of books and each cell is displaying a single book title, I recommend doing the request once, storing all the book results into an array, then returning a number of cells equal to the number of books in your array.
On another note, you should strive to keep to a minimum the amount of different tasks performed within any function. So a function that is supposed to return a cell should return a cell and that's it... not perform network requests.
The recommended code structure would be closer to this:
var books: JSON?
override func viewDidLoad() {
super.viewDidLoad()
retrieveListOfBooks()
}
func retrieveListOfBooks() {
Alamofire.request(booksUrl, method: .get).responseJSON { (response) in
DispatchQueue.main.async {
if response.result.isSuccess{
self.books = JSON(response.result.value)
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return books?["items"].count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customCellTableViewCell
if indexPath.row < (books?["items"].count ?? 0) {
let book = books!["items"][indexPath.row]
let bookTitle = book["volumeInfo"]["title"].stringValue
cell.bookNameLabel?.text = bookTitle
}
return cell
}
WARNING: I haven't tested the code above, and you should probably also use Codable to represent your book data models.