Display JSON data in table view - json

I am not able to display the JSON data on my table view I don't know why. I tried to get the JSON data but I am not able of displaying it on screen on table format.
import UIKit
class User{
var userId : Int
var id : Int
var title : String
var completed : Bool
init (userId : Int , id: Int, title : String, completed : Bool){
self.userId = userId
self.id = id
self.title = title
self.completed = completed
}
}
class ViewController: UITableViewController{
var users = [User]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
makeGetCall()
//makePostCall()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.title
cell.detailTextLabel?.text = user.title
return cell
}
func makeGetCall(){
// users = []
let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
let todo = try JSONSerialization.jsonObject(with: responseData, options: .mutableLeaves)
as? NSArray
for eachUser in todo! {
let eachUsers = eachUser as! [String : AnyObject]
let userId = eachUsers["userId"] as! Int
let id = eachUsers["id"] as! Int
let title = eachUsers["title"] as! String
let completed = eachUsers["completed"] as! Bool
self.users.append(User(userId: userId, id: id, title: title, completed: completed))
print(eachUser)
DispatchQueue.main.async {
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: self.cellId)
}
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
}

You need to reload the table after appending the data
DispatchQueue.main.async {
self.tableView.reloadData()
}
And register line should be in viewDidLoad
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: self.cellId)
A good way also is to use
struct User :Decodable{
let userId , id : Int
let title : String
let completed : Bool
}
users = try? JSONDecoder().decode([User].self,from:data)

Related

I'm having troubles displaying an image from JSON in a Table View

I'm trying to display images that comes from an API. The images are inside an URL and I want to fill a Table View with all the array, but it shows only one image at the Table View.
Here's my code:
struct Autos {
let Marca:String
let Modelo:String
let Precio:String
let RutaImagen:String
init?(_ dict:[String:Any]?){
guard let _dict = dict,
let marca=_dict["Marca"]as?String,
let modelo=_dict["Modelo"]as?String,
let precio=_dict["Precio"]as?String,
let rutaImagen=_dict["RutaImagen"]as?String
else { return nil }
self.Marca = marca
self.Modelo = modelo
self.Precio = precio
self.RutaImagen = rutaImagen
}
}
var arrAutos = [Autos]()
func getImage(from string: String) -> UIImage? {
// Get valid URL
guard let url = URL(string: string)
else {
print("Unable to create URL")
return nil
}
var image: UIImage? = nil
do {
// Get valid data
let data = try Data(contentsOf: url, options: [])
// Make image
image = UIImage(data: data)
}
catch {
print(error.localizedDescription)
}
return image
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "carsCell", for: indexPath) as! CarsDetailTableViewCell
let url = URL(string: "http://ws-smartit.divisionautomotriz.com/wsApiCasaTrust/api/autos")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: []) as? NSArray
self.arrAutos = jsonResponse!.compactMap({ Autos($0 as? [String:String])})
DispatchQueue.main.async {
// Get valid string
let string = self.arrAutos[indexPath.row].RutaImagen
if let image = self.getImage(from: string) {
// Apply image
cell.imgCar.image = image
}
cell.lblBrand.text = self.arrAutos[indexPath.row].Marca
cell.lblPrice.text = self.arrAutos[indexPath.row].Precio
cell.lblModel.text = self.arrAutos[indexPath.row].Modelo
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
return cell
}
The JSON serialization is working fine, because the other data is showed correctly at the table view, the issue is with the image, because in the table view only appears one image, the other rows are empty. Does anyone have an advise?
I think you should download your full data before loading tableview and reload tableview in the completion handler. Call loadData() method in your viewDidLoad().
fileprivate func loadData() {
let url = URL(string: "http://ws-smartit.divisionautomotriz.com/wsApiCasaTrust/api/autos")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: []) as? NSArray
self.arrAutos = jsonResponse!.compactMap({ Autos($0 as? [String:String])})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
For loading images in tableView cell, download the image in background thread and then update the imageView in the main thread.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "carsCell", for: indexPath) as! CarsDetailTableViewCell
// Get valid string
let string = self.arrAutos[indexPath.row].RutaImagen
//print(string)
cell.lblBrand.text = self.arrAutos[indexPath.row].Marca
cell.lblPrice.text = self.arrAutos[indexPath.row].Precio
cell.lblModel.text = self.arrAutos[indexPath.row].Modelo
let url = URL(string: string)
if url != nil {
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
if data != nil {
cell.imgCar.image = UIImage(data:data!)
}
}
}
}
return cell
}
Hope this will work.

why is my json returning nil

