How to display JSON data from API in TableView? - ios

I recieves json data from api and totally confused what should I do next to pass this data to custom cell with imageView and labels in order to update UI in tableView.
Getting JSON
import Foundation
struct Breed: Codable {
let name: String?
let origin: String?
let life_span:String?
let temperament: String?
let description: String?
let wikipedia_url: String?
let image: Image?
}
struct Image: Codable {
let url: String?
}
func getDataFromCatsApi() {
let url = URL(string: "https://api.thecatapi.com/v1/breeds")
let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
let decoder = JSONDecoder()
if let data = data {
let breed = try? decoder.decode([Breed].self, from: data)
print (breed as Any)
} else {
print (error as Any)
}
}
task.resume()
}
All data is printed correctly.
In ViewController I have a tableView with custom cell.
import UIKit
class MainVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
title = "Cats"
view.backgroundColor = .systemBackground
getDataFromCatsApi()
}
}
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as? CustomTableViewCell
return cell ?? CustomTableViewCell()
}
}
Class for custom cell. Here I have imageView and labels for displaying data from json.
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var catImageView: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var originLabel: UILabel!
#IBOutlet weak var addToFavButton: UIButton!
}

First of all, you are not returning anything from getDataFromCatsApi(). Since it is an asynchronous call, you have to implement a way to get the values either by using a callback or a delegate. In this case callback would suffice.
Then once you receive a value from the api call, set those values in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) in which you can use cell.nameLabel.text = <received value> and etc.

First of all declare only those properties as optional which can be nil
struct Breed: Decodable {
let name, origin, lifeSpan, temperament, description: String
let wikipediaUrl: String?
let image: Image?
}
struct Image: Decodable {
let url: String?
}
In getDataFromCatsApi add a completion handler
func getDataFromCatsApi(completion: #escaping (Result<[Breed],Error>) -> Void ) {
let url = URL(string: "https://api.thecatapi.com/v1/breeds")
let task = URLSession.shared.dataTask(with: url!) { data, _ , error in
if let error = error { completion(.failure(error)); return }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
completion(Result { try decoder.decode([Breed].self, from: data!) })
}
task.resume()
}
In MainVC declare a data source array
var cats = [Breed]()
Replace viewDidLoad with
override func viewDidLoad() {
super.viewDidLoad()
title = "Cats"
view.backgroundColor = .systemBackground
getDataFromCatsApi {[unowned self] result in
DispatchQueue.main.async {
switch result {
case .success(let breed):
self.cats = breed
self.tableView.reloadData()
case .failure(let error): print(error)
}
}
}
}
and the table view datasources methods with
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cats.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as! CustomTableViewCell
let cat = cats[indexPath.row]
cell.nameLabel = cat.name
cell.originLabel = cat.origin
}
}
To load the pictures is beyond the scope of the question. There are libraries like SDWebImage or Kingfisher to load and cache images asynchronously.

I have attached the code please check
extension MainVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Breed.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as? CustomTableViewCell
cell.nameLabel.text = Breed[indexPath.row].name // see here
return cell ?? CustomTableViewCell()
}
}

Related

Convert JSON to a Array with struct

I am trying to make a IOS app that is a home automation thing. I am using TableViewCell to display information.
My problem is that I have no idea how to get JSON to an Array with struct because I have to have struct I think.
My JSON is:
[{"namea":"TV","statea":"up_tv"},{"namea":"test","statea":"test"}]
My code is:
struct cellData {
let nameLabel : String!
let stateLabel : String!
}
class Main: UITableViewController {
var array = [cellData]()
override func viewDidLoad() {
array = [cellData(nameLabel: "tv", stateLabel: "up_tv"),
cellData(nameLabel: "tv", stateLabel: "down_tv")]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("TableViewCell", owner: self, options: nil)?.first as! TableViewCell
cell.nameLabel.text = array[indexPath.row].nameLabel
cell.stateLabal.text = array[indexPath.row].stateLabel
return cell
}
You need jsonDecoder
struct cellData : Decodable {
let nameLabel : String
let stateLabel : String
enum CodingKeys:String,CodingKey {
case nameLabel = "namea"
case stateLabel = "statea"
}
}
//
let str = """
[{"namea":"TV","statea":"up_tv"},{"namea":"test","statea":"test"}]
"""
do {
let cellArr = try JSONDecoder().decode([cellData].self, from: str.data(using:.utf8)!)
print(cellArr) //// check this
} catch {
}
//
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as TableViewCell
}

JSON data on UITableView

