fetch json data to tableview in swift 5 - json

I am trying to fetch data from php file in json to my tableview in swift 5
I am able to get the data in the console as json array,
what I want is to fetch the data into my custom cell but I am not getting the json data array keys for example data["user_id"] it is not loading anything
my json data one array sample
func getUsers() {
AF.request(SiteUrl).validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful)")
if let json = response.data {
do{
let data = try JSON(data: json)
let str = data
print("DATA PARSED: \(str)")
}
catch{
print("JSON Error")
}
}
case .failure(let error):
print(error)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for: indexPath) as! TableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
cell.textLabel?.text = "Hi Hadi"
//customCell.AdTitle.text =
return customCell
}

First of all it's highly recommended to drop SwiftyJSON in favor of Codable.
Nevertheless just create a data source array, assign the received data to the data source array and reload the table view. In cellForRow get the item from the array and assign the values to the corresponding UI elements
var data = [JSON]()
func getUsers() {
AF.request(SiteUrl).validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful)")
if let json = response.data {
do{
let jsonData = try JSON(data: json)
self.data = jsonData.arrayValue
self.tableView.reloadData() // we are already on the main thread
print("DATA PARSED: \(jsonData)")
}
catch {
print("JSON Error", error)
}
}
case .failure(let error):
print(error)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let customCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for: indexPath) as! TableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
let item = data[indexPath.row]
cell.textLabel?.text = item["user_id"].string
return cell
}
And this is the modern way, first create a struct (you have to add the other members according to the JSON and use camelCase names: "user_id" -> userId)
struct User : Decodable {
let id, contactName : String
let userId : String
}
and replace the other code above with
var users = [User]()
func getUsers() {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
AF.request(SiteUrl).validate().responseDecodable(decoder: decoder) { (response : DataResponse<[User],AFError>) in
switch response.result {
case .success(let result):
print("Validation Successful)")
self.users = result
self.tableView.reloadData()
case .failure(let error): print("JSON Error", error)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let customCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for: indexPath) as! TableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.userId
return cell
}

Related

Issue adding sections to tableview from JSON data

I am trying to group my table view that is being populated from JSON data.
Here is an example of what it looks like:
[{"customer":"Customer1","serial":"34543453",
"rma":"P2384787","model":"M282","manufacturer":"Manufacturer1"},
{"customer":"Customer1","serial":"13213214",
"rma":"P2384787","model":"M384","manufacturer":" Manufacturer1"},
{"customer":"Customer2","serial":"1212121323",
"rma":"P3324787","model":"M384","manufacturer":" Manufacturer1"}]
I would like to group the table view based on the customer name.
So in my case, it should look like:
Customer1
34543453 - Manufacturer1 - M282
13213214 - Manufacturer1 - M384
Customer2
1212121323 - Manufacturer1 - M384
NOTE:
The reason there is a line separating the serial manufacturer and model is because of this separator in CustomerViewController.swift:
let titleStr = [item.serial, item.manufacturer, item.model].compactMap { $0 }.joined(separator: " - ")
PortfolioController.swift
import UIKit
class PortfolioController: UITableViewController {
var portfolios = [Portfolios]()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Customer"
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.portfolios = try decoder.decode([Portfolios].self, from: data)
self.tableView.reloadData()
} catch let jsonError {
print("Failed to decode json", jsonError)
}
}
}.resume()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return portfolios.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let customer = portfolios[indexPath.row]
//cell.textLabel?.text = customer.serial
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = titleStr
return cell
}
}
Portfolios.swift
import UIKit
struct Portfolios: Codable {
let customer, serial, rma, model: String
let manufacturer: String
}
1- Create an instance var
var portfoliosDic = [String:[Portfolios]]()
2- Assign it here
let res = try JSONDecoder().decode([Portfolios].self, from: data)
self.portfoliosDic = Dictionary(grouping: res, by: { $0.customer})
DispatchQueue.main.async {
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return portfoliosDic.keys.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let keys = Array(portfoliosDic.keys)
let item = portfoliosDic[keys[section]]!
return item.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let keys = Array(portfoliosDic.keys)
let arr = portfoliosDic[keys[indexPath.section]]!
let customer = arr[indexPath.row]
//cell.textLabel?.text = customer.serial
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = titleStr
return cell
}

Table empty when populating cells from json