let myUrl = URL(string: "http://app.avatejaratsaba1.com/api/Values/GetPriceList?paymentType=1&goodType=102")
var request = URLRequest(url: myUrl!)
request.httpMethod = "GET" // compose a query string
request.addValue("application/json", forHTTPHeaderField: "content-type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request)
{
(data : Data? , response : URLResponse? , error : Error?) in
self.removeActivtyIndicator(activityIndicator: MyActivityIndicator)
if error != nil
{
self.DisplayMessage(UserMessage: "2Could not successfully perform this request , please try again later.")
print("error = \(String(describing : error))")
}
// let's convert response sent from a server side code to a NSDictionary object:
do { let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json
{
I have the exact code in another viewcontroller with another url and it works properly !!! it works properly in Postman!!
and i'm coding swift
UPDATED::::
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [[String:Any]] {
var goodNameArray = [String].self
for i in 0..<json.count{
guard let goodName = json[i]["goodName"] as? String else{return}
Global.GlobalVariable.GoodName = goodNameArray.append(goodName)
}
print("GoodNames: \(goodNameArray)")
}
} catch let parseError {
print("parsing error: \(parseError)")
let responseString = String(data: data, encoding: .utf8)
print("raw response: \(String(describing: responseString))")
}
}
task.resume()
and the error it returns is :
Cannot invoke 'append' with an argument list of type '(String)'
global var code:::::
class Global: UIViewController
{
struct GlobalVariable
{
static var companyName = "Company"
static var bigName = ((0) , (""))
static var names = ["Loading..." , ""]
////////////
static var AgentInfo = "agentinfo"
////////////
static var genaral_goodID = 000
static var GoodName = [String]()
static var PriceVariableName = "PriceVariableName"
static var paymentType = "paymentType"
static var fee = "fee"
static var exipreDate = "exipreDate"
static var expireTime = "expireTime"
}
}
UPDATED::::::
uitable
class secondtable : TableViewController
{
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Global.GlobalVariable.names.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let content = Global.GlobalVariable.GoodName[indexPath.row]
cell.textLabel?.text = content
//cell.accessoryType = .disclosureIndicator
return cell
}
}
here in this par of my code , i'm supposed to populate a table with "goodName"
I tested in bellow way ,it is working for me.The response in array of dictionaries. Don't do force unwrap.
func viewDidLoad(){
downloadDataFromServer { (success, goodNamesArray) in
if success{
print("goodNamesArray: \(goodNamesArray)")
print("successfully updated tableview data")
self.tableView.reloadData()
}
}
}
func downloadDataFromServer(completionHandler: #escaping(Bool,[String])->()){
guard let url = URL(string: "http://app.avatejaratsaba1.com/api/Values/GetPriceList?paymentType=1&goodType=102") else {
return
}
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print("request failed \(String(describing: error))")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [[String:Any]] {
var goodNameArray = [String]()
for i in 0..<json.count{
guard let goodName = json[i]["goodName"] as? String else{return}
self.goodNameArray.append(goodName)
}
print("GoodNames: \(self.goodNameArray)")
Global.GlobalVariable.GoodName = goodNameArray
}
} catch let parseError {
print("parsing error: \(parseError)")
let responseString = String(data: data, encoding: .utf8)
print("raw response: \(String(describing: responseString))")
}
}
task.resume()
}
Do yourself a favour and save some time by reading up on the Codable protocol. It will allow you to generate a pretty decent JSON-parser by basically just defining your structure. Using JSONDecoder.decode will provide you with much more valuable error information if something goes wrong.
Since your API is currently only providing an empty array using the URL you provide us with it is pretty hard to come up with any meaningful code. You should resort to a simple String-representation of your JSON, at least a minimalized form that shows us all about the structure. That way your question would not depend on the workings of a probably fairly complicated web service.

No Data in array github Api Swift

How do I get the "names" from the if let statement into my tableview? The code triggers the else block right now. I am trying to parse the name data from the github api. Here's the code:
import UIKit
import Foundation
class ViewController: UITableViewController {
var tableArray = [String] ()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
parseJSON()
}
func parseJSON () {
let url = URL(string: "https://api.github.com")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error ) in
guard error == nil else {
print("returned error")
return
}
guard let content = data else {
print("No data")
return
}
guard let json = (try? JSONSerialization.jsonObject(with: content,
options: JSONSerialization.ReadingOptions.mutableContainers)) as?
[String: Any] else {
print("Not containing JSON")
return
}
if let array = json["name"] as? [String] {
self.tableArray = array
} else {
print("Name is blank")
}
print(self.tableArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
task.resume()
}
}
extension ViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = self.tableArray[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableArray.count
}
}
Right now it's displaying "name is blank" in the console. I am trying to get the names of the users and display them in a tableview. Other url's seem to work, but I can't seem to figure out Github. Thanks for the help.
Your mistakes is.
1. Used wrong url.
2. Wrong mapping response
https://api.github.com Return Api list.
https://api.github.com/users/ Return user list.
Function fetchNameOfUsers
1. Implement request by use "user list" url.
2. Mapping the response by use "user list" structure.
func fetchNameOfUsers() {
guard let url = URL(string: "https://api.github.com/users") else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
return // Error
}
guard let data = data, let _ = response else {
return // No data
}
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] else {
return // Not [String: Any] list
}
let nameOfUsers = json.compactMap {
$0["login"] as? String
}
let displayedNameOfUsers = nameOfUsers.joined(separator: ", ")
print(displayedNameOfUsers)
}.resume()
}
To see result call function fetchNameOfUsers.
fetchNameOfUsers()

