Not populating UiTableView with Parse.com results in SWIFT - ios

I am hoping someone can help, as I am trying to debug, but am going round in circles.
I have a table in Parse.com and can query and retrieve data successfully.
I did a test with a println and the correct values of the strings are displayed in the output.
What I was trying to do was put these values into a UITableView, but this has taken me down some pretty frustrating paths (I am still trying to learn this as best as I can and sometimes some concepts are hard to comprehend).
My last attempt (see code below) I thought by writing the values to a struct I could use this as I have done in the past, given that I can see the values I need to populate. I don't think this is the right way but I thought it should work.
My code when I put a breakpoint in doesn't get to even defining the tableview :(
I know I am missing something but maybe just need a fresh pair of eyes to help me see what I am missing.
Any help would be greatly appreciated:
#IBOutlet weak var navlabel: UILabel!
var TopicPassed:String!
var storedsentences=[getsentences]()
#IBOutlet weak var sentencetableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navlabel.text = TopicPassed
var query = PFQuery(className:"TalkToMeSentences")
query.whereKey("Topic", equalTo:TopicPassed)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// query successful - display number of rows found
println("Successfully retrieved \(objects.count) sentences")
// print sentences found
for object in objects {
let retrievedsentences = object["Sentence"] as NSString
self.storedsentences = [getsentences(parsesentence: "\(retrievedsentences)")]
println("\(retrievedsentences) ")
}
self.sentencetableview.reloadData()
} else {
// Log details of the failure
println("Error: \(error) \(error.userInfo!)")
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return storedsentences.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
var sentence : getsentences
// Configure the cell...
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
cell.textLabel!.lineBreakMode = NSLineBreakMode.ByWordWrapping
sentence = storedsentences[indexPath.row]
cell.textLabel!.text = sentence.parsesentence
cell.textLabel?.numberOfLines = 0
return cell
}

Resolved it, I think.
My problem was I had not assigned outputs for the the datasource or the delegates.
Once I did I could get the table to populate.

Related

sort tableViewCells by numbers inside a label

I try to sort the tableViewCells by numbers inside a label, so the cell which includes the highest number in a label should be last, and vice versa.
I tried it with different solutions like following, but it's simply not working, it also doesn't show any error code
I don't know if there is just a small mistake or if it is all completely wrong, but if so, I hope that you know a completely different way to solve it.
TableView:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// download jobs
jobsRef.observe(.value, with: { (snapshot) in
self.jobs.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let job = Job(snapshot: childSnapshot)
print(job)
self.jobs.insert(job, at: 0)
}
filterLocation()
self.tableView.reloadData()
})
}
var jobArr = JobTableViewCell.jobDistance!.jobArr
func filterLocation() {
jobArr.sort() { $0.distance.text > $1.distance.text}
}
TableViewCell:
#IBOutlet weak var distance: UILabel!
static var jobDistance: JobTableViewCell?
var jobArr = [JobTableViewCell.jobDistance!.distance.text]
override func layoutSubviews() {
super.layoutSubviews()
JobTableViewCell.jobDistance = self
}
lets check out apple doc for the table view https://developer.apple.com/documentation/uikit/uitableviewdatasource
as it says there is method:
func tableView(UITableView, cellForRowAt: IndexPath) -> UITableViewCell
we can read it like "give me[UITableView] cell[-> UITableViewCell] for this index[cellForRowAt]"
so all we need is just map our data source to tableview indexes:
e.g.
we have datasource array of strings
var dataSource = ["String", "Very long string", "Str"]
sort...
> ["Str", "String", "Very long string"]
and then just provide our data to cell (your tableview must conform UITableViewDataSource protocol)
// Provide a cell object for each row.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch a cell of the appropriate type.
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTypeIdentifier", for: indexPath)
// Configure the cell’s contents.
cell.textLabel!.text = dataSource[indexPath]
return cell
}
The problem is you sort another array jobArr
jobArr.sort() { $0.distance.text > $1.distance.text}
and append values to another one jobs

First cell of custom UITableViewCell is blank

