Populating tableview cells from JSON with Alamofire (Swift 2) - ios

I have the following code.
import UIKit
import Alamofire
class CheHappyTableViewController: UITableViewController, NSURLConnectionDelegate {
var happyHours = [HappyHour]()
override func viewDidLoad() {
super.viewDidLoad()
//Load the cell elements
loadHappyHourElements()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadHappyHourElements(){
let testhappyhour:HappyHour = HappyHour(title: "TEST", image: "TESST", description: "TEST", postedDate: "TEST")
self.happyHours.append(testhappyhour)
let url:String = "https://gist.githubusercontent.com/arianitp/036133ebb5af317a595c/raw/f134ec241ec3126bedd6debe5de371e3e01d225b/happyhours.json"
Alamofire.request(.GET, url, encoding:.JSON).responseJSON
{ response in switch response.result {
case .Success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = HappyHour(title:obj["title"] as! String, image:obj["image"] as! String, description:obj["description"] as! String, postedDate:obj["date"] as! String)
self.happyHours.append(happyhour)
}
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
self.tableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return happyHours.count
}
//Displays the cells in the table
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
self.tableView.rowHeight = UIScreen.mainScreen().bounds.size.width
let cellIdentifier = "CheHappyTableViewCellController"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CheHappyTableViewCellController
let happyHour = happyHours[indexPath.row]
cell.lblTitle.text = happyHour.title
//cell.cheHappyImage = happyHour.photo
// Configure the cell...
return cell
}
}
The table cells don't get updated even though I included the self.tableView.reloadData() in the Alamofire request complete function. I have defined also one Sample Object with Title and all Properties set to "TEST", this does get loaded, however the JSON file doesn't populate the table. I can see the file is being downloaded correctly and read, but I think the elements are either not being added to the Property Variable happyHours or somehow the elements are not being reloaded.
I tried many solutions from here but without success. What am I doing wrong?

Your self.tableView.reloadData() line is outside the callback, meaning that it gets called straight away, before the data has been loaded. Try this:
func loadHappyHourElements(){
let testhappyhour:HappyHour = HappyHour(title: "TEST", image: "TESST", description: "TEST", postedDate: "TEST")
self.happyHours.append(testhappyhour)
let url:String = "https://gist.githubusercontent.com/arianitp/036133ebb5af317a595c/raw/f134ec241ec3126bedd6debe5de371e3e01d225b/happyhours.json"
Alamofire.request(.GET, url, encoding:.JSON).responseJSON
{ response in switch response.result {
case .Success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = HappyHour(title:obj["title"] as! String, image:obj["image"] as! String, description:obj["description"] as! String, postedDate:obj["date"] as! String)
self.happyHours.append(happyhour)
}
self.tableView.reloadData()
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}

Related

Swift 5 Saving data from json using Alamofire to an array to show it in TableView

I use Alamofire for JSON request to get data from the gitHub repositories, but i cant use it in TableView, here is my code:
i create class "Repos"
class Repos: Codable {
var fullName: String
var description: String
var language: String
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
case description = "description"
case language = "language"
}
}
This is ViewContoller
var repos = [Repos]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: idCell)
fetchRepos {
print("succsed")
}
}
then i created an extension to fetchData
extension ReposViewController {
func fetchRepos(completed: #escaping () -> ()) {
let headers = ["Authorization": "Token ***********"]
request("https://api.github.com/user/repos", method: .get, headers: headers).validate().responseJSON { (responseJSON) in
switch responseJSON.result {
case .success:
guard let data = responseJSON.data else { return }
do {
let decoder = JSONDecoder()
let loginRequest = try decoder.decode([Repos].self, from: data)
print(loginRequest.first?.fullName ?? "ed")
print(loginRequest[2].language)
print(loginRequest[3].description)
DispatchQueue.main.async {
completed()
self.tableView.reloadData()
}
} catch let error {
print(error)
}
case .failure(let error):
print("вы не авторизованы! \(error)")
}
} .resume()
}
}
Everything is good, i got data, but when i use array in tableView , i see the empty List
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return repos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: idCell) as! TableViewCell
let reposList = repos[indexPath.row]
cell.titleOutlet?.text = reposList.fullName
cell.descriptionOutlet?.text = reposList.description
cell.languageOutlet?.text = reposList.language
return cell
}

Tableview with JSON Data in Swift 4.2

