iOS Swift get JSON data into tableView - ios

I have a JSON Data which I want to get into UITable. The data is dynamic so table should update every time view loads. Can anyone help?
{
data = (
{
id = 102076330;
name = "Vicky Arora";
}
)
}

try this....
When you receive response,get the whole array of dictionary
if let arr = response["data"] as? [[String:String]] {
YourArray = arr
// Define YourArray globally
}
Then in tableview cell,cellForRowAtIndexPath method
if let name = YourArray[indexpath.row]["name"] as? String{
label.text = name
}
//Same You can done with id
And don't forget to set number of rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return YourArray.count
}

Try this one. But this sample i'm using Alamofire and SwitfyJSON. Import it using CocoaPod.
import UIKit
import Alamofire
class TableViewController: UITableViewController{
var users: [JSON] = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://xxxxx/users.json").responseJSON { (request, response, json, error) in
if json != nil {
var jsonObj = JSON(json!)
if let data = jsonObj["data"].arrayValue as [JSON]?{
self.users = data
self.tableView.reloadData()
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return users.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UserCell", forIndexPath: indexPath) as! UITableViewCell
let user = users[indexPath.row]
if let idLabel = cell.viewWithTag(100) as? UILabel {
if let id = user["id"].string{
idLabel.text = id
}
}
if let nameLabel = cell.viewWithTag(101) as? UILabel {
if let name = user["name"].string{
nameLabel.text = name
}
}
return cell
}
}

If you are up to using Core Data, I would suggest using the NSFetchedRequest.
Every time you are getting the data from the server, save it to Core data, and that will automatically update the table view.
Here is a tutorial from Ray Wenderlich

Related

XCode Swift 3 UITableViewController Cell doesn't show

I am exporting json file from itunes api>converted that to a json file>send that to firebase> brought that in to a dictionary file here and now I am trying to put that into a tableview but it does not seem to work and I have no clue why. I checked that:
1. The file that I imported was successfully converted into a dictionary.
2. Datasource and delegate is connected to this UITableViewController file.
3. Put other arrays to check if my connections were right(and it worked but it does not work at all if I use the data that I brought in with Firebase)
4. I put cell style as subtitle and type as dynamic.
Below is the UITableViewController code:
import UIKit
import Firebase
class TableViewController1: UITableViewController {
var ref: FIRDatabaseReference!
var rank = [String]()
var song = [[String]]()
var artist = [[String]]()
var tna = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
self.ref.child("top100itunes").observeSingleEvent(of: .value, with: { (snapshot) in
let jsonfile = snapshot.value! as! String
//print(jsonfile)
let jsondict:[String:Any] = self.convertToDictionary(text: jsonfile)!
for (key, value) in jsondict {
if key != nil {
self.rank.append(key)
if value != nil{
self.tna.append(value as! String)
}
}
}
for x in self.tna{
if x != nil {
for (key,value) in self.convertToDictionary(text: x)!{
self.song.append([key])
self.artist.append([value as! String])
}
}
}
}, withCancel: nil)
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return self.song.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.song[section].count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.rank[section]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
cell.textLabel?.text = self.song[indexPath.section][indexPath.row]
cell.detailTextLabel?.text = self.artist[indexPath.section][indexPath.row]
return cell
}
}
The json file that I imported is in the structure like:
{ 1 : { Title : Artist },
2 : { Title : Artist },
.....
}
observeSingleEvent() is asynchronous and numberOfRowsInSection() will be getting called before observeSingleEvent() has finished populating the data.
If you put a call to self.tableView.reloadData() after the 2nd for loop then this will cause a refresh of the table view once the data has been populated.
Alternatively you could consider re-architecting your app so it has a separate model component that is responsible for data retrieval so that the data could have been populated before the view is launched if applicable.
Your code is doing this:
viewDidLoad
start firebase async
ask for tableview number of sections/rows
song.count is 0
tableview loads with no cells
firebase async completes and populates song array
... nothing else
After this method you should add tableView reloadData like so:
for x in self.tna{
if x != nil {
for (key,value) in self.convertToDictionary(text: x)!{
self.song.append([key])
self.artist.append([value as! String])
}
}
}
self.tableView.reloadData()
I agree with Halfman that you should rearchitect your application to separate out the model from the controller. Read up on MVC.
I also suggest that instead of maintaining multiple arrays (i.e. song, artist, tna) you use a single struct to manage all this data. Read up on Swift structures.
TableViewController1 isn't an optimal choice for a controller name. Maybe SongListTableViewController is more clear?