I am querying parse for some information and displaying the info in a UITableViewCell, there is only one entry in the table, a test entry with fictional information but the table is displaying the first cell blank and then the second cell with the information in the table. I have attempted to google this and also add more rows of testing info but either way the table displays one cell at the beginning with no info
Here is my code:
var query = PFQuery(className: "marathons")
query.orderByAscending("end")
query.findObjectsInBackgroundWithBlock { (marathons, error:NSError?) -> Void in
if(error == nil ){
//success
for marathon in marathons! {
self.Name.append(marathon["Name"] as! String)
self.entryNumber.append(marathon["Number"] as! Int)
self.totalEntries.append(marathon["entries"] as! Int)
self.runnerDistance.append(marathon["distance"] as! Int)
}
self.TableView.reloadData()
}else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: TableView
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Name.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
singleCell.Name.text = names[indexPath.row]
singleCell.entryNumber.text = "\(entryNumbers[indexPath.row])"
singleCell.totalEntries.text = "\(entires[indexPath.row])"
singleCell.runnerDistance.text = "\(distance[indexPath.row])"
return singleCell
}
Name, entryNumber, totalEntries, and runnerDistance defined as:
#IBOutlet var Name: UILabel!
#IBOutlet var entryNumber: UILabel!
#IBOutlet var totalEntries: UILabel!
#IBOutlet var runnerDistance: UILabel!
Any advice? I am using swift, parse as my back end, and XCODE 7
Where are the model objects defined in your code? You're appending to objects named Name, entryNumber, totalEntries, and runnerDistance, but you're trying to assign values from properties named entryNumbers entries and distance.
Edit:
I see what's going on here. Based off your comment, you're initializing your arrays like this:
var names = [String()]
That actually creates an array with one empty string in it [""]. When you call append, you're adding another element to the array, which makes you wind up with ["","John"]. if you want an empty array you should be creating it like this:
var names = [String]()

SWIFT: Data model for "Like" button functionality in iOS app

I'm creating a playlist app (Mock-Up Below) using Swift and Parse. Each user has the ability add a song (or multiple songs) to the TableView and "Like" or "Dislike" (toggle style) as many songs as they like, including their own. This functionality is very similar to the Instagram "Like" button behaviour.
I'm having a bit of trouble conceptualizing the data model. As shown below, I have a User table and a Playlist table (with all of the added songs):
User Table:
Playlist Table:
I'm having trouble with the next step, which is storing the "Like" data for each user and each song.
UPDATED: I've added a column in the 'PlaylistData' table in Parse called "userVotes". As you can see, I'm appending all of the data into an array called 'voters'. For some reason, I'm getting a fatal run time error - 'fatal error: unexpectedly found nil while unwrapping an Optional value'.
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!)
self.voters = object["userVotes"] as! [String]
print(self.voters)
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func viewWillAppear(animated: Bool) {
}
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.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I've also created a TableViewCell swift file for the prototype cell:
import UIKit
import Parse
class TrackTableViewCell: UITableViewCell {
var numVotes = 0
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var votes: UILabel!
#IBAction func voteButton(sender: UIButton) {
self.numVotes = self.numVotes + 1
self.votes.text = "\(self.numVotes)"
}
}
At this point, all it does is when the heart is clicked, it increases the vote count by 1 on screen (the user can do this as many times as possible, no data is stored in the database). What I need help with is the best way to implement table(s) which allow me to keep track of which users have voted for each song (or not). I know I need a TRUE / FALSE column somewhere, but I can't seem to figure out how to model it. Any suggestions or links to SWIFT tutorials would be greatly appreciated. A simple explanation on what tables to create (or what columns to add to existing tables) would help the best. Thanks!

Connecting TableView within ViewController - self.tableView.reloadData() not working in Swift

I'm a newbie learning iOS and Swift so apologies ahead of time. Currently I'm trying to setup a tableView within a viewController and display data in the cells in a portion of the screen. My current problem seems to be in reloading the tableView data after the Alamofire HTTP request in viewDidLoad() is called for numberOfRowsInSection(). Here's the code:
import UIKit
import Alamofire
import SwiftyJSON
class CourseDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var titleLabel: UILabel?
#IBOutlet weak var creditsLabel: UILabel?
#IBOutlet weak var descriptionLabel: UILabel?
#IBOutlet weak var tableView: UITableView!
var detailCourse: Course? {
didSet {
configureView()
}
}
var course: Course!
func configureView() {
self.title = detailCourse?.abbr
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "SectionCell")
tableView.delegate = self
tableView.dataSource = self
if let theCourse: Course = self.detailCourse as Course! {
var abbr: String = theCourse.abbr!
APIService.getCourseByAbbr(abbr) { (data) -> Void in
self.course = Course(courseJSON: data)
// Set labels
self.titleLabel?.text = self.course.title!
self.descriptionLabel?.text = self.course.description!
if let creditsArray = self.course.credits {
let minimumCredit = creditsArray[0] as Int
self.creditsLabel?.text = String(minimumCredit)
}
self.tableView.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return course.sections.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("SectionCell", forIndexPath: indexPath) as! SectionTableViewCell
let sectionCell = course.sections[indexPath.row]
cell.termLabel?.text = sectionCell.term
cell.timeLabel?.text = sectionCell.startTime
cell.instructorLabel?.text = sectionCell.instructor
return cell
}
}
When I run, I get the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I believe that the reason may be that I set up the tableView within the viewController incorrectly.
For the full project, here is a link to the repo: https://github.com/classmere/app/tree/develop
The problem is that you're trying to unwrap an optional whose value is nil. When you declare the course property, since its an optional, its initial value is nil. Usually, optionals are declared with ? and the compiler will prevent you from accessing the underlying value without checking if the value is still nil. In this case however, you've made the course property an expected optional:
var course: Course!
This is like saying "I know that course will always have a value and will never be nil". We don't know that however, since its value is nil until the Alamofire callback successfully completes.
To fix this problem, start by making course a standard optional:
var course: Course?
Now Xcode will complain that you're accessing course without unwrapping it, since your declaration of course no longer unwraps it.
Fix this by forcibly unwrapping everything in the Alamofire callback:
APIService.getCourseByAbbr(abbr) { (data) -> Void in
println("APIService()")
self.course = Course(courseJSON: data)
// Notice we can access self.course using ! since just assigned it above
self.titleLabel?.text = self.course!.title!
self.descriptionLabel?.text = self.course!.description!
if let creditsArray = self.course!.credits {
let minimumCredit = creditsArray[0] as Int
self.creditsLabel?.text = String(minimumCredit)
}
self.tableView.reloadData()
}
Then in cellForRowAtIndexPath, we will use optional chaining to ensure we only access course's properties if they exist:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("SectionCell", forIndexPath: indexPath) as! SectionTableViewCell
if let section = course?.sections[indexPath.row] {
cell.termLabel?.text = section.term
cell.timeLabel?.text = section.startTime
cell.instructorLabel?.text = section.instructor
}
return cell
}
Finally in numberOfRowsForSection make sure to get the actual number of sections instead of always returning 50. We'll use the nil-coalescing operator to return 0 if course is nil:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return course?.sections.count ?? 0
}
That should fix your problem!