I managed to include this API from America FAA website in order to download the NOTAM for a pilot. I manage to send the request of the param "api key, location, state". I get back my JSON data and it works fine. Now my problem is I want to display on a tableView one item of the array that I got back in JSON format, is the item called 'all'.
I created the IBOutlet. I gave the cell identifier, but I'm stuck here
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//Constants
let notamUrl = "https://v4p4sz5ijk.execute-api.us-east-1.amazonaws.com/anbdata/states/notams/notams-realtime-list"
let api_key = "mykey"
let notamModel = ModelloNotam()
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return //????????? i dont know
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
// ????????? i dont know
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate=self
tableView.dataSource=self
func getNOTAM (url:String,parameters:[String:String]){
Alamofire.request(url, method: .get, parameters: parameters).responseJSON {
response in
if response.result.isSuccess{
let notamJSON : JSON = JSON (response.result.value!)
self.displayNotam(json: notamJSON)
} else{
print("errore connessione\(response.result.error)")
}
}
}
var locations = "VMMC"
var state = "CHN"
let params : [String : String] = ["locations" : locations, "state" : state, "api_key" : api_key]
getNOTAM(url: notamUrl, parameters: params)
}
func displayNotam (json:JSON) {
let conta = json.count
for var i in 0...conta {
i = i + 1
notamModel.all = json [i]["all"].stringValue
notamModel.type = json [i]["type"].stringValue
// print("The NOTAM type is \(notamModel.type)")
// print(notamModel.all)
// print("************************")
}
}
}
please refer this tutorial https://www.youtube.com/watch?v=sd7d4eoM54U&t=1857s and model data according to JSON file. networking request goes out of main execution queue.
i solve with this code.!!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellid", for: indexPath)
// let text = array[indexPath.row]
// cell.textLabel?.text=text
(cell.contentView.viewWithTag(1) as! UILabel).text = array[indexPath.row]
(cell.contentView.viewWithTag(2) as! UILabel).text = array2[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
thanks for the help.

How to populate the data from external database to UItableView in Swift

I am trying to populate the data from the external database using a PHP script, and I can see the data has been received in viewdidload(). But I am not able to see the data in the tableview.
I tried using the breakpoint, but no luck as my program does not reach to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ....}
Can you please suggest what I am not doing correctly.
import UIKit
class UsersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data: NSArray = []
#IBOutlet weak var listTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
data = dataOfJson(url: "http://kpstudio18.com/app/select.php")
print("data are as \(data)")
}
func dataOfJson(url: String) -> NSArray {
var data = NSData(contentsOf: NSURL(string: url)! as URL)
var error: NSError?
var jsonArray: NSArray = try! JSONSerialization.jsonObject(with: data! as Data, options: .mutableContainers) as! NSArray
return jsonArray
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if data.count != 0 {
return data.count
} else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: additionInfoCell = self.listTableView.dequeueReusableCell(withIdentifier: "customCell") as! additionInfoCell
let maindata = (data[indexPath.row] as! NSDictionary)
cell.name.text = maindata["name"] as? String
cell.info.text = maindata["id"] as? String
print("Cell Name is \(cell.name.text)")
print("Cell Info (id) is \(cell.info.text)")
return cell
}
}
yourtableview_name.registerNib(UINib(nibName: "customCell", bundle: nil), forCellReuseIdentifier: "customCell")
try this in viewdidload.if it works let me know.
You simply forgot to reload your table. In your viewDidLoad() method add the following line yourTableView.reloadData() and hopefully it will work.
Also, check if you registered your "CustomCell" as said above by RB1509

Downloading file names from firebase storage

As i read in the documentation i can access single url in firebase storage like this:
`// Create a reference to the file you want to download
let starsRef = storageRef.child("images/stars.jpg")
// Fetch the download URL starsRef.downloadURL { url, error in
if let error = error {
// Handle any errors }
else {
// Get the download URL for 'images/stars.jpg'
} }`
However, i have many files there, so how can i skip giving direct path and instead iterate through all files in the given directory?
Thanks for tips.
DownloadURL takes single string at a time. In case you want to show all the files inside a folder to a tableview like me, here is the
full code:
import UIKit import Firebase
My very First View Controller-
class FolderList: UIViewController {
var folderList: [StorageReference]?
lazy var storage = Storage.storage()
#IBOutlet weak var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.storage.reference().child("TestFolder").listAll(completion: {
(result,error) in
print("result is \(result)")
self.folderList = result.items
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
} }
extension FolderList : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return folderList?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "FolderListCell", for:
indexPath) as? FolderListCell else {return UITableViewCell()}
cell.itemName.text = folderList?[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64.0
} }
extension FolderList : UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let downloadVC = storyBoard.instantiateViewController(withIdentifier:
"DownloadedItemView") as? DownloadedItemView else {
return
}
downloadVC.storageRef = folderList?[indexPath.row]
self.navigationController?.pushViewController(downloadVC, animated: true)
}
}
You each cell:
class FolderListCell: UITableViewCell {
#IBOutlet weak var itemName : UILabel!
}

tableView Cell not displaying data, What's the issue? (Swift 3)

So I have a view controller that is parsing some bitcoin price data. The function successfully responds and parses the data, but I cannot seem to get it to display in tableView.
I have already tested the outlets and identities via a test cell, which does work.
What am I doing wrong?
Code: `
import UIKit
import Alamofire
import AlamofireObjectMapper
import ObjectMapper
class Response: Mappable{
var data: [Amount]?
required init?(map: Map){
}
func mapping(map: Map) {
data <- map["data"]
}
}
class Amount: Mappable {
var data : String?
required init?(map: Map){
}
func mapping(map: Map) {
data <- map["data.amount"]
}
}
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
var mount = [String]()
var am = [String]()
#IBOutlet var tableView: UITableView!
func Call_bitcoin() {
let url = "https://api.coinbase.com/v2/prices/BTC-USD/buy"
Alamofire.request(url).responseObject{ (response: DataResponse<Amount>) in
let mount = response.result.value
let am = mount?.data
self.tableView.reloadData()
return
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.delegate = self
tableView.dataSource = self
Call_bitcoin()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return am.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
print(am)
cell.textLabel?.text = am[indexPath.row]
return cell
}
}
`
You want to add elements in your Call_bitcoin() function but you are doing it wrong. You have already created var am = [String](). So you don't need to use let am = mount?.data. You could add your data in your am variable which is already created. You need to change some of your code lines in Call_bitcoin() function:
From:
let mount = response.result.value
let am = mount?.data
To:
let mount = response.result.value
am.append(mount?.data) // This part should be set by your 'mount?.data' value type
Your JSON not returning array, it's having dictionary.that why you get
am.count = 0
Solve first your data and put data into the array.
{"data":{"amount":"2414.88","currency":"USD"},"warnings":[{"id":"missing_version","message":"Please supply API version (YYYY-MM-DD) as CB-VERSION header","url":"https://developers.coinbase.com/api#versioning"}]}

Resources