I'm kinda new to iOS, was working on network fetching from the GitHub API but not able to show the users in the table view. Below is the code,
View Controller:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var avatarImage: UIImageView!
@IBOutlet weak var userName: UILabel!
@IBOutlet weak var usersTableView: UITableView!
var network = Network()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
network.delegate = self
usersTableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
network.network()
}
}
extension ViewController: NetworkDelegate {
func updateTableView() {
DispatchQueue.main.async {
self.usersTableView.reloadData()
}
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let users = network.users {
print(users.count)
return users.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("CALLED")
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UserViewCell
return cell
}
}
btw, the identifier is from the .xib file, the identifier matches, I don't think the problem is occurring here.
Network File
import Foundation
protocol NetworkDelegate {
func updateTableView()
}
class Network {
var users: [GitHub]?
var delegate: NetworkDelegate?
func network() {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let data = result {
// print(String(data: data, encoding: .utf8)!)
self.users = self.parseJSON(data)
self.delegate?.updateTableView()
} else {
print(error!.localizedDescription)
}
}
.resume()
}
}
private func parseJSON(_ data: Data) -> [GitHub]? {
let json = JSONDecoder()
do {
let decodedData = try json.decode([GitHub].self, from: data)
// print(decodedData)
return decodedData
} catch {
print(error.localizedDescription)
}
return nil
}
}
The GitHub API Model
struct GitHub: Codable {
let login: String
let id: Int
let node_id: String
let avatar_url: String
let gravatar_id: String
let url: String
let html_url: String
let followers_url: String
let following_url: String
let gists_url: String
let starred_url: String
let subscriptions_url: String
let organizations_url: String
let repos_url: String
let events_url: String
let received_events_url: String
let type: String
let site_admin: Bool
}
When I run this code on the simulator, the output is blank (Below the label)
Not able to figure out where I'm doing wrong
Thanks In Advance.
CodePudding user response:
Try to refactor you code using a completion handler without using the delegation pattern.
in your network file
:
enum ApiError: Error {
case network(Error)
case genericError
case httpResponseError
case invalidData
case decoding
// you can handle your specific case
}
func network(completion: @escaping ( _ error: ApiError?, _ users: [GitHub]?)-> Void) {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let error = error {
completion(.network(error), nil)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
completion( .httpResponseError, nil)
return
}
guard let data = result else {
completion(.invalidData, nil)
return
}
do {
let decodedData = try JSONDecoder().decode([GitHub].self, from: data)
completion(nil, decodedData)
} catch {
completion(.decoding, nil)
}
}
.resume()
}
}
then inside your ViewController you can use it this way:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
network.network { [weak self] error, users in
guard let self = self else { return }
if let error = error {
print(error)
return
}
DispatchQueue.main.async {
guard let users = users else { return }
self.users = users
self.tableView.reloadData()
}
}
}
if it still doesn't show and cellForRow
doesn't get called, you probably have a problem with your constraints and the tableView frame is zero (either height, width or both).
Try to debug setting a breakpoint inside numberOfRowsInSection
and then in your debug area po tableView
or just print the tableView and check if width or height is zero. it will be probably get called a few times. The first time the frame should be zero but at some point you should get a frame with height and width. If don't then check your constraints.
You can check my example which has a table view 375 x 641
CodePudding user response:
If your cell is a xib file then you have to register your cell with tableView.
before calling datasource in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
usersTableView(UINib(nibName: "userCell", bundle: nil), forCellReuseIdentifier: "userCell")
network.delegate = self
usersTableView.dataSource = self
}