Marco
Marco

Reputation: 527

Swift using delegate tableview to refresh content from another class

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

Answers (2)

Sky Shadow
Sky Shadow

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

Mohan Carpenter
Mohan Carpenter

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

Related Questions