Reputation: 527
I need to update the content of a TableView from another class that execute an async call to an external API.
I implemented a protocol to refresh the table after the API call but seem that this code is never executed. I can't understand what is wrong with my code.. probably I forgot something, this is my first application and I just start studying protocols implementation
Below I wrote an example of my code where I remove all unnecessary code (for this question).
Protocol definition:
protocol ProductUpdateDelegate: class {
func refreshTableView()
}
Tabelview Implementation:
class TestTableViewController: UITableViewController,
UITextFieldDelegate, ProductUpdateDelegate {
var productList = [productInfo]()
// Delegate function implementation
func refreshTableView() {
self.tableView.reloadData()
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 50;
self.tableView.dataSource = self
self.tableView.delegate = self
more code ...
}
@IBAction private func buttonTappedAction(_ sender: Any) {
code that get input from user and did some check...
if(isValidCode){
self.storeProduct(productCode: code)
}
}
func storeProduct(productCode: String){
if(isNewCode){
self.productList.append(productInfo(code: productCode,
quantity: 1))
}
more code ...
}
Class that need to update some information on tableView
import Foundation
class Product {
weak var delegate: ProductUpdateDelegate?
some code here...
func getProductInfoFromAPI(){
API CAll...
if(result != "no_data")
self.delegate?.refreshTableView()
}
more code ...
}
class used to store informations about products and used to update products description using remote API
import Foundation
class productInfo: NSObject, NSCoding, Codable {
var code = ""
var quantity = 1
public var product_name = "Wait for information..."
init(code: String, quantity : Int,
product_name : String = "Wait for information...") {
self.code = code
self.quantity = quantity
self.product_name = product_name
super.init()
self.getProductInfoFromAPI()
}
required convenience init(coder aDecoder: NSCoder) {
let code = aDecoder.decodeObject(forKey: "code") as! String
let quantity = aDecoder.decodeInteger(forKey: "quantity")
self.init(code:code, quantity:quantity)
self.getProductInfoFromAPI()
}
func getProductInfoFromAPI(){
let productInfo = Product()
productInfo.getInfoByCode(productCode: code, refObject: self)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.code, forKey: "code")
aCoder.encode(self.quantity, forKey: "quantity")
}
}
UPDATE1: The product class is called from a some other classes (4 in total) that I not included here for sake of simplicity
The application workflow is:
Then there are some different options for the user to update the TableView content each option is controlled by a separate class, so the product class is called from some other classes and is the only one that interact directly with the TestTableViewController
UPDATE2: Add more code to show the complete process. After view initialization, the user can click on a button and can insert a product code.
The inserted code is saved in a array of productInfo using append. This action activate the init method of the productInfo class and a call to a remote service start to retrieve information about the product itself.
The problem is that the article information are update correctly but are not show on the tableview because the view is not updated. Because is an async process, i need a way to update the view content when the retrieving process has been completed. So i think that the best solution was the protocols. But as already stated, the function refreshTableView seem ignored.
Upvotes: 0
Views: 858
Reputation: 324
Based in your code, you're not setting any value to your weak variable named delegate in Product
class. You should set it to the class who should be using it.
One way is to make your Product
class as singleton in a way that it will only initialize once.
e.g.
class Product {
static let shared = Product()
weak var delegate: ProductUpdateDelegate?
private init() { }
func getProductInfoFromAPI(){
if(result != "no_data")
self.delegate?.refreshTableView()
}
}
And in you tableview controller instead of let productInfo = Product()
you can use let productInfo = Product.shared
then set your delegate.
productInfo.delegate = self
That's it!
Upvotes: 1
Reputation: 117
var refreshControl = UIRefreshControl()
@IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
refreshControl.attributedTitle = NSAttributedString(string: "")
refreshControl.addTarget(self, action: #selector(refresh(sender:)), for: UIControl.Event.valueChanged)
tblView.addSubview(refreshControl)
}
@objc func refresh(sender:AnyObject) {
refreshControl.endRefreshing()
apiFunction()
}
func apiFunction(){
// call your api
}
Upvotes: 2