Swift 4, Json data doesn't appear in cells - json

I can't put data to the cells, I searched in different tutorials and it should work , I checked in debug area and data are downloaded but doesn't exist in cells, I tried also with custom cells but it doesn't work too. I have not got any error message, simply empty cells. Do you know maybe what can cause this issue? I spended much time for searching solution but I can't find anything, on every tutorial people do this similar to me.
struct Country: Decodable {
let name: String
}
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var countries = [Country]()
var liczba = Int()
#IBOutlet var tv: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
json() {
self.tv.reloadData()
}
tv.dataSource = self
tv.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return liczba
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = countries[indexPath.row].name
return cell
}
func json (completed: #escaping()->()) {
let jsonUrl = "https://restcountries.eu/rest/v2/all"
let url = URL(string: jsonUrl)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.countries = try JSONDecoder().decode([Country].self, from: data!)
let numer = self.countries.count
self.liczba = numer
}
catch {
print("error")
}
}.resume()
}
}

It seems that you are having a problem with the sequentiality of your code. You are calling reloadData on your tableview but at that moment you don't have set the datasource yet.
try this:
tv.dataSource = self
tv.delegate = self
json() {
self.tv.reloadData()
}

You are never calling the completed closure of your json method, so self.tv.reloadData() will never be executed.
Add completed() after self.liczba = numer.

Since you are not returning anything from the closure I recommend to delete the completion handler and reload the table view on the main thread within the closure.
And don't forget to handle a potential error
func json() {
let jsonUrl = "https://restcountries.eu/rest/v2/all"
let url = URL(string: jsonUrl)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
if let error = error { print(error); return }
do {
self.countries = try JSONDecoder().decode([Country].self, from: data!)
self.liczba = self.countries.count
DispatchQueue.main.async {
self.tv.reloadData()
}
}
catch {
print("error")
}
}.resume()
}
and call the method in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
tv.dataSource = self // I'd connect datasource and delegate in Interface Builder
tv.delegate = self
json()
}

Related

UITableView returning nil when trying to call reloadData()

I am trying to get my table view to show json data from the news api. I've been able to parse the data and display it to the console but a nil value was caught in the self.tableview.reload(). I need help in resolving the issue
let urlRequest = "https://newsapi.org/v2/everythingq=Coronavirus&sortBy=publishedAt&apiKey"
var articles: [Articles]? = []
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
retriveData()
}
func retriveData(){
guard let aritcleUrl = URL(string: urlRequest) else {
return
}
let request = URLRequest(url: aritcleUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error ?? 0)
return
}
if let data = data {
self.articles = self.parseData(data: data)
// Reload table view
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
})
task.resume()
}
func parseData(data:Data)-> [Articles] {
var articles: [Articles]? = []
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonArticles = jsonResult?["articles"] as? [AnyObject] ?? []
for jsonArticle in jsonArticles{
let article = Articles()
article.author = jsonArticle["author"] as? String
article.title = jsonArticle["title"] as? String
article.publishedAt = jsonArticle["publishedAt"] as? String
articles?.append(article)
}
print(jsonArticles)
} catch {
print(error)
}
return articles ?? []
}
It seems that your class doesn't conform to table view's protocols.
You should edit your declaration to:
class <YourViewController>: UIViewController, UITableViewDelegate, UITableViewDataSource {
then in the viewDidLoad you should set the delegate and datasource to your tableview
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
retriveData()
}
Then you have to add the protocol stubs as suggested by xcode
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.articles?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// YOUR CELL CUSTOMIZATION GOES HERE
}
However I suggest you to look at any TableView tutorial on Internet, such as the Apple's official one
here

Can't get data from rest API to load on ViewDidLoad