Populate an array for the tableView section and the tableView cell, swift

I am trying to implement a TableView like Instagram with one row per section.
I would like to populate two arrays :
first sectionArray to get the row data in function of the section
and object to get the Name of the section.
But when I try to populate sectionArray, I get an error :
"fatal error: Array index out of range"
Do you have an idea of how to fix it??
Thanks!
import UIKit
import ParseUI
import Parse
class TableView: UIViewController, UITableViewDelegate, UITableViewDataSource, CLLocationManagerDelegate {
#IBOutlet weak var tableView : UITableView?
var sectionArray : [[PFFile]] = []
override func viewDidLoad() {
super.viewDidLoad()
self.loadCollectionViewData()
}
var object = [PFObject]()
func loadCollectionViewData() {
let query = PFQuery(className: "Myclass")
// Fetch data from the parse platform
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
// The find succeeded now rocess the found objects into the countries array
if error == nil {
// Clear existing country data
self.object.removeAll(keepCapacity: true)
// Add country objects to our array
if let objects = objects as [PFObject]? {
self.object = Array(objects.generate())
let index = self.object.count as Int
print (index)
for i in 1...index {
//error here!
if let finalImage = self.object[i]["image"] as? [PFFile]
{
self.sectionArray[i] = finalImage
print(self.sectionArray[i])
}
}
}
// reload our data into the collection view
self.tableView?.reloadData()
} else {
// Log details of the failure
print("Error: \(error!) ")
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sectionArray.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionArray[section].count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < self.object.count {
if let namelabel = object[section]["Name"] as? String {
return namelabel
}
}
return nil
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! ListControllerViewCell!
if cell == nil
{
cell = ListControllerViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
if let finalImage = sectionArray[indexPath.section][indexPath.row] as? PFFile //object[indexPath.row]["image"] as? PFFile
{
finalImage.getDataInBackgroundWithBlock{(imageData: NSData?, error: NSError?) -> Void in
if error == nil
{
if let imageData = imageData
{
cell.ImagePromo!.image = UIImage(data:imageData)
}
}
}
if let CommentLabel = sectionArray[indexPath.section][indexPath.row]
//object[indexPath.row]["Comment"] as? String
{
cell.CommentLabel!.text = CommentLabel
cell.CommentLabel!.adjustsFontSizeToFitWidth = true
}
return cell;
}
}
You have a problem in your for in loop :
You should start at 0, not 1 so your call to the loop looks like :
for i in 0..<index
This is the "danger" with for-in loops compared to C-style loops. You are looping the correct number of times, but you exceed your array size by 1 because you are starting at the wrong index.
Try adding Exception Breakpoint to catch the error location exactly,
Also edit your datasource as,
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if(sectionArray.count != 0) {
return sectionArray.count
} else {
return 0;
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(sectionArray.count < section) {
return sectionArray[section].count
} else {
return 0;
}
}

swift parsing CSV file from API does not separate with the delimiter

I'm trying to pass the data into the cells of a tableView. The networking communication works because The list of items appear in the first cell.
For example the list come like: sensor1, sensor2, sensor3,....
but it should be like :
sensor1
sensor2
...
this is how I'm parsing the CSV file
struct ParseCVS {
func parseURL (contentsOfURL: NSURL, encoding: NSStringEncoding) -> ([String])?{
let rowDelimiter = ","
var nameOfSensors:[String]?
do {
let content = try String(contentsOfURL: contentsOfURL, encoding: encoding)
print(content)
nameOfSensors = []
let columns:[String] = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String]
for column in columns {
let values = column.componentsSeparatedByString(rowDelimiter)
if let nameOfSensor = values.first {
nameOfSensors?.append(nameOfSensor)
}
}
}
catch {
print(error)
}
return nameOfSensors
}
}
and this is my TableViewController
class TableViewController: UITableViewController {
// Array which will store my Data
var nameOfSensorsList = [String]()
override func viewDidLoad() {
super.viewDidLoad()
guard let wetterURL = NSURL(string: "http://wetter.htw-berlin.de/phpFunctions/holeAktuelleMesswerte.php?mode=csv&data=1")
else {
return
}
let parseCSV = ParseCVS()
nameOfSensorsList = parseCSV.parseURL(wetterURL, encoding: NSUTF8StringEncoding)!
tableView.estimatedRowHeight = 100.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
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 nameOfSensorsList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MenuTableViewCell
cell.nameLabel?.text = nameOfSensorsList[indexPath.row]
return cell
}
}
if someone have any ideas I would really appreciate it.
You've forgotten to iterate through an array of "values".
Try something like this:
for column in columns {
let values = column.componentsSeparatedByString(rowDelimiter)
print(values.count)
for value in values {
nameOfSensors?.append(value)
}
}

How to populate my tableView in Swift 2 from JSON?

A new programmer here. How would I populate my tableView from this JSON?
My first problem is the JSON Serialization and then plugging it in the tableView.
Code
import UIKit
class LegislatorsTableVC: UITableViewController {
// MARK: Variables & Outlets
private let cellIdentifer = "cellReuse"
// MARK: View Did Load
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if let error = error {
print("The error is: \(error)")
return
}
// Response
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200, let data = data {
print("Status Code: \(httpResponse.statusCode)")
// self.JSONSerialization(data)
}
}).resume()
}
} // End Of View Did Load
// JSON Serialization Function With SwiftyJSON.swift
private func JSONSerialization(data: NSData){
// I See this Gets A Status Code 200 And Then I'm Lost.
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
} // End Of JSONSerialization
// MARK: - Table view data source
// Number Of Sections
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
} // End Of Number Of Sections
// Number Of Rows In Section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 15
} // End Of Number Of Rows In Section
// Cell For Row At Index Path
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
// Configure the cell...
cell.name.text = "Name"
cell.title.text = "Title"
cell.party.text = "Party"
return cell
} // End Of Cell For Row At Index Path
}
Create a custom class Person outside the view controller
class Person {
var firstName = ""
var lastName = ""
var title = ""
var party = ""
}
Create an array of Person in the view controller
var people = [Person]()
The JSON has a key results which contains an array of dictionaries.
In viewDidLoad parse the JSON and create Person instances. Finally reload the table view.
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if error != nil {
print("The error is: \(error!)")
return
} else if let jsonData = data {
do {
let parsedJSON = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String:AnyObject]
guard let results = parsedJSON["results"] as? [[String:AnyObject]] else { return }
for result in results {
let person = Person()
person.firstName = result["first_name"] as! String
person.lastName = result["last_name"] as! String
person.party = result["party"] as! String
person.title = result["title"] as! String
self.people.append(person)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
} // End Of View Did Load
The table view delegate methods look very clear when using a custom class.
Since cellForRowAtIndexPath is called very often the code is quite effective.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let person = people[indexPath.row]
cell.name.text = person.firstName + " " + person.lastName
cell.title.text = person.title
cell.party.text = person.party
return cell
} // End
Of course I couldn't test the code but this might be a starting point.
Basically what you want to do is introduce a new variable to your class, for example jsonDict like so:
class LegislatorsTableVC: UITableViewController {
var jsonDict:Dictionary<String,AnyObject>?
// further code
And then - you almost got it right already - save your JSON serialization into that in your JSONSerialization function. (which I would rename to parseJSON or something like that to avoid confusion) like so:
do {
jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
So then you can return the right values to your tableView data source:
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return jsonDict["your JSON key"].count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonDict["your JSON key"]["items"].count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let item = jsonDict["your JSON key"][indexPath.row]
// Configure the cell...
cell.name.text = item["name"]
cell.title.text = item["title"]
cell.party.text = item["party"]
return cell
}
Naming is a little confusing, as I don't know the layout of your JSON, but replace your JSON key with your path to the data of course.

SWIFT: Parse column is not appending into array

I'd like to append the 'userVotes' column in the following parse table into an array using Swift -
Here is my code -
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var voters = [String]()
var numVotes = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.voters.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
let title = object["userVotes"]! as? String
self.voters.append(title!)
print("Array: \(self.voters)")
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
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 usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
//cell.username.text = usernames[indexPath.row]
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I would like the parse array column to append as follows -
[["user1,"user5,"user9"],["user1,"user2,"user3"],["user4,"user5,"user6"],...]
At this point, I'm getting the following runtime error - fatal error: unexpectedly found nil while unwrapping an Optional value
Since each object that is in your "userVotes" is an array and your you've declared
var voters = [String]()
which is not right because you're saying that there will be one element being appended which is not the case.
So, you should declare voters as...
var voters = Array<Array<String>>()
then as you are downloading it,
for object in objects {
let title = object["userVotes"]! as? [String]
self.voters.append(title!)
print("Array: \(self.voters)")
}

Resources