I'm new to Swift and I am at my wit's end, so please be kind to me.
I am building an app which downloads data from an url by posting a specific ID and getting back an JSON array. The JSON looks like this:
[{"group":"500","from":"2019-01-01","to":"2019-01-05"},{...}]
It has the group number as "group" and a start Date "from" and an end date "to".
I want to let the different groups shown in a Tableview with the From and To-Dates as well. I managed to download the data correctly but I failed by showing them in a tableview.
My code is down below. Maybe someone of you can find out what I've done wrong.
When I run the project I get an empty table.
import UIKit
typealias Fahrten = [FahrtenElement]
struct FahrtenElement: Codable {
let group: String?
let from: String?
let to: String?
enum CodingKeys: String, CodingKey {
case group = "group"
case from = "from"
case to = "to"
}
}
class BookingsController: UITableViewController {
var tableview: UITableView!
var groups = [Fahrten]()
// MARK: - Table view data source
override func viewDidLoad() {
super.viewDidLoad()
downloadData()
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel()
label.text = "Groups"
label.backgroundColor = .yellow
return label
}
// Section
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groups.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
//lblname to set the title from response
cell.textLabel?.text = "Group \(String(describing: groups[indexPath.row].group))"
cell.detailTextLabel?.text = "From \(String(describing: groups[indexPath.row].from)) to \(String(describing: groups[indexPath.row].to))"
return cell
}
func downloadData() {
let id = "..."
let url = URL(string: "...")!
// request url
var request = URLRequest(url: url)
// method to pass data POST - cause it is secured
request.httpMethod = "POST"
// body gonna be appended to url
let body = "id=(\(id))"
// append body to our request that gonna be sent
request.httpBody = body.data(using: .utf8)
URLSession.shared.dataTask(with: request) { (data, response, err) in
guard let data = data else { return }
do {
let groups = try JSONDecoder().decode(Fahrten.self, from: data)
print(groups)
self.tableview.reloadData() // I get an crash here: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value }
} catch let jsonErr {
print("Error serializing json:", jsonErr)
}
}.resume()
}
From what I can see it looks like you are emptying the array and not reloading the data.
You should assign the resulting groups property to self.groups and then reload the data.
DispatchQueue.main.async {
self.groups = groups // change here
self.tableview.reloadData()
}
Try this code.
Your model.
typealias Fahrten = [FahrtenElement]
struct FahrtenElement: Codable {
let group: String
let from: String
let to: String
enum CodingKeys: String, CodingKey {
case group = "group"
case from = "from"
case to = "to"
}
}
Use it, I am using it with local json with same structure.
func getFehData(){
let url = Bundle.main.url(forResource: "fah", withExtension: "json")!
do {
let jsonData = try Data(contentsOf: url)
let fahrten = try? JSONDecoder().decode(Fahrten.self, from: jsonData)
print(fahrten)
}
catch {
print(error)
}
}
Output : (Dummy data)

Swift 3 / Alamofire : I am not able to fetch the data on UITableView