I am trying to populate a table with json content. Everything seems to work fine except that the table is not showing any data. Actually, the code shown below should display the "title" information of each json data array into one cell. See line
cell.textLabel?.text = myNewsItems[indexPath.row].title
However, from what I can see in the console output, I can verify that the news array is parsed like expected (see Checkpoint: print(myNewsS)).
Any idea what I am missing?
Swift4
import UIKit
// structure from json file
struct News: Codable{
let type: String
let timestamp: String
let title: String
let message: String
}
class HomeVC: UIViewController, UITableViewDelegate, UITableViewDataSource{
var myTableView = UITableView()
var myNewsItems: [News] = []
override func viewDidLoad() {
super.viewDidLoad()
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: 150, width: displayWidth, height: displayHeight - barHeight))
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
myTableView.dataSource = self
myTableView.delegate = self
self.view.addSubview(myTableView)
// JSON
let url=URL(string:"https://api.myjson.com/bins/ywv0k")
let session = URLSession.shared
let task = session.dataTask(with: url!) { (data, response, error) in
// check status 200 OK etc.
guard let data = data else { return }
do {
let myNewsS = try
JSONDecoder().decode([News].self, from: data)
print(myNewsS)
DispatchQueue.main.async {
self.myTableView.reloadData()
}
} catch let jsonErr {
print("Error json:", jsonErr)
}
}
task.resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myNewsItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = myNewsItems[indexPath.row].title
return cell
}
}
Assign the array
myNewsItems = myNewsS
DispatchQueue.main.async {
self.myTableView.reloadData()
}

Swift 4 saving data from json to an array to show it in TableView

I'm trying to save the data from func getCoinData to an array sympolsCoin and array sympolsCoin to use it in my TableView
I create this class in the same ViewController.swift file :
struct Coin: Decodable {
let symbol : String
let price_usd : String }
And this in my View controller class :
var coins = [Coin]()
var sympolsCoin = [String]()
var priceUSDcoin = [String]()
func getCoinData(completion: #escaping () -> ()) {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
completion()
}
}
catch {
print("Error is : \n\(error)")
}
}.resume()
}
And when i use the array in my TableView i got blank table !
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BitcoinTableViewCell", for: indexPath) as! BitcoinTableViewCell
cell.coinNameLable.text = sympolsCoin[indexPath.row]
cell.priceLable.text = priceUSDcoin[indexPath.row]
return cell
}
Since you are using JSONDecoder the entire logic to create and populate sympolsCoin and priceUSDcoin is pointless and redundant.
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case symbol, priceUSD = "price_usd"
}
let symbol : String
let priceUSD : String
}
var coins = [Coin]()
The completion handler is redundant, too. Just reload the table view on the main thread after receiving the data:
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
In viewDidLoad load the data
override func viewDidLoad() {
super.viewDidLoad()
getCoinData()
}
In cellForRow update the UI
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BitcoinTableViewCell", for: indexPath) as! BitcoinTableViewCell
let coin = coins[indexPath.row]
cell.coinNameLable.text = coin.symbol
cell.priceLable.text = coin.priceUSD
return cell
}
Create an Outlet of tableView in ViewController Class and give it name "tableView" then
Try this code: Swift 4
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v1/ticker/"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
self.tableView.reloadData()
}
}
catch {
print("Error is : \n\(error)")
}
}.resume()
}
Call this function in ViewDidLoad like this
override func viewDidLoad() {
super.viewDidLoad()
getCoinData()
}
You need to update the tableView from the main thread. As a good lesson to learn: Always update the UI from the Main Thread. Always.
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data!)
for info in self.coins {
self.sympolsCoin.append(info.symbol)
self.priceUSDcoin.append(info.price_usd)
DispatchQueue.main.async {
self.tableView.reloadData()
}
print("\(self.sympolsCoin) : \(self.priceUSDcoin)")
completion()
}
}
There is, however another problem with your code the way you have your labels setup won't work. TableViewCells get reused so I'm guessing you have #IBOutlets for them somewhere else. What you should do is declare a label constant in cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let coinNameLabel = cell.viewWithTag(100) as! UILabel
coinNameLabel.text = sympolsCoin[indexPath.row]
let priceNameLabel = cell.viewWithTag(101) as! UILabel
priceNameLabel.text = priceUSDcoin[indexPath.row]
}
The above code assumes you've setup two labels with the tags 100 and 101 in your storyboard(assuming your using one)
**
// First View Controller
//
//
//
import UIKit
struct Countory : Decodable {
let name: String
let capital: String
let region: String
let alpha2Code: String
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var listArr = [Countory]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
let url = "https://restcountries.eu/rest/v2/all"
let urlObj = URL(string: url)!
URLSession.shared.dataTask(with: urlObj) {(data, responds, Error) in
do {
self.listArr = try JSONDecoder().decode([Countory].self, from: data!)
for country in self.listArr {
print("Country",country.name)
print("###################")
print("Capital",country.capital)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(" not ")
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.listArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.label1.text = "Name: \(listArr[indexPath.row].name)"
cell.lable2.text = listArr[indexPath.row].capital
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let homeView = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
homeView.res = listArr[indexPath.row].region
homeView.alpha = listArr[indexPath.row].alpha2Code
self.navigationController?.pushViewController(homeView, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
// SecondViewController
class SecondViewController: UIViewController {
#IBOutlet weak var label4: UILabel!
#IBOutlet weak var label3: UILabel!
var res = ""
var alpha = ""
override func viewDidLoad() {
super.viewDidLoad()
self.label3.text = res
self.label4.text = alpha
}
}
**

Displaying JSON imageURL into UITableView using Alamofire and SwiftyJSON

I am currently trying to display the JSON data on to my UITableView. I've created a separate class which handles and declares the data being extracted from the API. I also created a custom cell to display the data. However I am not sure how to display the images of the API. I was able to display the title of the API using just a standard TableViewController without custom cells:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
cell.textLabel?.text = recipes = [indexPath.row].title
return cell
}
but when trying to create a custom cell the image uses an imageURL which works differently. Other tutorials and demo don't seem to be using Alamofire and SwiftyJSON.
I did read the documentation: https://github.com/SwiftyJSON/SwiftyJSON#integration but still not sure how to solve this issue. Please tell me how you'd get the API data to display on my table.
Class
import Foundation
import SwiftyJSON
class Recipe {
var title: String!
var image: URL?
init(json: JSON) {
self.title = json["title"].stringValue
self.image = json["image"].url
}
}
CustomCell
import UIKit
class RecipeCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var imgView: UIImageView?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
ViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(searchURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: headers).response { [unowned self] response in
guard let data = response.data else { return }
let json = JSON(data: data)
for recipe in json.arrayValue {
let newRecipe = Recipe(json: recipe)
self.recipes.append(newRecipe)
}
for recipe in self.recipes {
print(recipe.title)
print(recipe.image)
}
}
}
// Display JSON Data into the tableView
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
// What to write here?
return cell
}
Thank you in advance! :)
for this you have to make the extension of Imageview as
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { () -> Void in
self.image = image
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode)
}
}
then you have to write cellForRowAt method as
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "recipeCell", for: indexPath)
let recipe = recipes[indexPath.row]
let imagePath = recipe.image
cell.titleLabel.text = recipe.title
cell. imgView.downloadedFrom(link: imagePath)
return cell
}

