Unable to append strings to array while parsing JSON data - ios

I am having difficulties storing the results retrieved from a JSON source data. I have confirmed the ability to print the data retrieved but it was not able to store into my local array.
My end objective is to actually print in a UITableView the results.
Below is the code for my relevant table view controller :
import UIKit
class CommunityActivityTableViewController: UITableViewController {
var displayNameArr = [String]()
var postDateArr = [String]()
var postDetailArr = [String]()
var testArr = ["teaad"]
override func viewDidLoad() {
super.viewDidLoad()
parseJson()
print(self.displayNameArr.count) //returns 0
print(self.postDateArr.count) //returns 0
print(self.postDetailArr.count) //returns 0
print(self.testArr.count)
print("end")
}
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 {
// #warning Incomplete implementation, return the number of rows
return self.displayNameArr.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("3")
let cell = tableView.dequeueReusableCellWithIdentifier("Cell_activity", forIndexPath: indexPath)
print("hi")
cell.textLabel?.text = "hi"
cell.detailTextLabel?.text = "test"
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func makeAttributedString(title title: String, subtitle: String) -> NSAttributedString {
let titleAttributes = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline), NSForegroundColorAttributeName: UIColor.purpleColor()]
let subtitleAttributes = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)]
let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)
titleString.appendAttributedString(subtitleString)
return titleString
}
func parseJson(){
//MARK: JSON parsing
let requestURL: NSURL = NSURL(string: "<sanitised>")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
print("Everyone is fine, file downloaded successfully.")
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let results = json["result"] as? [[String: AnyObject]] {
for result in results {
if let lastname = result["last_name"] as? String {
if let postdate = result["timestamp"] as? String {
if let firstname = result["first_name"] as? String {
if let postdetails = result["post_details"] as? String {
let displayname = firstname + " " + lastname
//print(displayname)
self.displayNameArr.append(displayname)
self.postDateArr.append(postdate)
self.postDetailArr.append(postdetails)
self.testArr.append("haha")
}
}
}
}
}
}
}catch {
print("Error with Json: \(error)")
}
}
}
task.resume()}
}
As per the code above the print results of displaynamearr.count and postDateArr.count and postDetailArr.count returned 0 when it should have returned more than 0 as a result of parseJson() method.
I have printed the display name, postgame and post details variables and they all contain data within so the problem does not lie with the extraction of data but the appending of data into the array.
Appreciate any help provided thanks ! Developed on Xcode 7 and Swift 2.2
Sanitised my JSON source due to sensitive nature of information (i have verified the retrieval of information is OK)