UITableView Swift questions

I want to query using Parse to add strings to an array. Then I want to put those strings into the cells of my UITableView. However, every time I run the app nothing seems to appear on my table. Here is my code if someone could help explain some of the reasons that it may not be appearing
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var friendsArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
var usrname = currentUser?.username
var query = PFQuery(className:"Relation")
query.whereKey("Sender", equalTo : usrname!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
//println("Successfully retrieved \(objects) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
var friendName = object["Friend"] as! String
println(friendName)
self.friendsArray.append(friendName)
}
}
}
else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendsArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
cell.textLabel?.text = self.friendsArray[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
You need to call [self.tableView reloadData]; in the completion block of the findObjectsInBackground: call.
At the end of your if error == nil{...} statement, load your table data like this:
if error == nil{
//All that other stuff...
tableView.reloadData()
}
Also make sure your tableView has its delegate and datasource connected to your view controller. This may require putting this in viewDidLoad:
tableView.delegate = self
tableView.datasource = self
I also find it peculiar that your tableView is declared as weak, but that may not be relevant.
Try these things and let us know if you're still having trouble. Good luck! :)
The app does not know when your data will be returned so that you have to explicitly to refresh the UITableView once you have successfully received the data. Use [self.tableView reloadData] to reload the UITableView later on.
The UITableView will only load once when UIViewController gets loaded. Unless, you have already have data the time when UIViewController loads. Otherwise, the number of rows will be 0 at the first time and the delegate method cellForRowAtIndexPath will not be called.
findObjectsInBackgroundWithBlock is an async call when you take a look at the documentation. It's not like its literal meaning that it's running in the background. It means that it won't block the current thread so that users can still have interaction with the application.
When you call findObjectsInBackgroundWithBlock its doing exactly what it name says, specifically its running in the background.
Meanwhile while its running in the background your table view code continues to run in the foreground, effectively in parallel.
So your viewDidLoad function will exit and numberOfRowsInSection will be called next, but at that time if findObjectsInBackgroundWithBlock has not yet finished then friendsArray.count will be 0.
just try these things. it will help you
after you get array of element make reload the uitableView.
if you use storyboard check the datasource outlet
it you did not use storyboard or xib, then you set datasource in coding wisse
hi you can use like this
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
cell.textLabel?.text = app.str[indexPath.row]
print(" cell \(cell.textLabel?.text!)")
return cell
}
TableOutLet.reloadData()
Conform your tableView to its delegate and datasource by written below two lines in your viewDidLoad() after super.viewDidLoad():
tableView.datasource = self
tableView.delegate = self
Then update this code as below :
if let objects = objects as? [PFObject] {
for object in objects {
var friendName = object["Friend"] as! String
println(friendName)
self.friendsArray.append(friendName)
}
tableView.reloadData()
}
After that in tableView delegate methods return self.friendsArray.count for numberOfRows() method and also put your code in cellForRow also.
Just place your line after appending elements in array.
override func viewDidLoad() {
super.viewDidLoad()
var usrname = currentUser?.username
var query = PFQuery(className:"Relation")
query.whereKey("Sender", equalTo : usrname!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
//println("Successfully retrieved \(objects) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
var friendName = object["Friend"] as! String
println(friendName)
self.friendsArray.append(friendName)
}
}
}
else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
Or you can reload table once your final array up-to-date
As mentionded by another hint you need to call reloadData but you need to call it in main thread to see result as soon as possible
dispatch_async(dispatch_get_main_queue()) {self.tableView.reloadData()}

Resources