I'm new to swift and I can't get my head around this issue.
I can successfully make JSON fetch from an API, but data on the Table View only load after I click a button. Calling the same function on the viewDidLoad didn't load the data when the app is opening.
I tried lot of solutions, but I can't find where the fault is
here's the main view controller code:
import UIKit
struct ExpenseItem: Decodable {
let name: String
let amount: String
let id: String
let timestamp: String
let description: String
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBAction func fetchData(_ sender: Any) {
ds()
self.eTW.reloadData()
}
#IBOutlet weak var eTW: UITableView!
var allExpenses = [ExpenseItem]()
let expensesURL = "https://temp.cuttons.com/json/expenses.json"
override func viewDidLoad() {
super.viewDidLoad()
eTW.delegate = self
eTW.dataSource = self
ds()
self.eTW.reloadData()
}
func parseJSON(data: Data) -> [ExpenseItem] {
var e = [ExpenseItem]()
let decoder = JSONDecoder()
do {
e = try decoder.decode([ExpenseItem].self, from: data)
} catch {
print(error.localizedDescription)
}
return e
}
func ds() {
if let url = URL(string: expensesURL){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, session, error) in
if error != nil {
print("some error happened")
} else {
if let content = data {
self.allExpenses = self.parseJSON(data: content)
}
}
}
task.resume()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.allExpenses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = eTW.dequeueReusableCell(withIdentifier: "expense", for: indexPath) as! myCellTableViewCell
cell.name.text = self.allExpenses[indexPath.row].name
if let am = Double(self.allExpenses[indexPath.row].amount) {
if (am > 0) {
cell.amount.textColor = .green
} else {
cell.amount.textColor = .red
}
}
cell.amount.text = self.allExpenses[indexPath.row].amount
return cell
}
thanks
L.
You have to reload the table view after receiving the data asynchronously.
Remove the line in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
eTW.delegate = self
eTW.dataSource = self
ds()
}
and add it in ds
func ds() {
if let url = URL(string: expensesURL) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if let error = error {
print("some error happened", error)
} else {
self.allExpenses = self.parseJSON(data: data!)
DispatchQueue.main.async {
self.eTW.reloadData()
}
}
}
task.resume()
}
}
Do not reload data after the api call in viewDidLoad(). Do it inside of the completion block after you parse the JSON into the object you need (assuming it's successful like you said).
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, session, error) in
guard error == nil else {
print("some error happened")
return
}
guard let data = data else {
print("bad JSON")
return
}
self.allExpenses = self.parseJSON(data: data)
DispatchQueue.main.async {
self.eTW.reloadData()
}
}
task.resume()
Also, use guard let instead of if let if possible.

Fill a tableview from JSON with Swift

I'm a total swift novice and even the simplest things I can do. For example, I would like to show in a UItableview a list of names that come from a JSON file, but I am not able to do so.
Specifically, my JSON returns the following:
[{"id_centro":"3","0":"3","nombre":"Colegio Vistarreal","1":"Colegio Vistarreal"},{"id_centro":"1","0":"1","nombre":"IES ITC Sistemas","1":"IES ITC Sistemas"}]
And I have implemented my UITableViewController in the following way:
import UIKit
class TCentrosController: UITableViewController {
var centrosList = [String] ()
override func viewDidLoad() {
super.viewDidLoad()
Dologin()
}
func Dologin(){
let url = URL (string: "https://neton.es/WS_neton/lista_centros.php")
let session = URLSession.shared
let request = NSMutableURLRequest (url: url!)
request.httpMethod = "POST"
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data else{
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [[String:Any]]
if json.isEmpty{
DispatchQueue.main.async {
let alertController = UIAlertController(title: "Atención", message: "No hay centros disponibles", preferredStyle: .alert)
let yesAction = UIAlertAction(title: "OK", style: .default)
{ (action ) -> Void in
print("Centros no operativos")
}
alertController.addAction(yesAction)
self.present(alertController, animated: true, completion: nil)
}
return
//Si el json no está vacío, sigue por aquí
}else{
let nombreCentro:String = json[1]["nombre"] as! String
self.centrosList.append(nombreCentro)
}
}
catch{
print("Error")
}
})
task.resume()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return centrosList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "centros_lista", for: indexPath) as! CeldaCentrosTableViewCell
cell.labelCell.text = centrosList[indexPath.row]
return cell
}
}
But it does not return anything to me by screen. In fact, if I do a print (centrosList.count) it tells me that zero for five times.
As you can see, I do not have much idea and I sure have more than one fault that I am not able to see. By the way, I'm programming with swift 3
Thanks, any help is appreciated
It seems that you are not reloading the data to tableView on received it from server.
The general steps to be followed is
set delegate, dataSource for the tableView.
Implement all the required delegate and dataSource methods.
fetch the data from the server and store it to array
reload the tableView.
So, you should add this line below
self.centrosList.append(nombreCentro)
Add this
DispatchQueue.main.async {
self.tableView.reloadData()
}
Try and share the results.