dataTaskWithRequest() is an asynchronous data loading. It loads on the background thread ensuring your UI won't freeze up. So your array will be empty when you this will be getting executed and hence your error. You need to a completion handler like so:
func parseJson(completion: (isDone: Bool) -> ()){
///code
for result in results {
if let lastname = result["last_name"] as? String {
if let postdate = result["timestamp"] as? String {
if let firstname = result["first_name"] as? String {
if let postdetails = result["post_details"] as? String {
let displayname = firstname + " " + lastname
//print(displayname)
self.displayNameArr.append(displayname)
self.postDateArr.append(postdate)
self.postDetailArr.append(postdetails)
self.testArr.append("haha")
}
completion(isDone: True)
}
}
Now in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
parseJson(){ success in
if success{
print(self.displayNameArr.count) //returns a value
print(self.postDateArr.count) //returns a value
print(self.postDetailArr.count) //returns a value
print(self.testArr.count) //This wont because I havent added it in the completion handler
print("end")
self.tableView.reloadData()
}
}
}

All of your UI updates run on the main thread. If you do something like
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
// ...
}.resume()
you start a task asynchronously on another thread (not the main thread). Your iPhone is doing a network request and this takes some time. So I guess when your cellForRowAtIndexPath delegate method is called you haven't received any data yet. This is the reason you don't see anything.
The easiest solution to this would be to reload the table view once you have received the data. When you're done with all the parsing in your parseJson method (outside of all the loops) simply run:
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
This forces your table view to update. Remember that you have to run code that updates the UI on the main thread. This is what dispatch_async(dispatch_get_main_queue()) {} does.
EDIT: The answer above was to illustrate the problem to you. The more elegant solution would be to use a completion handler like so:
func parseJson(completionHandler: (Bool) -> Void) {
//do all your json parsing.
//....
dispatch_asyc(dispatch_get_main_queue()) {
//run this if you received the data
//implement some kind of if statement that checks if the parsing was successful
completionHandler(true)
//run this if it failed
completionHandler(false)
}
}
In your viewDidLoad you would do something like
override func viewDidLoad() {
super.viewDidLoad()
//...
parseJson() { success in
tableView.reloadData()
if(success) {
print("success")
}
}
}
If you want to display an activity indicator while data is loaded (which I would recommend) it is easier to use a callback as I've just described.

Related

Why JSON data from decoder to become UITableView datasource does not assigns?

Recently got stuck on a problem of assigning freshly downloaded JSON data to table view datasource variable. I suppose the problem is something obvious but my skill is not enough to gather the big picture. Let me share a bunch of code.
(1) A function retrieves the data from Open Weather Map API (defined in the separate class 'GetWeather').
func getMowForecast(completion: #escaping ((WeatherForecast?, Bool)) -> Void) {
let url = URL(string: "http://api.openweathermap.org/data/2.5/forecast?id=524901&APPID=b3d57a41f87619daf456bfefa990fce4&units=metric")!
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
do {
let json = try JSONDecoder().decode(WeatherForecast.self, from: data)
completion((json, true))
} catch {
print(error)
completion((nil, false))
}
} else {
print(error)
}
}
task.resume()
}
Everything works fine here. JSON loads correctly and fits the data model.
Here's a link to JSON data to be displayed in tableView: https://pastebin.com/KkXwxYgS
(2) A controller handles the display of retrieved JSON data in tableView format
import UIKit
class ForecastViewController: UITableViewController {
#IBOutlet weak var tableV: UITableView! // tableView outlet in the IB
let weatherGetter = GetWeather() // object to handle the JSON retrieval
var tableData: WeatherForecast? // tableView data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData?.list.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
cell.dateLabel.text = "\(self.tableData?.list[indexPath.row].dt)"
cell.tempLabel.text = "\(self.tableData?.list[indexPath.row].main.temp)"
cell.feelsLikeLabel.text = "\(self.tableData?.list[indexPath.row].main.feels_like)"
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
tableV.delegate = self
tableV.dataSource = self
weatherGetter.getMowForecast { (data, status) in
if let data = data, status {
} else if status {
print("-------- Ошибка разбора данных прогноза погоды --------")
} else {
print("-------- Ошибка получения данных прогноза погоды --------")
}
self.tableData = data
print(self.tableData)
}
print(self.tableData?.list.count) // returns nil
self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'
}
}
The problem is - the table view gets nil datasource so it is unable to load the data and shows the blank screen.
I suppose the mistake is in scope - I try to retrieve the JSON data inside a function and it does not go anywhere else. What I am wondering about is - how comes that assigning the data to self.tableData does not makes any effect?
Could you please help.
Thank you!
Regards
First of all delete
print(self.tableData?.list.count) // returns nil
self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'
The error occurs because the method does not return anything and the completion handler syntax is wrong. Both lines are pointless anyway due to the asynchronous behavior of getMowForecast
Secondly I recommend to declare the data source array as a non-optional array of the type which represents List. Then you get rid of all those unnecessary optionals.
var tableData = [List]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
let weatherData = self.tableData[indexPath.row]
cell.dateLabel.text = "\(weatherData.dt)"
cell.tempLabel.text = "\(weatherData.main.temp)"
cell.feelsLikeLabel.text = "\(weatherData.main.feels_like)"
return cell
}
To be able to display the data – as already mentioned by others – you have to reload the table view in the completion handler. And assign the data only if status is true.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
tableV.delegate = self
tableV.dataSource = self
weatherGetter.getMowForecast { [weak self] (data, status) in
if let data = data, status {
self?.tableData = data.list
DispatchQueue.main.async {
self?.tableV.reloadData()
}
} else if status {
print("-------- Ошибка разбора данных прогноза погоды --------")
} else {
print("-------- Ошибка получения данных прогноза погоды --------")
}
}
}
And consider that the message Ошибка разбора данных прогноза погоды will be never displayed.
You need to reload the table inside the callback as it's asynchronous
self.tableData = data
print(self.tableData)
DispatchQueue.main.async { self.tableV.reloadData() }

