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

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)
}
}

Related

Grouped sections not displaying in tableview

My entire table view is being written programmatically and the data is coming from JSON. I am trying to group the cells by the customer the code seems to be correct but no sections are showing up at all.
Here is the code:
Portfolios.swift
import UIKit
struct Portfolios: Codable {
let customer, serial, rma, model: String
let manufacturer: String
}
PortfolioController.swift
import UIKit
class PortfolioController: UITableViewController {
var portfolios = [Portfolios]()
var portfoliosDic = [String:[Portfolios]]()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
view.backgroundColor = UIColor.blue
navigationItem.title = "Customer"
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try JSONDecoder().decode([Portfolios].self, from: data)
self.portfoliosDic = Dictionary(grouping: res, by: { $0.customer})
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.portfolios = try decoder.decode([Portfolios].self, from: data)
self.tableView.reloadData()
} catch let jsonError {
print("Failed to decode json", jsonError)
}
}
}.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return portfoliosDic.keys.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let keys = Array(portfoliosDic.keys)
let item = portfoliosDic[keys[section]]!
return item.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let keys = Array(portfoliosDic.keys)
let arr = portfoliosDic[keys[indexPath.section]]!
let customer = arr[indexPath.row]
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
//cell.textLabel?.text = titleStr
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = customer.serial
return cell
}
}
UPDATE:
Because it is a UIViewController Xcode told me to remove the override func
and I added #IBOutlet weak var tableView: UITableView!
(The end results is an empty table for some reason)
Using a UITableViewController instead:
import UIKit
class CustomerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var sections = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellId")
navigationController?.navigationBar.prefersLargeTitles = true
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode([Portfolios].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.customer })
let keys = grouped.keys.sorted()
self.sections = keys.map({Section(name: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Failed to decode json", error)
}
}
}.resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].name
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
let section = sections[indexPath.section]
let item = section.items[indexPath.row]
let titleStr = "\(item.serial) - \(item.manufacturer) - \(item.model)"
cell.textLabel!.text = titleStr
return cell
}
}
First of all why do you decode the JSON twice?
No sections are displayed because the method titleForHeaderInSection is not implemented.
The code is not reliable anyway because the order of the sections is not guaranteed. I recommend to create another struct for the sections.
struct Section {
let name : String
let items : [Portfolios]
}
struct Portfolios: Decodable {
let customer, serial, rma, model: String
let manufacturer: String
}
Delete portfolios and portfoliosDic and declare the data source array
var sections = [Section]()
Group the JSON, sort the keys and map the dictionaries to Section instances
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode([Portfolios].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.customer })
let keys = grouped.keys.sorted()
self.sections = keys.map({Section(name: $0, items: grouped[$0]!)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Failed to decode json", error)
}
The table view datasource and delegate methods are
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].name
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
let section = sections[indexPath.section]
let item = section.items[indexPath.row]
let titleStr = "\(item.serial) - \(item.manufacturer) - \(item.model)"
cell.textLabel!.text = titleStr
return cell
}
Note:
Always dequeue cells in cellForRowAt

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;
}
}

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)")
}

iOS Swift get JSON data into tableView

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

Resources