1

I am developing a small app to connect to my site, download data via a PHP web service, and display it in a table view. To get started I was following a tutorial over on Medium by Jose Ortiz Costa (Article on Medium).

I tweaked his project and got it running to verify the Web service was working and able to get the data. Once I got that working, I started a new project and tried to pull in some of the code that I needed to do the networking and tried to get it to display in a tableview in the same scene instead of a popup scene like Jose's project.

This is where I am running into some issues, as I'm still rather new to the swift programming language (started a Udemy course and have been picking things up from that) getting it to display in the table view. I can see that the request is still being sent/received, but I cannot get it to appear in the table view (either using my custom XIB or a programmatically created cell). I thought I understood how the code was broken down, and even tried to convert it from a UITableViewController to a UITableviewDataSource via an extension of the Viewcontroller.

At this point, I'm pretty stumped and will continue to inspect the code and tweak what I think might be the root cause. Any pointers on how to fix would be really appreciated!

Main Storyboard Screenshot

Struct for decoding my data / Lead class:

import Foundation

struct Lead: Decodable {
    var id: Int
    var name: String
    var program: String
    var stage: String
    var lastAction: String
}

class LeadModel {

weak var delegate: Downloadable?
let networkModel = Network()

func downloadLeads(parameters: [String: Any], url: String) {
    
    let request = networkModel.request(parameters: parameters, url: url)
    
    networkModel.response(request: request) { (data) in
        let model = try! JSONDecoder().decode([Lead]?.self, from: data) as [Lead]?
        self.delegate?.didReceiveData(data: model! as [Lead])
    }
  }
}

ViewController:

import UIKit

class LeadViewController: UIViewController {

// Buttons
@IBOutlet weak var newButton: UIButton!
@IBOutlet weak var firstContactButton: UIButton!
@IBOutlet weak var secondContactButton: UIButton!

@IBOutlet weak var leadTable: UITableView!
let model = LeadModel()
var models: [Lead]?

override func viewDidLoad() {
    super.viewDidLoad()
    
    //Make Buttons rounded
    newButton.layer.cornerRadius = 10.0
    firstContactButton.layer.cornerRadius = 10.0
    secondContactButton.layer.cornerRadius = 10.0
    
    //Delegate
    model.delegate = self
}
    
    //Send request to web service based off Buttons Name
    @IBAction func findLeads(_ sender: UIButton) {
        let new = sender.titleLabel?.text
        let param = ["stage": new!]
        
        print ("findLead hit")
        model.downloadLeads(parameters: param, url: URLServices.leads)
    }

}

extension LeadViewController: UITableViewDataSource {

func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    print ("number of sections hit")
    return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    guard let _ = self.models else {
        return 0
    }
    print ("tableView 1 hit")
    return self.models!.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // Create an object from LeadCell
    let cell = tableView.dequeueReusableCell(withIdentifier: "leadID", for: indexPath) as! LeadCell
    
    // Lead selection
    cell.leadName.text = self.models![indexPath.row].name
    cell.actionName.text = self.models![indexPath.row].lastAction
    cell.stageName.text = self.models![indexPath.row].stage
    cell.progName.text = self.models![indexPath.row].program
    
    print ("tableView 2 hit")
    
    // Return the configured cell
    return cell
  }
}

extension LeadViewController: Downloadable {

func didReceiveData(data: Any) {
    
    //Assign the data and refresh the table's data
    DispatchQueue.main.async {
        self.models = data as? [Lead]
        self.leadTable.reloadData()
        
        print ("LeadViewController Downloadable Hit")
    }
  }
}

EDIT

So with a little searching around (okay...A LOT of searching around), I finally found a piece that said I had to set the class as the datasource.

leadTable.dataSource = self

So that ended up working (well after I added a prototype cell with the identifier used in my code). I have a custom XIB that isn't working right now and that's my next tackle point.

Omega636
  • 15
  • 4
  • Welcome to SO. Please *do not post screenshots* of your code - it is difficult to read. Paste the code in your question, properly formatted. See also [ask]. – koen Jul 07 '20 at 17:42
  • as @Alexander Gaidukov said, you need to become the `delegate` of your model object so you can be notified once the request is done. – Lucho Jul 07 '20 at 19:48

1 Answers1

0

You load the data, but don't use it. First, add the following statement to the end of the viewDidLoad method

model.delegate = self

Then add the following LeadViewController extension

extension LeadViewController: Downloadable {
    func dicReceiveData(data: [Lead]) {
        DispatchQueue.main.async {
            self.models = data
            self.tableView.reloadData()
        }
    }
}

And a couple of suggestions: It is not a good practice to use the button title as a network request parameter:

let new = sender.titleLabel?.text
let param = ["stage": new!]

It is better to separate UI and logic. You can use the tag attribute for buttons (you can configure it in the storyboard or programmatically) to check what button is tapped.

You also have several unnecessary type casts in the LeadModel class. You can change

let model = try! JSONDecoder().decode([Lead]?.self, from: data) as [Lead]?
self.delegate?.didReceiveData(data: model! as [Lead])

to

do {
    let model = try JSONDecoder().decode([Lead].self, from: data)
    self.delegate?.didReceiveData(data: model)
}
catch {}
  • Thank you for the input. I made the changes you mentioned for the logic, however it still doesn't show up in the table view. I dropped some print statements in to show when the functions were hit and it seems to not hit the last tableview function to return the cell. Also, should I change the implementation of Downloadable, because it was set to any and then I got an error saying it didn't conform to Downloadable – Omega636 Jul 08 '20 at 13:28
  • So I've spent the past two days trying to figure out why it doesn't seem to be hitting the functions @Alexander Do you know why it might not be displaying? – Omega636 Jul 10 '20 at 14:19
  • @Omega636 it is hard to say from the code you provided. If you provide the link to your project on GitHub I can have a look. – Alexander Gaidukov Jul 10 '20 at 15:54