Assign value received from #escaping to instance variable in another class in swift

I believe that I misunderstood some conception in Swift and can assign received array to my instance variable. Can somebody explain why overall my announcementsList array has 0 elements?
UIViewController.swift
var announcementsList: [Announcement] = []
override func viewDidLoad() {
super.viewDidLoad()
api.getAnnouncements(){ announcements in //<- announcements is array which has 12 elements
for ann in announcements{
self.announcementsList.append(ann)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return announcementsList.count //<- have 0 here
}
API.swift
func getAnnouncements(completion: #escaping ([Announcement]) -> ()){
var announcements: [Announcement] = []
let url = URL(string: "https://api.ca/announcements")!
let task = self.session.dataTask(with: url) {
data, response, error in
if let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
guard let announcements_json = json!["announcements"] as? [[String: Any]]
else { return }
for announcement in announcements_json{
let title = announcement["title"] as! String
let desc = announcement["description"] as! String
announcements.append(Announcement(title: title,desc: desc))
}
}
completion(announcements)
}
task.resume()
}
P.S.: In my defence, I should say code works in Java pretty well
UPD
In UIViewController.swift if glance inside my announcementsList in viewWillDisappear() I will get my objects there. So I assume that tableView() started count elements earlier then they became reassigned in viewDidLoad().
The question now how to assign objects inide viewDidLoad() to a new array faster than tableView() start count them.
var announcementsList: [Announcement] = [] {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
api.getAnnouncements { announcements in
self.announcementsList = announcements
}
}
The operation is asynchronous , so the tableView reloads when the VC appears and at that moment the response isn't yet return
for ann in announcements{
self.announcementsList.append(ann)
}
self.tableView.reloadData()
BTW why not
self.announcementsList = announcements
self.tableView.reloadData()
Also don't know current thread of callback , so do
DispatchQueue.main.async {
self.announcementsList = announcements
self.tableView.reloadData()
}

Json Data not properly reflecting in tableView

Loading local json file to table view and in Debugger Log all goes fine but data is repeating itself in tableView. I've taken screenshot of simulator and Log both here - May be i think i've a problem in appending data. I have TableViewCell - viewCell and my data class - attendance.swift and a tableViewController of course. I am trying to display two data fields. Code for tableViewController -
var checkins = [attendance]()
override func viewDidLoad() {
super.viewDidLoad()
jsonParsingFromFile()
}
func jsonParsingFromFile()
{
let path: NSString = NSBundle.mainBundle().pathForResource("jsonFile", ofType: "json")!
let data : NSData = try! NSData(contentsOfFile: path as String, options: NSDataReadingOptions.DataReadingMapped)
self.parseJsonData(data)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
func parseJsonData(data:NSData) -> [attendance]{
do{
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
//parse json data
let jsonCheckins = jsonResult?["university1"] as! [AnyObject]
for jsonAttendance in jsonCheckins {
let checkin = attendance()
checkin.id = jsonAttendance["id"] as! Int
checkin.name = jsonAttendance["name"] as! String
if (creden != checkin.id)
{
}
else
{
print(checkin.id)
print(checkin.name)
let check = jsonAttendance["attendance"] as! [AnyObject]
for ch in check {
checkin.subject = ch["subject"] as! String
print(checkin.subject)
checkin.attended = ch["attended"] as! Int
checkin.done = ch["held"] as! Int
checkin.atd = (Float(checkin.attended)/Float(checkin.done))*100
print(checkin.atd , " %")
checkins.append(checkin)
}
}
}
}
catch{
print(error)
}
return checkins
}
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 {
// #warning Incomplete implementation, return the number of rows
return checkins.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! viewCell
cell.subjectLabel.text = checkins[indexPath.row].subject
cell.attendanceLabel.text = String(checkins[indexPath.row].atd)+" %"
return cell
}
Error seems to be because you are updating a single object only and adding it to array. Therefore in the end you are left up with only one type of object in array with same values
let checkin = attendance()
move that to inside the loop
for ch in check {

ReloadData not working Swift with Alamofire

I tried to reload my UITableView after adding new items. When I try with a reloadData() it's not working. Nothing is shown.
If I try to reload my getallrecords function, that reload items but they are repeated.
My source code is :
class FriendsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate {
#IBOutlet var tabeview: UITableView!
var textArray: NSMutableArray! = NSMutableArray()
var subArray: NSMutableArray! = NSMutableArray()
let defaults = NSUserDefaults.standardUserDefaults()
var valueToPass:String!
var reports_d:String!
var reports:String!
#IBOutlet var menuButton: UIBarButtonItem!
#IBOutlet var friends_icon: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
tabeview.dataSource = self
tabeview.delegate = self
tabeview.emptyDataSetSource = self
tabeview.emptyDataSetDelegate = self
tabeview.tableFooterView = UIView()
getallrecords()
self.tabeview.addPullToRefresh({ [weak self] in
// refresh code
self!.getallrecords()
self?.tabeview.stopPullToRefresh()
})
// Do any additional setup after loading the view.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.textArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = self.textArray.objectAtIndex(indexPath.row) as? String
cell.detailTextLabel?.text = self.subArray.objectAtIndex(indexPath.row) as? String
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel!.text
reports = reports_d
performSegueWithIdentifier("friends_details", sender: self)
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
// handle delete (by removing the data from your array and updating the tableview)
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
let friend2 = currentCell.textLabel!.text
let defaults = NSUserDefaults.standardUserDefaults()
let username = defaults.objectForKey("name") as! String
Alamofire.request(.GET, "http://www.example.com/app/remove_friends.php", parameters: ["key_id": "xxxxx","user_id": username,"friend_receive_id": friend2!, "action": "delete"])
.response { request, response, data, error in
print(request)
print(response)
print(error)
if(error == nil)
{
self.tabeview.beginUpdates()
self.textArray.removeObjectAtIndex(indexPath.row)
self.subArray.removeObjectAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.tabeview.endUpdates()
}
}
NSNotificationCenter.defaultCenter().postNotificationName("reloadData",object: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "friends_details") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destinationViewController as! DetailsFriendsViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
viewController.reports = reports
}
}
func getallrecords(){
if(defaults.stringForKey("name") != nil ){
let username = defaults.objectForKey("name") as! String
let full = "http://www.example.com/app/danger_friend_view.php?search=true&username=" + username
let url = NSURL(string: full)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
do {
let d = NSString(data: data!, encoding: NSUTF8StringEncoding)
var arr = d!.componentsSeparatedByString("<") // spliting the incoming string from "<" operator because before that operator is our required data and storing in array
let dataweneed:NSString = arr[0] as NSString // arr[0] is the data before "<" operator and arr[1] is actually no use for us
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
SwiftSpinner.hide()
do {
if let data = try NSJSONSerialization.JSONObjectWithData(dataweneed.dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSJSONReadingOptions.MutableContainers]) as? NSArray {
for dd in data{
var name : String = dd["danger"]! as! String
self.reports_d = name
let info : String = dd["username"]! as! String
name = NSLocalizedString("SEND_ALERT_BEGIN",comment:"SEND_ALERT") + name + NSLocalizedString("ALERTS",comment:"ALERTS")
print("ID is : \(name)")
print("Username is : \(info)")
self.textArray.addObject(info)
self.subArray.addObject(name)
}
self.tabeview.reloadData()
}
} catch let error as NSError {
print(error.localizedDescription)
}
})
}
}
task.resume()
}
else
{
//Do something
}
}
#IBAction func reload_data(sender: UIButton) {
let banner = Banner(title: NSLocalizedString("RELOAD_DATA_TITLE",comment:"I'm in danger, I'm currently at "), subtitle: NSLocalizedString("RELOAD_DATA",comment:"I'm in danger, I'm currently at "), image: UIImage(named: "Icon"), backgroundColor: UIColor(red:52.00/255.0, green:152.00/255.0, blue:219.00/255.0, alpha:0.89))
banner.dismissesOnTap = true
banner.show(duration: 10.0)
dispatch_async(dispatch_get_main_queue()) {
//Not working ....
self.tabeview.reloadData()
}
}
func titleForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = "Oups"
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)]
return NSAttributedString(string: str, attributes: attrs)
}
func descriptionForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = NSLocalizedString("NO_FRIENDS_TO_SHOW",comment:"No friends to show ")
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
return NSAttributedString(string: str, attributes: attrs)
}
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
return UIImage(named: "no-friends")
}
func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! {
let str = NSLocalizedString("ADD_FRIENDS",comment:"Add a friend ")
let attrs = [NSFontAttributeName: UIFont(name: "HelveticaNeue-Light", size: 19)!]
return NSAttributedString(string: str, attributes: attrs)
}
func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
let alert = SCLAlertView()
let txt = alert.addTextField("Friend's username")
alert.addButton("Add") {
if(txt.text=="")
{
let banner = Banner(title: NSLocalizedString("ERROR_NO",comment:"An error occured"), subtitle: NSLocalizedString("ERROR_NO_TEXT",comment:"I'm in danger, I'm currently at "), image: UIImage(named: "Icon"), backgroundColor: UIColor(red:152.00/255.0, green:52.00/255.0, blue:52.00/255.0, alpha:0.89))
banner.dismissesOnTap = true
banner.show(duration: 10.0)
}
else
{
let defaults = NSUserDefaults.standardUserDefaults()
let username = defaults.objectForKey("name") as! String
let remove_friend_username = txt.text! as String
Alamofire.request(.GET, "http://www.example.com/add_friends.php", parameters: ["key_id": "xxx","user_id": username,"friend_receive_id": remove_friend_username, "action": "add"])
.response { request, response, data, error in
dispatch_async(dispatch_get_main_queue()) {
self.tabeview.reloadData()
//Not working
}
}
}
}
alert.showEdit("Add friend", subTitle: "You can add a friend by enter his username")
}
}
I believe you are missing a little point in here buddy :)
Question 1
Why reloading tableView wont show new data ??
Your function reload_data is doing nothing more than reloading data buddy :) When you call reload data all the tableView delegates like number of rows in section,number of sections and cellForRowAtIndexPath gets called but all these methods return the value depending on the data source you provide isn't it buddy :)
So if you change the data source and then call reload data they will show you the new data :) but in your reload_data function you are not altering the data source at all :) simply calling reload data on the unalterred data source will re render the tableView again thats all :)
What you can do :)
You already have a method that fetches the new data using almofire :) just call it and in the success block anyway you are reloading the tableView :) So everything will be fine buddy :)
#IBAction func reload_data(sender: UIButton) {
let banner = Banner(title: NSLocalizedString("RELOAD_DATA_TITLE",comment:"I'm in danger, I'm currently at "), subtitle: NSLocalizedString("RELOAD_DATA",comment:"I'm in danger, I'm currently at "), image: UIImage(named: "Icon"), backgroundColor: UIColor(red:52.00/255.0, green:152.00/255.0, blue:219.00/255.0, alpha:0.89))
banner.dismissesOnTap = true
banner.show(duration: 10.0)
self.getallrecords() //simply call this method this will anyhow will reload data on success :)
}
Question 2
Why my tableView shows duplicate data???
Your tableView always show the data which is there in its datasource :) SO if your tableView is showing duplicate cells that means you have duplicate entry in your data source :)
You are dealing with array, in future you might migrate to coredata :)
Understand one thing, when you enter or add a entry to your data source if you dont want to show duplicates you will have to handle it explicitly.
How can I do that ???
From your code I beilieve info(username) value is unique per object. So before blindly adding response to textArray check if text array already consists that object if yes then dont add it again :)
Based on the above stated assumption and believing you are making use of swift 2.0
if let data = try NSJSONSerialization.JSONObjectWithData(dataweneed.dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSJSONReadingOptions.MutableContainers]) as? NSArray {
for dd in data{
var name : String = dd["danger"]! as! String
self.reports_d = name
let info : String = dd["username"]! as! String
name = NSLocalizedString("SEND_ALERT_BEGIN",comment:"SEND_ALERT") + name + NSLocalizedString("ALERTS",comment:"ALERTS")
print("ID is : \(name)")
print("Username is : \(info)")
if !self.textArray.contains(info){
self.textArray.addObject(info)
self.subArray.addObject(name)
}
}
self.tabeview.reloadData()
}
Now that's a lot of code, I want a easier solution :)
Clear the array before adding the new response :) Thats all :)
if let data = try NSJSONSerialization.JSONObjectWithData(dataweneed.dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSJSONReadingOptions.MutableContainers]) as? NSArray {
self.textArray.removeAll()
self.subArray.removeAll() //clear the arrays and then re populate them thats all no duplicate data anymore :P
for dd in data{
var name : String = dd["danger"]! as! String
self.reports_d = name
let info : String = dd["username"]! as! String
name = NSLocalizedString("SEND_ALERT_BEGIN",comment:"SEND_ALERT") + name + NSLocalizedString("ALERTS",comment:"ALERTS")
print("ID is : \(name)")
print("Username is : \(info)")
self.textArray.addObject(info)
self.subArray.addObject(name)
}
self.tabeview.reloadData()
}

