1

I have a Parse "Event" object already passed in to the viewController. No need to query for it.

This "Event" object contains an array of pointers to Parse "Comment" objects.

I am interested in two properties of the "Comment" objects. Both are strings called "commentAuthor" and "commentText".

I have a UITableView that has prototype cells, which needs to display the "commentAuthor" and "commentText" strings.

It is currently functioning, but I'm not satisfied with the way I'm doing it, which is this:

var commentsArrayOfPointers: [AnyObject]?
var commentsArrayOfObjects = [PFObject]()

func dealWithCommentPointers() {

    //get array of pointers from "Event" object
    commentsArrayOfPointers = theEvent!["commentsArray"] as? [AnyObject]

    commentsArrayOfObjects.removeAll()

    //loop through each pointer, and "Fetch" its associated "Comment" object,
    //then append that "Comment" object to my array of "Comment" objects
    for comment in commentsArrayOfPointers! {

        comment.fetchIfNeeded()

        commentsArrayOfObjects.append(comment as! PFObject)

    }

    //print(commentsArrayOfObjects)

    self.tableView.reloadData()

}

Then, I build my table cells like so, pulling the data out of my previously filled "commentsArrayOfObjects":

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("commentCell",
      forIndexPath: indexPath) as! commentCellClass

    cell.commentCellNameLabel.text = commentsArrayOfObjects[indexPath.row]["commentAuthor"] as! String
    cell.commentCellCommentLabel.text = commentsArrayOfObjects[indexPath.row]["commentText"] as! String

    return cell
}

It works, but I'm not thrilled about using the Parse command "fetchIfNeeded()" on each comment object, because it is a synchronous function, meaning it is causing my app to pause before showing the table. I would prefer to use an asynchronous function, which would allow the rest of the view controller to load, and then the comments would populate the table as they become available.

I tried doing a fetchInBackgroundIfNeeded() which is an asynchronous command, but of course it doesn't finish before the prototype cells are built, resulting in a crash (because the array it is looking for data is nil).

I also tried setting a PFQuery on the "Event" I need (event though I already have the event, no need to re-query for it) and adding an "includeKey" on the "commentsArray" property of the "Event" class. Then at the end of the Query, I did a self.tableView.reloadData(), but this didn't work either. There seems to be a larger problem here, as the query never even seems to execute, and the table reloads, even though I have commented out the code to do so. Here is that attempt (which I think is closer to the correct solution to my problem):

func dealWithCommentPointers() {

    print("inside the function")

    var query = PFQuery(className: "Event")
    let objectIdToLookup = self.theEvent?.objectId
    query.whereKey("objectId", equalTo: objectIdToLookup!)
    query.includeKey("commentsArray")

    query.findObjectsInBackgroundWithBlock { (results, error) -> Void in

        if error != nil {
            print("Error found")
            print(error)
        }


        else {

            let eventArray = results as! [PFObject]

            let theEventToDisplayCommentsFor = eventArray[0] //this should always be [0]

            print("yo dude")

            self.commentsArrayOfObjects = theEventToDisplayCommentsFor["commentArray"] as! [PFObject]

            print("hey here i am!")

            print(self.commentsArrayOfObjects.count)

            self.tableView.reloadData() //even tried commenting this out, it is still called?!

        }

    }

}

So, any ideas?

shadowmoses
  • 343
  • 3
  • 17

2 Answers2

2

Why not include all of the Comment objects in the array beforehand? My guess would be that you have performed a query on the previous view controller and that is how you have the Event object to pass in. When you perform that query, use includeKeyon the array so all of the Comment objects are returned with the query at the same time.

If that can't be done, I would recommend adding a couple function to your table view for asynchronously grabbing the data from Parse and then reloading the table. Here's an example for doing that which I've posted before. In that example there is a UISearchController but the ideas are the same.

