How do I populate a tableview with JSON data from Alamofire? - json

Before I state my problem, I want to let everyone know that I am new to the coding environment that is Swift, so forgive me for my lack of knowledge. Currently, I am having trouble populating the cells of a tableview using Alamofire based on the data that is returned from a JSON URL. When I run the app in a simulator, data is displayed in the console, but the app crashes with a SIGABRT error. For reference, instead of using a viewcontroller with a tableview element inside, I am using a tableviewcontroller. Here is my code thus far:
import UIKit
import Alamofire
class TableViewController: UITableViewController {
var responseArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request("https://rss.itunes.apple.com/api/v1/us/apple-music/top-songs/all/10/explicit.json").responseJSON { response in
if let json = response.result.value {
print(json)
self.responseArray = json as! NSArray
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return responseArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "top10", for: indexPath)
// Configure the cell...
let whichSong = responseArray[(indexPath as NSIndexPath).row]
let artistName = (whichSong as AnyObject)["artistName"] as? String
cell.textLabel?.text = artistName
return cell
}

The crash occurs because the root object of the JSON is a dictionary (represented by {}) not an array.
First of all declare a type alias for a JSON dictionary and the data source array as native type, an array of JSON dictionaries:
typealias JSONDictionary = [String:Any]
var responseArray = [JSONDictionary]()
Then parse the JSON and reload the table view, you want probably the array for key results:
Alamofire.request("https://rss.itunes.apple.com/api/v1/us/apple-music/top-songs/all/10/explicit.json").responseJSON { response in
if let json = response.result.value as? JSONDictionary,
let feed = json["feed"] as? JSONDictionary,
let results = feed["results"] as? [JSONDictionary] {
print(results)
self.responseArray = results
self.tableView.reloadData()
}
}
Then show the data in cellForRow
let song = responseArray[indexPath.row]
cell.textLabel?.text = song["artistName"] as? String

Okay so firstly change
let cell = tableView.dequeueReusableCell(withIdentifier: "top10", for: indexPath)
to
let cell = tableView.dequeueReusableCell(withIdentifier: "top10")
However, with this, cell will be cell?, you will have to return cell!.
Next in your Alamofire response,
if let json = response.result.value {
print(json)
self.responseArray = json as! NSArray
self.reloadData()
//If above line doesn't work, try tableView.reloadData()
}
Why?
The Alamofire request is "asynchronous", meaning it executes codes while your app is doing other things. Therefor, it is likely that you are setting that array after your table is loaded, hence the reloadData()

Replace the below line
let cell = tableView.dequeueReusableCell(withIdentifier: "top10", for: indexPath)
with
let cell = tableView.dequeueReusableCell(withIdentifier: "top10")

Related

Swift Tableview data load getting Index out of range

My scenario, I am trying to load JSON data into Tableview. Here, Tableview custom cell I am maintaining. Whenever trying to upload data into tableview, I may have chance to add new data from server.
Here, while loading I am getting Index out of range error. Every time I am calling JSON function from viewWillAppear. I have enough data, Inside my array nothing uneven data. Below code I am using
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // No need for semicolon
self.tableArray.removeAll()
self.cartname.removeAll()
self.parentid.removeAll()
self.name.removeAll()
self.year.removeAll()
parseJSON()
}
This is my JSON Process
if let content = json["content"] as? [[String:String]] {
print(json)
for category in content {
let cat_id = category["cat_id"]
let cat_name = category["cat_name"]
let cat_parentid = category["cat_parentid"]
let name = category["name"]
let year = category["year"]
self.tableArray.append(cat_id ?? "unknnown")
self.cartname.append(cat_name ?? "unknnown")
self.parentid.append(cat_parentid ?? "unknnown")
self.name.append(name ?? "unknnown")
self.year.append(year ?? "unknnown")
}
Tableview cell data load
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyCustomCell
cell.cat_id.text = self.tableArray[indexPath.row]
cell.cat_name.text = self.cartname[indexPath.row]// Some time Here I am getting out of range error
cell.cat_parentid.text = self.parentid[indexPath.row]
cell.name.text = self.name[indexPath.row] // Here I am getting out of range error
cell.year.text = self.year[indexPath.row]
return cell
}
}
It's expected with multiple arrays when using the tableView ,First from OOP point you need to create 1 model like
struct Root: Codable {
let catID, catName, catParentid, year,name: String?
enum CodingKeys: String, CodingKey {
case catID = "cat_id"
case catName = "cat_name"
case catParentid = "cat_parentid"
case year, name
}
}
and use Codable to parse the json
var arr = [Root]()
do {
let content = json["content"] as! [[String:String]]
let staData = try JSONSerialization.data(withJSONObject:content,options:[])
arr = try JSONDecoder().decode([Root].self, from:staData)
}
catch {
print(error)
}
in numberOfRows
return arr.count
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyCustomCell
let item = arr[indexPAth.row]
cell.cat_id.text = item.catID
cell.cat_name.text = item.catName
cell.cat_parentid.text = item.catParentid
cell.name.text = item.name
cell.year.text = item.year
return cell
}

JSON data not passing to tableView Cell