Dynamically populating an iOS table view with Swift from an API

I'm currently in the process of creating an app to display the latest football scores. I've connected to an API through a URL and pulled back the team names for the english premier league into an array of strings.
The problem seems to come from populating the iOS table view that I intend to display the list of teams with. The data appears to be pulled from the API fine, but for some reason the TableView method which creates a cell and returns it doesn't seem to be called. The only time I can get the method to be called is when I actually hard code a value into the array of team names.
Here is my code:
class Main: UIViewController {
var names = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let URL_String = "https://football-api.com/api/?Action=standings&APIKey=[API_KEY_REMOVED]&comp_id=1204"
let url = NSURL(string: URL_String)
let urlRequest = NSURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let teams = json["teams"] as? [[String : AnyObject]] {
for team in teams {
if let name = team["stand_team_name"] as? String {
self.names.append(name)
}
}
}
} catch {
print("error serializing JSON: \(error)")
}
})
task.resume()
}
// Number of Sections In Table
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// Number of Rows in each Section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
// Sets the content of each cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = names[indexPath.row]
return cell
}
}
Just wondering if anyone can point me in the right direction here. This code doesn't crash or throw any errors, it just refuses to load a table view. The only reason I can possibly think of is that the array of team names is empty after completing a request to the API. However I've set breakpoints throughout and checked the values of local variables and the desired information is being pulled from the API as intended...
you are in the correct way , just refresh the table using reloadData once you got the new data from API
if let teams = json["teams"] as? [[String : AnyObject]] {
for team in teams {
if let name = team["stand_team_name"] as? String {
self.names.append(name)
}
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.yourtableViewname.reloadData()
})
}

Resources