I am using Alamofire and trying to fetch data on my tableview, however I am not able to get the data. When I use the cmd Print, its showing me the data but not able to fetch the data. How can I fetch the data on my tableview?
Please find the code below:-
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSURLConnectionDelegate {
//let myarray = ["item1", "item2", "item3"]
var tableData = Array<Group>()
var arrRes = [[String:AnyObject]]() //Array of dictionary
var group = [Group]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadGroups()
}
func loadGroups(){
let testhappyhour:Group = Group(tempName: "TEST", tempID: "TESST", icons: "TEST", tempgbcount: "TEST")
self.group.append(testhappyhour)
//let groupQuery:String = "http://jsonplaceholder.typicode.com/users"
Alamofire.request("http://jsonplaceholder.typicode.com/users").responseJSON
{ response in switch response.result {
case .success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = Group(tempName:obj["NAME"] as! String, tempID:obj["id"] as! String, icons:obj["icon"] as! String, tempgbcount:obj["TOTAL"] as! String)
self.group.append(happyhour)
}
self.tableview.reloadData()
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}
func convertToArray(text: String) -> [Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableview.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return myarray.count
return arrRes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath) as! UITableViewCell
// cell.textLabel?.text = myarray[indexPath.item]
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "groupCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["NAME"] as? String
cell.detailTextLabel?.text = dict["TOTAL"] as? String
return cell
}
}
Thank you!!
Need to change loadGroups function like this
func loadGroups(){
let testhappyhour:Group = Group(tempName: "TEST", tempID: "TESST", icons: "TEST", tempgbcount: "TEST")
self.group.append(testhappyhour)
Alamofire.request("http://jsonplaceholder.typicode.com/users").responseJSON
{ response in switch response.result {
case .success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = Group(tempName:obj["NAME"] as! String, tempID:obj["id"] as! String, icons:obj["icon"] as! String, tempgbcount:obj["TOTAL"] as! String)
self.group.append(happyhour)
self.arrRes.append(obj) // ADD THIS LINE
}
self.tableview.reloadData()
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}
Array 'group' is appended with the Alamofire responses, but Array 'arrRes' is used as the table view data source. If you use self.group instead of arrRes in the data source methods, the table should update with the new groups received in the Alamofire response.

In Swift, tableView data loaded from JSON only loads 80% of the time

I'm populating my tableView with JSON data, most of the time the data shows but for some strange reason other times it doesn't. I tested the JSON data in Chrome and the info is there. I also made print statements to print the info after it has downloaded and it appears to download correctly. I can't figure out why 80% of the time the data populates the tableView correctly and 20% of the time it doesn't. Here is a sample of my code, there are many more cells but I shortened it to 2 for this example:
var task : NSURLSessionTask?
var newURL : String?
var bannerArray: [String] = []
var overViewArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON(newURL!)
}
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateDetailShowInfo(data)
}
else {
"Not getting JSON"
}
}
}
task!.resume()
}
func updateDetailShowInfo (data: NSData!) {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
else { return }
_ = ""
print(overview)
bannerArray.append(banner)
overViewArray.append(overview)
}
catch {
print("It ain't working")
}
self.DetailTvTableView.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return bannerArray.count
case 1: return overViewArray.count
default: fatalError("Unknown Selection")
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("bannerCell", forIndexPath: indexPath) as! BannerCell
cell.bannerImage.sd_setImageWithURL(NSURL(string: bannerArray[indexPath.row]))
self.DetailTvTableView.rowHeight = 100
DetailTvTableView.allowsSelection = false
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("overviewCell", forIndexPath: indexPath) as! OverviewCell
let overViewText = overViewArray[indexPath.row]
if overViewText != "" {
cell.overView.text = overViewText
} else {
cell.overView.text = "N/A"
}
self.DetailTvTableView.rowHeight = 200
print(overViewArray[indexPath.row])
return cell
default: ""
}
return cell
}
I'm just doing this off the web. And I think there are some errors. You need to debug them yourself.
Your understanding of fetching the JSON and GCD is totally wrong. I believe these codes you got somewhere off the web. Go read up what is dispatch_async.
Basically, you need to create session to fetch JSON data, which you have done it correctly, however, within the NSJSONSerialization, you need to store them in a variable and append it to your array. This is fetched asynchronously. Your dispatch_async will reload data serially.
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
guard let banner = jsonResult["banner"] as? String,
let overview = jsonResult["overview"] as? String
bannerArray.append(banner)
overViewArray.append(overview)
} dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.DetailTvTableView.reloadData()
}
else {
"Not getting JSON"
}
}
catch {
print("It ain't working")
}
}
}
task!.resume()
}

Swift Assign two values to UITableViewCell

Is it possible to assign two values to a cell in a UITableView?
I have a json file that is structured like this:
{
"band": [
"Name": "The Kooks",
"id": "1258"
]
}
I can get the label to display in the cell and pass it to a new view controller, but how do I also assign the id so that I can pass that too?
I am new to swift so please dont eat me.
Edit 1:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
items is empty, so I get it like so:
Alamofire.request(.GET, testurl, parameters: ["bandName": bandName])
.responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
for (_,bands) in json {
for (_,bname) in bands {
let bandName = bname["Name"].stringValue
print(bandName)
self.items.append(bandName)
self.tableView.reloadData()
}
}
}
case .Failure(let error):
print(error)
}
}
you should not add every value in bname Dictionary to self.items.
Try add bname to self.items,code:
Alamofire.request(.GET, testurl, parameters: ["bandName": bandName])
.responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
for (_,bands) in json {
for (_,bname) in bands {
self.items.append(bname)
self.tableView.reloadData()
}
}
}
case .Failure(let error):
print(error)
}
}
and in cellForRowAtIndexPath use it:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
if let dic = self.items[indexPath.row] as? NSDictionary{
if let id = dic["id"] as? String{
cell.idLabel.text = id
}
if let name = dic["Name"] as? String{
cell.nameLabel.text = name
}
}
return cell
}
Create class or model with these properties and assign values in objects keep object in NSarray that works as datasource get object from datasource using selected indexpath and pass object to your new viewcontroller using prepareForSegue.

Resources