How to reload data in UITableview after making second Json call Swift 3

I am making JSON request and if its completed so I am navigating page with JSON data to my table view controller and everything works fine but when I am making second call to load more cells I am not able to reload data and here is the code of my table view controller there I am making second call.
var listData:[String] = []
var videoIDData:[String] = []
var valueKeyData:[String] = []
var nextPageToken:String?
var titleNamee:[String] = []
var videoIDD :[String] = []
var valueKeyy :[String] = []
var PrevPageToken:String?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath) as! VideoTableViewCell
cell.videoTitle.text = listData[indexPath.row]
let url = NSURL(string: valueKeyData[indexPath.row])
let dataaa = NSData(contentsOf: url! as URL)
let image = UIImage(data: dataaa! as Data)
cell.videoThumnailImageView.image = image
if (indexPath.row == listData.count - 1)
{
makeGetCall()
}
return cell
}
func makeGetCall() {
// Set up the URL request
var pageToken:String = (pageToken)
let todoEndpoint: String = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50\(pageToken)&playlistId=\(id)_ItOZ8WBF5_SI_SrSN3_F&\(key)"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String : AnyObject] else {
print("error trying to convert data to JSON")
return
}
self.PrevPageToken = jsonObject["prevPageToken"] as? String
self.nextPageToken = jsonObject["nextPageToken"] as? String
if let itemsArray = jsonObject["items"] as? [[String:AnyObject]]{
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
You are reloading tableView at wrong place you need to reload it inside completion block after the for loop because completion block will call async, so remove your current reload code of tableView and put it after the for loop.
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
//Reload your table here
DispatchQueue.main.async{
self.tableView.reloadData()
}
Note: Instead of creating multiple array that you are doing currently what you need to do is make one custom class or struct with all these properties and make array of that custom class or struct objects.
Edit: I haven't looked at that you are calling this method in cellForRowAt don't do that as of cell will be reused and that is the reason you UI getting stuck so remove that calling code from cellForRowAt, if you want to make something like pagination than you can use scrollViewDelegate method like this way.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if((scrollView.contentOffset.y + scrollView.frame.size.height) == scrollView.contentSize.height)
{
self.makeGetCall()
}
}
Also it its better that you show some process indicator while you are making API request.

How do I download json file from remote saver and save to bundle for offline access

I am able to download a json file from a server and put it into a TableView each time the App is opened. (see code below)
However, I also want to allow offline access using the last file downloaded. So essentially I am having trouble working out how to save the downloaded file for local access offline. Any help (especially examples) much appreciated.
class KalkanTableController: UITableViewController {
var TableData:Array< String > = Array < String >()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://ftp.MY FILE LOCATION/kalkanInfoTest.json")
if let path = Bundle.main.path(forResource: "kalkanInfoTest", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
do {
let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
} catch {}
} catch {}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let item_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let item_obj = item_list[i] as? NSDictionary
{
if let item_name = item_obj["kalkanInfo"] as? Stri
{
TableData.append(item_name)
print(item_name)
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
return
})
}
}
You can try to save your json locally using these methods to save on disk
override func viewDidLoad() {
super.viewDidLoad()
if readKalkanDataFromDisk() {
extract_json(readKalkanDataFromDisk())
} else {
get_data_from_url("http://ftp.MY FILE LOCATION/kalkanInfoTest.json")
}
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
saveKalkanDataOnDisk(kalkanData: data!)
self.extract_json(data!)
})
task.resume()
}
func saveKalkanDataOnDisk(kalkanData: Data) {
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent("Kalkan.json")
try kalkanData.write(to: fileURL, options: .atomic)
} catch { }
}
func readKalkanDataFromDisk() -> Data? {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsURL.appendingPathComponent("Kalkan.json").path
if FileManager.default.fileExists(atPath: filePath), let data = FileManager.default.contents(atPath: filePath) {
return data
}
return nil
}