Russell
  • 3,099
  • 2
  • 14
  • 18
  • Thanks Russell. I thought about passing in the `Comment` objects beforehand. That seems like the easiest solution, but one of the ways I get to the current viewController is a "create Event" view controller, in which I then set the "event" object to pass in to the current viewController in the prepareForSegue method of the prior (or "create Event") viewController. There would always be zero "Comment" objects when coming from this VC, but it seems like I would have to do a query in order to include the `commentsArray` with it. I guess it wouldn't kill me to do another query, but seems wasteful? – shadowmoses Sep 24 '15 at 19:19
  • So, then I thought about what you said regarding adding a couple of functions to my table view. I would like to do that, but I'm not sure how. I looked closely at your linked question, but I don't really understand how it applies to what I'm doing. I saw a few `if` statements in your tableView code, but no additional functions. I'm probably missing something... – shadowmoses Sep 24 '15 at 19:25
  • I'm not understanding why you'd need to add another query if the event was just created. In that case, wouldn't there just be 0 comments and the table view would be empty? The example I linked was to give an alternative to using a `PFQueryTableViewController`. By using your own table view controller and implementing your own functionality for accessing Parse, you have much more flexibility. The if statements were just for the search controller and you can ignore those in your case. You would just need the array container along with the Parse method. – Russell Sep 24 '15 at 19:40
  • Thanks Russell. I'm not using a `PFQueryTableViewController`. I am indeed using my own tableViewController. I create the event in the previous view controller, and I store the comments array in that event. However, Parse stores them as "pointers". So, I'm not sure how to pass along the actual objects to the next view controller, with the event object, without doing a Parse query with `includeKey`. To your question, yes, when you create an event, there would be zero comments and the tableView would be empty. But I thought I would have to pass in this array of objects, even if it is empty... – shadowmoses Sep 24 '15 at 20:34
  • Happy to help. Since you're passing in the `Event`, I'm guessing you have a `PFObject` for it defined in your table view that gets set during `prepareForSegue` on the previous view controller. You could similarly have a `[PFObject]` array defined in your table view for the comments that get's set during `prepareForSegue`. – Russell Sep 24 '15 at 21:10
  • I thought about this some more, and your initial response about including the `Comment` objects in the array beforehand was what I needed to get me thinking in the right direction. Thanks again. – shadowmoses Sep 28 '15 at 18:07
1

You should really be using a relationship for the comments rather than an array, then you have a query that you can run asynchronously to get the comments. Also, you need to change your table view setup so it doesn't crash when you don't have any comments yet. You don't show why it crashes so I can't help with that, but you should really have an activity indicator while the query is in progress and then replace it with the real cells or a cell indicating there are no comments yet.

Technically using the asynchronous fetch should work just as well for small numbers of comments, but as you add more comments you're making more network requests and pretty quickly you'll flood the network and they'll all fail.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Thanks Wain. If I can't get my current approach to work, I will abandon and re-think using a relationship. I expect there to be no more than 20-30 comments at **most** for each event. Probably more like 5-10, if that. Do you have any suggestions of where I would put that asynchronous fetch code? I tried doing it in the function that returned my custom `cell`, but I can't return anything in that Parse function (`findObjectsInBackgroundWithBlock`), or perhaps I can, and I just don't know? – shadowmoses Sep 24 '15 at 19:28
  • I have code to prevent `func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell ` from running unless it has data to look at, but thank you for noting it. – shadowmoses Sep 24 '15 at 19:30
  • You should load the comments when the view is loaded rather than while trying to configure a cell, then reload the table – Wain Sep 24 '15 at 20:14
  • Thanks Wain. I am already doing that. I have a function (`dealWithCommentPointers`) that is called in `viewWillAppear`. And at the end of that function, am calling `reloadData` on the tableView. – shadowmoses Sep 24 '15 at 20:41
  • so why are you getting a crash? if the table waits for data then it should all be fine – Wain Sep 24 '15 at 21:27