I am having issues setting with Parse data and a UITableView. Everytime I run the application, whether on the simulator or my phone, the app stops working and the only thing the console shows is (lldb). When checking the debugger, only following two lines of code are highlighted.
findTimelineData.findObjectsInBackgroundWithBlock{
and
self.timelineData = array as NSMutableArray
Both have the error: Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT,subcode=0x0) and im not quite sure what that means...
Here are snippets of my code:
override func viewDidAppear(animated: Bool)
{
self.loadData()
}
func loadData()
{
timelineData.removeAllObjects()
var findTimelineData : PFQuery = PFUser.query()
findTimelineData.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!)-> Void in
if error == nil
{
println("No error")
if let object = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
let array : NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
and these would be the cells that i'm trying to load, but the code never gets this far...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
println("loading cell")
let postCell : LocationTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as LocationTableViewCell
let post : PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
postCell.imageView.image = post.objectForKey("currentLocation") as? UIImage
postCell.userInfo.text = post.objectForKey("FirstLastName") as? String
// Configure the cell...
return postCell
}
for the full code: http://pastebin.com/MN7qcFhq
this worked for me:
in your code just only replace
self.timelineData = array as NSMutableArray
to
self.timelineData = NSMutableArray(array: array)
Aren't you missing an s in:
if let object = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
Should be:
if let objects = objects as? [PFObject!]
{
for object in objects
{
self.timelineData.addObject(object)
}
}
Hey Buddy I got you on this one. I had the same issues but I got help and got it figured out.
This is your Load Function
Try this
func loadData(){
timelineData.removeAll(keepCapacity: false)
var findTimelineData:PFQuery = PFQuery(className:"Sweets")
findTimelineData.findObjectsInBackgroundWithBlock
{
(objects:[AnyObject]! , error:NSError!) -> Void in
if error == nil
{
self.timelineData = objects.reverse() as [PFObject]
//let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
println(objects)
// self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
Then go to your
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
And change
let sweet:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
To This
let sweet: PFObject = self.timelineData[indexPath.row] as PFObject
Whats going on is you are mixing some Obj C with Swift , the code you are following was probably written in Beta so make sure you go back and learn the differences. If you are still having problems check all your conditions if(error != nil) should not be in your code.
Related
Working on a social iPhone app using Swift (with a Storyboard) and Parse where users can create posts and comment on posts similar to the Facebook iOS app and other social network apps.
The app has an initial, master Home Feed page (which displays user posts) and a detail Reply page (which is supposed to display the comments for a particular post that was selected but is showing the same replies for different posts). Both use the PFTableViewController class and each have their own PFTableViewCell implemented in separate swift files as the prototype cells.
When a user taps on ANY post cell in the Home Feed page, it navigates to the Reply page but shows all existing comments (as well as every new comment) for the post. I am trying to have only the comments for a specific post show when the user selects a particular post from the Home Feed page.
Any idea why this is happening? I greatly appreciate your time and help!
Home Feed page:
class HomeTableVC: PFQueryTableViewController,CLLocationManagerDelegate {
var posts: NSMutableArray! = NSMutableArray()
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showReplyViewController", sender: self)
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?, object: PFObject!) -> PFTableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("PostCell", forIndexPath: indexPath!) as! PostTableCell
if let userPost : PFObject = self.posts.objectAtIndex(indexPath!.row) as! PFObject {
cell.name.text = object["userName"] as? String
cell.message.text = object["postMessage"] as? String
let dateUpdated = object.createdAt! as NSDate
let dateFormat = NSDateFormatter()
dateFormat.dateFormat = "h:mm a"
cell.dateTime.text = NSString(format: "%#", dateFormat.stringFromDate(dateUpdated)) as String
cell.message.numberOfLines = 0
cell.message.text = userPost.objectForKey("postMessage") as? String
}
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showReplyViewController") {
let indexPath = self.tableView.indexPathForSelectedRow
let postObject = self.objects![indexPath!.row] as! PFObject
//postObject (on LHS) is the PFObject declared in ResponseViewController
if let destinationVC = segue.destinationViewController as? ReplyTableViewController {
destinationVC.postObject = postObject
}
}
}
}
Reply page:
class ReplyTableViewController: PFQueryTableViewController {
var postObject: PFObject?
var replies: NSMutableArray! = NSMutableArray()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
replies = NSMutableArray()
var replyQuery = PFQuery(className: "Reply")
replyQuery.addAscendingOrder("createdAt")
replyQuery.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
let reply: PFObject = object as! PFObject
self.replies.addObject(reply)
}
let repliesArray: NSArray = self.replies.reverseObjectEnumerator().allObjects
self.replies = NSMutableArray(array: repliesArray)
self.tableView.reloadData()
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return replies.count
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?, object: PFObject!) -> PFTableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("replyCell", forIndexPath: indexPath!) as! ReplyTableViewCell
let replyObject: PFObject = self.replies.objectAtIndex(indexPath!.row) as! PFObject
cell.replyMessageLabel.text = replyObject.objectForKey("replyMessage") as? String
var queryUser: PFQuery = PFUser.query()!
queryUser.whereKey("objectId", equalTo: (replyObject.objectForKey("replyUser")?.objectId)!)
queryUser.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
let user: PFUser = (objects! as NSArray).lastObject as! PFUser
cell.replyAuthorLabel.text = user.username
}
}
return cell
}
}
In your segue you need to tell the destination ViewController which post to show replies for.
Add this to the bottom of your segue (exactly where your comment is):
if let destinationVC = segue.destinationViewController as? ReplyTableViewController{
destinationVC.postObject = postObject
}
And in ReplyTableViewController you need a postObject variable so that the code in the segue works. At the top of your ReplyTableViewController put:
var postObject = PFObject()
It looks like the postObject should be used somewhere in your PFQuery() to filter the replies, but I am not familiar with it.
I found a solution to my own problem!
I have updated the Reply page to use UITableViewController instead of PFTableViewController and updated the storyboard correspondingly (I made the necessary changes in the code and in the Storyboard to comply with the constraints of UITableViewController, etc).
I implemented a PFQuery with the appropriate constraints to fetch all the replies for a given post (only) by writing something similar to the following:
query.whereKey("parent", equalTo: aPost)
// Finds objects *asynchronously* and call the given block with the results.
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
// if there is no error, for each object in `objects`,
// assign the given object to a PFObject
// add the object to an array that will store all of the applicable replies for the post
// ...
}
I am trying to reload my table view using
self.tableView.reloadData()
It works properly if I'm loading static datasource using array. Everything work properly.
But when I try to use my query function with parse, it loads a new cell but the contents of the tableview cell doesn't change. If I re-open the app, the cells will update properly.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "EmpPostTVCellIdentifier"
let cell: EmpPostTVCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? EmpPostTVCell
//If datasource
if dataSource.isEmpty{
fetchDataFromParse()
print("no posts")
}
let itemArr:PFObject = self.dataSource[indexPath.row]
cell?.companyPostLabel.text = (PFUser.currentUser()?.objectForKey("companyName")!.capitalizedString)! as String
cell?.occupationPostLabel.text = itemArr["occupation"]!.capitalizedString as String
cell?.countryPostLabel.text = itemArr["country"]!.capitalizedString as String
let companyImage: PFFile?
companyImage = PFUser.currentUser()?.objectForKey("profileImageEmployer") as? PFFile
companyImage?.getDataInBackgroundWithBlock({ (data, error) -> Void in
if error == nil{
cell?.companyLogoImage.image = UIImage(data: data!)
}
})
let dateArr = createdByDate[indexPath.row]
let strDate = Settings.dateFormatter(dateArr)
cell?.closingDateLabel .text = strDate
return cell!
}
I am using pull to refresh my tableviews contents using this code
func refresh(sender:AnyObject)
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.fetchDataFromParse()
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
})
}
with or without the dispatch_asynch function the results remains the same. It just add new tableviewcell but the contents in it does not change. Any ideas guys?
edit 1 :
func fetchDataFromParse() {
// MARK: - JOB POST QUERY
if PFUser.currentUser()?.objectId == nil{
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
})
} else {
let query = PFQuery(className: "JobPost")
//creating a pointer
let userPointer = PFUser.objectWithoutDataWithObjectId(PFUser.currentUser()?.objectId)
query.whereKey("postedBy", equalTo: userPointer)
query.orderByDescending("createdAt")
let objects = query.findObjects()
for object in (objects as? [PFObject])!{
//print(object.objectId)
self.dataSource.append(object)
self.createdByDate.append((object.objectForKey("closingDate") as? NSDate)!)
print(self.dataSource)
print(self.createdByDate)
}
}//end of PFUser objectID == nil else clause
}
Let's see the content of the fetchDataFromParse() function where I presume you're filling the self.dataSource array
Try to call self.tableview.reloadData() when fetchDataFromParse() is finished.
Check whether your dataSource array is empty at the end of your fetchDataFromParse method
PFUser.currentUser()?.saveInBackgroundWithBlock is an asynchronus method. So your tableView cell is having no data.
Trying to display an array pulled from parse.com in a textview inside of a uitableviewcell. Everything else is showing but I can't seem to get a array to display in a textview. This is the code I have. I'm getting fatal error: Array index out of range for myCell2.feedbacktextview.text = feedback![indexPath.row]
var feedback: [String]?
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "Post")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
if let objects = objects {
if object.objectForKey("Comments") != nil {
self.feedback = object.objectForKey("Comments") as! [String]
}
self.tableView.reloadData()
}}}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell2 = tableView.dequeueReusableCellWithIdentifier("feedcell1", forIndexPath: indexPath) as! YourAdviseControllerCell
myCell2.feedbacktextview.text = feedback![indexPath.row]
return myCell2
}
edit:
self.imageFiles.append(object["imageFile1"] as! PFFile)
self.imageFiles2.append(object["imageFile2"] as! PFFile)
self.usernames.append(object["message"] as! String)
self.usernames2.append(object["declaration"] as! String)
self.usernames3.append(object["whichbutton"] as! String)
Basically what you did is intentionally correct but a fair share of small mistakes are left to correct. The takeaway would be never use forced unwrapping when possible.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell2 = tableView.dequeueReusableCellWithIdentifier("feedcell1", forIndexPath: indexPath) as! YourAdviseControllerCell
//This below line fetches the value from feedback if it has else gives ""
myCell2.feedbacktextview.text = feedback?[indexPath.row] ?? ""
return myCell2
}
That would solve the problem for now but i see if this code gets called when then you might be returning some valid values from the numberOfRowsInCells method without respect to the feedback value. Ideally i would do something like this:
var feedback:[String]?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return feedback?.count ?? 0
}
Even then there is a slight problem i guess. Don't call tableView.reloadData() from a block which is executing in a separate thread or queue. Do all the work in main queue.
if object.objectForKey("Comments") != nil {
self.feedback = object.objectForKey("Comments") as! [String]
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
Hope it helps! Cheers!
I have a Parse query that returns a set of users (PFUsers). I place them in an array so that they can be used to populate a tableView. However, when I load the tableView I get the following error message: Could not cast value of type 'PFObject' to 'NSArray'. Here's the relevant code (I cut out some stuff to make it easier to read). It's heavily condensed but I can create a full gist of needed.
The error is caught on the line: self.realMatches = result as! [PFObject]
import UIKit
class MatchesViewController: BaseViewController {
var realMatches: [PFObject] = []
func loadMatches() {
if let user = self.user {
query{
(results: [AnyObject]?, error: NSError?) -> Void in
if error != nil {
println(error)
} else {
if results != nil {
self.matchesResults = results!
for result in results!{
if result.objectId != self.currentUser!.objectId {
self.realMatches = result as! [PFObject]
}
}
for result in results! {
self.user1 = result["user1"] as! PFUser
self.user2 = result["user2"] as! PFUser
}
self.tableView.reloadData()
}
}
}
} else {
println("current user doesnt exist")
}
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("MatchCell", forIndexPath: indexPath) as! UITableViewCell
let object = matchesResults[indexPath.row]
return cell
How can I safely store the PFUsers in an array to be used for the tableView?
Thanks!!
result is a single PFObject that you have extracted from the array of results array using a for loop.
You should simply say
self.realMatches = result as! PFObject
but self.realMatches is an array, so that assignment won't work either. You can append the result to the array using
self.realMatches.append(result as! PFObject)
I am building a Vote/Instagram-type app, where a user selects a non-user-made photo which is pushed into a Timeline.
I have built out the selection screen and button, and the Timeline. For some reason, I can only guess that it has something to do with the chronology of saving the PFObject (the chose photo), the Timeline is displaying the photo chosen BEFORE the currently chosen photo.
The following block is the 'Select' button on the 'SelectScreenVC':
#IBAction func selectNext(sender: UIButton) {
let imageData = UIImagePNGRepresentation(lgImgURL.image)
let imageFile = PFFile(name: "\(cName.text!)", data: imageData)
var user = PFUser.currentUser()
user["nextChosen"] = "\(cName.text!)"
user["imageFile"] = imageFile
var userNextPhoto = PFObject(className: "UserNextPhoto")
userNextPhoto["username"] = PFUser.currentUser().username
userNextPhoto["cName"] = "\(cName.text!)" as String
userNextPhoto["imageFile"] = imageFile
userNextPhoto.saveInBackgroundWithBlock { (success: Bool!, error: NSError!) -> Void in
if error == nil {
println("User \(PFUser.currentUser().username) chose: \(self.cName.text!)")
} else {
println(error)
}
}
self.navigationController?.popToViewController(timeLineVC, animated: true)
}
This the TimeLineVC. I have the function which creates the timeline array and I skipped some of the TableView methods, except for the cellForRowAtIndexPath.
class TimeLineViewController: UITableViewController {
var timelineData:NSMutableArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className: "UserNextPhoto")
findTimelineData.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
self.timelineData.addObject(object)
}
} else {
NSLog("Error: %# %#", error, error.userInfo!)
}
let array: NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("timelineCell", forIndexPath: indexPath) as TimelineTableViewCell
let userNextPhoto:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
let nextPhoto:PFObject = self.timelineData.objectAtIndex(indexPath.row) as PFObject
cell.usernameLabel.text = PFUser.currentUser().username
cell.cName.text = (userNextPhoto["cName"] as String)
return cell
}
I appreciate any help or insight!
That is what is going on: the new photo has not finished uploading when you pop the view controller. You can either wait for the photo to finish uploading (which is not a great UX), or you can pass back a reference to the newly-saved object back up to the popped view controller which can then display the new photo right away without waiting for the upload to finish.