I have some issue to display the 'author's username of each posts in my TableViewController.
It actually display the current user's username for all display posts, how to display each poster's username ?
I'm using Xcode 6.3 and Parse.com API.
The timeLabel is displayed correctly, but the userLabel display the current user who is logged in instead of the author of the post.
If I logged out and login with a different username all the userLabel change to the new user. The debug console display Optional("theNameOfTheCurrentUser") as many times as there are posts displayed.
Parse host 2 DB one for users (User) and one for posts (Poemes), there is a pointer in Poemes table to the specific user.
I update to Xcode 6.3 lately and had an error on var findLover:PFQuery = PFUser.query()
Value of optional type 'PFQuery?' not unwrapped
I add the exclamation mark (!) at the end of this line, which remove the error, is this causing the issue ?
I read Parse documentation and follow some exemples but looks like I'm a bit lost here, any help and suggestions will be highly appreciated, thanks.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as? String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
return cell
}
The function findUser.findObjectsInBackgroundWithBlock happens in the background, while the main thread still running, so by the time you get the response from parse with the values you need the cell you are trying to return in the function is long gone.
The easiest way to fix it is to fetch all the data you need before hand and safe it in a array and use this array to populate the cell.
Finally get it work for Xcode 6.3.2 changes, here is the result :
unwrap and optional seams to be my main problem :
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:DisplayTableViewCell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as! DisplayTableViewCell
let poeme:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
cell.poemeLabel.text = poeme.objectForKey("content") as! String
var dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd HH:mm"
cell.timeLabel.text = dateFormatter.stringFromDate(poeme.createdAt!)
var findUser:PFQuery = PFUser.query()!
findUser.whereKey("objectId", equalTo: poeme.objectForKey("user")!.objectId!!)
findLover.findObjectsInBackgroundWithBlock {
(objects, error)->Void in
if var objects = objects {
let author:PFUser = (objects as NSArray).lastObject as! PFUser
cell.userLabel.text = author.username
println(author.username)
})
}
}
return cell
}
Related
This is the craziest error I've encountered ever. I am trying to get the value from a column called nameRetailer in the Orders table, but it keeps getting nil.
Other columns of same String type are returning properly including the status column shown below.
What could lead to this? The spelling is certainly correct. Please help....
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = object?.objectForKey("dueDate") as! NSDate
let strDate = dateFormatter.stringFromDate(date)
cell.retailerName.text = object?.objectForKey("nameRetailer") as? String
cell.orderDueDate.text = strDate
cell.orderStatus.text = object?.objectForKey("status") as? String
When I tried to print the value of object?.objectForKey("nameRetailer"), it shows nil in console. In the parse data browser, column has data and was refreshed.
Update: Adding additional code:
The entire class code responsible for the table view:
class OrderViewController: PFQueryTableViewController {
override func queryForTable() -> PFQuery {
let query = PFQuery(className: "Orders")
query.cachePolicy = .CacheElseNetwork
query.orderByAscending("createdAt")
return query
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! OrdersTableViewCell
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = object?.objectForKey("dueDate") as! NSDate
let strDate = dateFormatter.stringFromDate(date)
cell.retailerName.text = object?.objectForKey("nameRetailer") as? String
cell.orderDueDate.text = strDate
cell.orderStatus.text = object?.objectForKey("status") as? String
print(object)
//let imageFile = object?.objectForKey("image") as PFFile
//cell.cellImageView.image = UIImage(named:"placeholder")
//cell.cellImageView.file = imageFile
//cell.cellImageView.loadInBackground()
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row + 1 > self.objects?.count
{
return 44
}
let height = super.tableView(tableView, heightForRowAtIndexPath: indexPath)
return height
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row + 1 > self.objects?.count
{
self.loadNextPage()
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.performSegueWithIdentifier("showDetail", sender: self)
}
}
An image of the table of orders:
and here is the log snapshot of printing PFObject:
And here is the updated snapshots showing the two rows
You have set your cache policy to start by looking up data in the local cache, which may be stale.
Change:
query.cachePolicy = .CacheElseNetwork
to
query.cachePolicy = .NetworkOnly // ignores cache on reading, but saves to cache
or
query.cachePolicy = .IgnoreCache // no cache at all -- this is the default
(or other appropriate value based on your specific context and use case)
I'm having some trouble figuring out of how make the connection between my app and Parse when a user completes a task in my to-do app. I have created the PFObject for it and I have connected the label up and that works great but I am trying to figure out how to work with Bools in Parse.
let obj = PFObject(className: "Tasks")
obj.setValue(task, forKey: "task")
obj.setValue(self.team, forKey: "team")
obj.setValue(self.checked.isChecked, forKey: "done")
obj.saveInBackgroundWithBlock() {success,error in
if error != nil {
print(error)
return
}
if success {
self.loadTasks()
// self.tasks.insert(obj, atIndex: 0)
}
}
}
}))
"checked" is the class instance of CheckBox (all of the brains of the button/checkbox) and "isChecked" is a Bool with a didSet property that changes the checkbox to checked or unchecked based on whether the button is tapped.
The checkbox works great in the app, it just doesn't sync that action.
This is what I have tried so far but to no avail...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath)
// Configure the cell...
cell.selectionStyle = .None
let idx = tasks[indexPath.row]
let task = idx["task"] as! String
if let label = cell.viewWithTag(1) as? UILabel {
label.text = task
}
let done = idx["done"] as! Bool
if let checkBox = cell.viewWithTag(2) as? UIButton {
checked.isChecked = done
}
return cell
}
Any and all help is welcome.
EDIT
I figured out how to create the value and save it to Parse but now I'm having trouble editing that value and sending that new info to Parse. When I refresh the data it just goes back to the default value (false).
let done = idx["done"] as! Bool
if let checkBox = cell.viewWithTag(2) as? CheckBox {
CheckBox.isChecked = done
}
How do I save the new value?
In Parse I accidentally deleted a column called "likes" that counts the number of a likes a user receives for their blog post. I created the column again with the same name but now when I run my app it crashes and I receive this message "unexpectedly found nil while unwrapping an Optional value". It points to my code where its suppose to receive the "likes" in my cellForRowAtIndexPath. I pasted my code below. Is there any way I could fix this issue and stop it from crashing?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> PFTableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("BCell", forIndexPath: indexPath!) as! BlogCell
if let object : PFObject = self.blogPosts.objectAtIndex(indexPath!.row) as? PFObject {
cell.author.text = object["blogger"] as? String
cell.post.text = object["blogPost"] as? String
let dateUpdated = object.createdAt! as NSDate
let dateFormat = NSDateFormatter()
dateFormat.dateFormat = "h:mm a"
cell.timer.text = NSString(format: "%#", dateFormat.stringFromDate(dateUpdated)) as String
let like = object[("likes")] as! Int
cell.likeTracker.text = "\(like)"
}
return cell
}
I would inspect what's going on with the object if I were you. You clearly aren't getting data that you expected to be there. As a stopgap, you can change let like = object["likes"] as! Int to
if let like = object["likes"] as? Int {
cell.likeTracker.text = "\(like)"
}
If you do that, you will also want to implement the prepareForReuse method in BlogCell to set that label's text to nil or else you might have some weird cell reuse bugs.
Where you delete a column from uitableview , you need to delete data from data source, and update the delete index or reload the whole table .
Look for if you are missing that step
I am working on an app that allows you to 'Like' posts. I was implementing the like button, but I got an error that I cannot seem to fix.
I searched in another posts, but I'm unsure of how to fix it.
This is the code I'm using to implement the like button. Do I need to import something into my project? Or unwrap at certain point?
Any help is appreciate it.
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:PostTableViewCell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath!) as! PostTableViewCell
let post = self.objectAtIndexPath(indexPath!) as PFObject
cell.postTextView.alpha = 0
cell.usernameLabel.alpha = 0
cell.timestampLabel.alpha = 0
cell.postTextView.text = post.objectForKey("content") as! String
var dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "yyyy-MM-dd HH:mm"
cell.timestampLabel.text = dataFormatter.stringFromDate(post.createdAt!)
// to get username from the post
var showUsername:PFQuery = PFUser.query()!
//the objectID is the same as the user in the two different tables
showUsername.whereKey("objectId", equalTo: post.objectForKey("user")!.objectId!!)
showUsername.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
let user = (objects as! [PFUser]).last
cell.usernameLabel.text = user!.username
UIView.animateWithDuration(0.5, animations: {
cell.postTextView.alpha = 1
cell.usernameLabel.alpha = 1
cell.timestampLabel.alpha = 1
})
}
}
return cell
}
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
#IBAction func likeButton(sender: UIButton) {
//disables the like button so it can't be pressed again
sender.enabled = false
sender.userInteractionEnabled = false
sender.alpha = 0.5
//get the point in the table view that corresponds to the button that was pressed
//in my case these were a bunch of cells each with their own like button
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = self.objectAtIndexPath(hitIndex!) as PFObject
//this is where I incremented the key for the object
object.incrementKey("likes")
object.saveInBackground() //still gives me error here
self.tableView.reloadData()
NSLog("Top Index Path \(hitIndex?.row)")
}
Update2: Added a photo of the error
Since the view controller is neither an NSFetchedResultsController or a PFQueryTableViewController, you'll have to implement objectAtIndexPath: yourself.
A hint about the code you need is in cellForRowAtIndexPath':
let post:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
Dispensing with the objectAtIndex method on array, just index into the array at the row:
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
Call it wherever the old code appears like this (in likeButton)...
let object = self.objectAtIndexPath(hitIndex) as! PFObject
or, in cellForRowAtIndexPath:...
let post = self.objectAtIndexPath(indexPath) as!PFObject
etc.
I noticed there are many questions on this in Obj-C but I hardly remember Obj-C and each of the answers was specific to the question. Here I get this error: "No index path for table cell being reused" sometimes when the app refreshes. I notice that when I don't refresh but I leave and reopen the table view the formatting is ruined.
Here is my "refresh" method used in a few places:
#IBAction func loadData(){
timeLineData.removeAllObjects()
//pulls the data from the server
var findTimeLineData: PFQuery = PFQuery(className: "Sweets")
findTimeLineData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error: NSError!) -> Void in
if !error{
for object:PFObject! in objects{
self.timeLineData.addObject(object)
}
let tempArray: NSArray = self.timeLineData.reverseObjectEnumerator().allObjects
self.timeLineData = tempArray as NSMutableArray
//reloads the data in the table view
self.tableView.reloadData()
}
}
}
And the tableview method:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
let cell: SweetTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SweetTableViewCell
let sweet: PFObject = self.timeLineData.objectAtIndex(indexPath.row) as PFObject
//part of the animation
cell.sweetTextView.alpha = 0
cell.userNameLabel.alpha = 0
cell.timestampLabel.alpha = 0
cell.sweetTextView.text = sweet.objectForKey("content") as String
//add the date
var dateFormatter: NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm yyyy-MM-dd"
cell.timestampLabel.text = dateFormatter.stringFromDate(sweet.createdAt)
//finds the sweeter associated with a pointer
var findSweeter: PFQuery = PFUser.query()
findSweeter.whereKey("objectId", equalTo: sweet.objectForKey("sweeter").objectId)
findSweeter.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!)-> Void in
if !error{
let user: PFUser = (objects as NSArray).lastObject as PFUser
cell.userNameLabel.text = user.username
}
}
//adds animation
UIView.animateWithDuration(1, animations: {
cell.sweetTextView.alpha = 1
cell.userNameLabel.alpha = 1
cell.timestampLabel.alpha = 1
})
return cell
}
Any idea what is causing the error?
What's causing the problem for you: when you scroll your table, the TableView dequeues the cell that your async call to parse server is trying to manipulate.
You can overcome this problem by:
1- in your Sweets table on Parse, store PFUser object as a pointer
2- in your loadData function, fetch user from the query by includeKey method of PFQuery
If you change according to this, you won't have to query the PFUser every time the cellForRowAtIndexPath is called.