I am trying to get the below code to pass JSON data to the table viewCell. I have confirmed that the JSON data is being captured and stored in the variable downloadLenderRates. But I cannot get the values to pass to the tabelView Cell. I confirmed that the cell identifier is named correctly and the swift file that helps manage the tableView cell is named correctly. At this point, I get no error messages and just a blank table when I run the app. I am not sure why!
class MortgageRatesVC: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let mortgousURL = URL(string:"http://mortgous.com/JSON/currentRatesJSON.php")!
var lenderRates = [LenderRate]()
override func viewDidLoad() {
super.viewDidLoad()
downloadJason()
}
func downloadJason () {
lenderRates = []
// guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: mortgousURL) { data, urlResponse, error in
guard let data = data else { return }
do {
let dateFormat = DateFormatter()
dateFormat.locale = Locale(identifier: "en_US_POSIX")
dateFormat.dateFormat = "yyyy-MM-dd"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormat)
let downloadLenderRates = try decoder.decode([LenderRate].self, from: data)
// print(downloadLenderRates)
self.lenderRates = downloadLenderRates
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lenderRates.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LenderCell") as? LenderCell else { return UITableViewCell() }
cell.lenderNamelbl.text = lenderRates[indexPath.row].financialInstitution
print(lenderRates[indexPath.row].financialInstitution)
return cell
}
}
The syntax
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LenderCell") as? LenderCell else {
return UITableViewCell()
}
is very bad habit.
The guard can only fail if there is a design error which occurs for example if the developer forgot to set the class of the cell to the custom class. In this case you won't see anything in the table view.
This is one of the few cases where force-unwrapping is recommended. If the design is set up properly the cell is valid and its type is the custom class. Further use always the API dequeueReusableCell(withIdentifier:for:) which returns a non-optional cell.
let cell = tableView.dequeueReusableCell(withIdentifier: "LenderCell", for: indexPath) as! LenderCell

How to fecth data from array with model class

I am not getting data from the array using model class, but array have data in it. I don't know the correct way of using model classes. Maybe the way i implemented is wrong. Please guide.
Code:
// . ViewController class
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell: VisitorsCell = tableView.dequeueReusableCell(withIdentifier: "VisitorsCell", for: indexPath) as! VisitorsCell
let cell:RecentVisitorsCell = self.tblView.dequeueReusableCell(withIdentifier: "VisitorsCell") as! VisitorsCell
**let ds:VisitorDs = recentVisitorsArray[indexPath.row] as! VisitorsDs // . crash here.//**
// let ds = VisitorsDs()
cell.imgProfile.image = UIImage(named: ds.profile)
cell.lblAge.text = ds.age as String
return cell
}
//MARK: Delegatres Server Communication
func didFinishServerCommunicationWithSuccess(_ response:ServerResponse){
print("api response: ", response.infoResponse?["data"] as! [AnyObject])
//VisitorsArray = response.infoResponse?["data"] as! [AnyObject] as NSArray
//if response.operation == "SERVICE_SOURCE" {
if let dictResult = response.infoResponse as? NSDictionary {
if let arrResult = dictResult.object(forKey: "data") as? NSArray
{
for result in arrResult {
let Visitors = VisitorsDs()
recentVisitors.initWithVal(dict: result as! NSDictionary)
VisitorsArray.add(VisitorsDs.self)
}
print("array received: ", VisitorsArray)
self.tblView.reloadData()
}
}
self.tblView.reloadData()
}
// Model class
class VisitorsDs:NSObject {
var profile:String = ""
var name:String = ""
/*
//MARK: Initialization
init(profile:String,name:String,age:String,parentsName:String,mobile:String,purpose:String,date:String,time:String,clinic:String ) {
self.profile = profile
self.name = name
}
*/
func initWithVal(dict: NSDictionary) {
// print(dict)
if let latestValue = dict["image_path"] as? String {
self.profile = latestValue
}
if let latestValue = dict["first_name"] as? String {
self.name = latestValue
}
}
}
Error: Could not cast value of type 'IPAN.VisitorsDs' (0x10d809f58) to 'IPAN.VisitorsDs' (0x10d806eb0).
Please guide, values are empty in uitableview, but value exist in array.
I would recommend to use new Swift 4 Codable protocol to parse JSON into models and then you can use much easier in your tableview. One tutorial:
https://medium.com/swiftly-swift/swift-4-decodable-beyond-the-basics-990cc48b7375
BTW what is in your response?

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.

Problems with adding value to an array with array.append() in Swift

in the moment we're programming a Swift App for iOS in which we want to get data of our JSON Website (MySql database) into the TableViewCell. The problem is by appending the text values of the strings for the label in the cell. Swift can import the JSON values into the name variable but I cant assign it to the text array for the cells. I havent no syntax errors, but the data[0] Variable print sth. as "123". Why it is 123? The test Value is "Test". I don't now where the problem by appending the value to the array is, that the result is 123 after that. Please help.
Here is the sourcecode:
class listViewViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data:[String?] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let myUrl = URL(string: "");//Empty link for this question
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "lid=1";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response!)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let Name = parseJSON["Name"] as? String
print("\(Name)")//Test
self.data.append(Name!)
print("\(data![0])" as String)//123
}
} catch {
print(error)
}
}
task.resume()
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! listViewTableViewCell
print("\(data[indexPath.row])")
let dataCell = data[indexPath.row]
cell.listViewCell.text = dataCell
return cell
}
}
this because your array properties and data callback block parameter have the same name "data". in your code you user print("(data![0])" as String) instead of print("(self.data![0])" as String) => you have to add self.
then you can optimise your code like this (it's optional : it's just like a code review ;) )
try to do this
- change your array type to String like this
var data = [String]()
- webService callback change your code like this :
if let parseJSON = json {
if let Name = parseJSON["Name"] as? String{
print("\(Name)")
self.data.append(Name)
print("\(self.data.last)")//123
}
}
When you append to your data array you use self.data but you then print from data which is the parameter to the inner function. You add and print from different arrays.