How to send data to table view with Alamofire And SwiftyJSON

I have a (large amount) of data that I want to send to Table View with alamofire and siftyJSON
web request :
let postEndPoint :String = "http://jsonplaceholder.typicode.com/posts/"
code Alamofire and SwiftyJSON:
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, postEndPoint).responseJSON { response in
// handle json
guard response.result.error == nil else{
print("error calling Get on /posts/1")
print(response.result.error)
return
}
if let value: AnyObject = response.result.value{
let post = JSON(value)
// for i in 0...post.count{
if let title = post["data"].arrayValue as? [JSON]{
self.datas = title
self.tableView.reloadData()
print("the title is :\(title)" )
}else{
print("eror parsing /post/1 ")
}
// }
}
}
}
code table view :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell")!
var dict = datas[indexPath.row]
cell.textLabel?.text = dict["userId"] as? String
cell.detailTextLabel?.text = dict["id"] as? String
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datas.count
}
}
web request :enter link description here
I am trying to post some json data to table view but when I send the data it returns nothing . why??
this is the code working for me. main thing is you have to reload the table at the end of api calling. and you are printing nuber as a string so you have to convert it to string
my code is here
import UIKit
class customcell: UITableViewCell {
#IBOutlet weak var lbl1: UILabel!
#IBOutlet weak var lbl2: UILabel!
}
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var tabledata: UITableView!
let postEndPoint :String = "http://jsonplaceholder.typicode.com/posts/"
var arr:NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
web()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("customcell") as! customcell
let dict = arr[indexPath.row] as! NSDictionary
print(dict)
print(dict["userId"])
cell.lbl1?.text = String (dict["userId"]! )
cell.lbl2?.text = String (dict["id"]! )
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func web()
{
request(.GET, postEndPoint, parameters: nil, encoding:
.JSON).responseJSON { (response:Response<AnyObject, NSError>) -> Void in
print(response.result.value)
if (response.result.value != nil)
{
self.arr = (response.result.value) as! NSMutableArray
}
self.tabledata.reloadData()
}
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (tableView == tabledata)
{
let cell = tableView.dequeueReusableCellWithIdentifier("customcell") as! customcell
let dict = arr[indexPath.row] as! NSDictionary
cell.lbl1?.text = String (dict["userId"]! )
cell.selectionStyle = .None
return cell
}
else if(tableView == tablefactoryoption)
{
let cell1:Facturyoptioncell = tableView.dequeueReusableCellWithIdentifier("Facturyoptioncell") as! Facturyoptioncell
let dict = arr[indexPath.row] as! NSDictionary
cell1.lbl2?.text = String (dict["Id"]! )
cell1.selectionStyle = .None
cell1.selectionStyle = .None
return cell1
}
else
{
let cell2:Technicalcell = tableView.dequeueReusableCellWithIdentifier("Technicalcell") as! Technicalcell
let dict = arr[indexPath.row] as! NSDictionary
cell2.lbl3?.text = String (dict["userId"]! )
return cell2
}
}
This is how you can use multiple tableview for display data.