TableView not populating data, but JSON file is being processed

I am having a problem getting my tableview to populate from my hosted JSON file. I've confirmed the app is successfully seeing the data within the JSON file, but the table itself is still blank (and oddly, shows two different vertical heights for some of the rows).
Here is my ViewController.swift:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
// var heroes = [HeroStats]()
var bonuses = [JsonFile.JsonBonuses]()
override func viewDidLoad() {
super.viewDidLoad()
downloadJSON {
self.tableView.reloadData()
}
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = bonuses[indexPath.row].name.capitalized
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? HeroViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
// MARK: - Download JSON from ToH webserver
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data!)
DispatchQueue.main.async {
completed()
}
print(posts.bonuses.map {$0.bonusCode})
} catch {
print("JSON Download Failed")
}
}
}.resume()
}
}
And here is what the JsonFile.swift file looks like:
import Foundation
struct JsonFile: Codable {
struct Meta: Codable {
let fileName: String
let version: String
}
struct JsonBonuses: Codable {
let bonusCode: String
let category: String
let name: String
let value: Int
let city: String
let state: String
let flavor: String
let imageName: String
}
let meta: Meta
let bonuses: [JsonBonuses]
}
That print within the tableView numberOfSections displays 0, and I've noticed I see that printed three times, then I see the print of the codes indicating the JSON was read, then I see the "Found 0 rows in section" print again.
What am I missing here?
In the datasource method you are reading from bonuses array. But when you are done downloading the posts you aren't assigning the bonuses of the post to your bonuses array.
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")
URLSession.shared.dataTask(with: url!) { [weak self] (data, response, error) in
if error == nil {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data!)
DispatchQueue.main.async {
completed()
}
print(posts.bonuses.map {$0.bonusCode})
// Here you need to assign the bonuses from your posts to your bonuses array
// Pay attention to the [weak self] that is added in the function call
self?.bonuses = ... // do anything that converts to bonuses
} catch {
print("JSON Download Failed")
}
}
}.resume()
}

swift 3.0: tableview cannot show data which received as JSON through web api

i started to learn IOS development using swift 3.0.i built a simple app to call web api to query data from server database. i can get the json data and parsed it into string array. the App can print the array, but it cannot show in the tableview. it confused me several days and i searched some examples and answers on internet but still couldn't work out it.
My codes as below:
class LocationTableViewController: UITableViewController {
var names: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
//——————————————————————————get the data from web api and using json parsing————————————————————————
let config = URLSessionConfiguration.default // Session Configuration
let session = URLSession(configuration: config) // Load configuration into Session
let url = URL(string: "http://XXXXXXX/api/mylocations")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
do {
var jsonResult: NSMutableArray = NSMutableArray()
let jsonArray = try JSONSerialization.jsonObject(with: data!, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
jsonResult = jsonArray.mutableCopy() as! NSMutableArray
var jsonElement: NSDictionary = NSDictionary()
for i in 0..<jsonResult.count {
jsonElement = jsonResult[i] as! NSDictionary
if let name = jsonElement["Name"] as? String
{
// print(id)
// print(name)
// print(address)
// print(latitude)
// print(longitude)
// print("-------")
self.names.append(name)
}
// self.tableView.reloadData()
// print(self.names)
}
print(self.names)
// can print the string array data like [“name1”,”name2”,”name3”]
} catch {
print("error in JSONSerialization")
}
}
})
task.resume()
//-------------- ——— result is [] it seems the above code didn't put the string array to names.——————————————
print(self.names)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count;
}
internal override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for:
indexPath as IndexPath) as UITableViewCell
// Configure the cell...
cell.textLabel?.text = names[indexPath.row]
return cell
}
}
Can anyone help me have a look?
Put self.tableView.reloadData() after print print(self.names).
At the point where you have commented...
result is [] it seems the above code didn't put the string array to
names
This line of code is being executed before the data has been downloaded within the completion handler, so we wouldn't expect to see anything here. You will note that it is working on the other print that you have within the completion handler.
The tableView.reloadData() at the end of the completion handler should be working.
Are you sure that you have the delegates set up correctly for the tableView? What do you see if you comment out the download task, and simply set
names = ["Tom", "Dick", "Harry"]
within viewDidLoad ? If that doesn't work, it's a problem with the delegates.