0

I have 4 functions containing Parse query.findObjectsInBackgroundWithBlock. These are being called to grab data and then populate the table view. Using dispatch groups.

Here is two examples of my parse querys

func getEventImages() {
    print("getEventImages enter")
    dispatch_group_enter(self.group)

    let query = PFQuery(className: "events")
    query.orderByAscending("eventDate")
    query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error: NSError!) -> Void in
        // Initialize your array to contain all nil objects as
        // placeholders for your images
        if error == nil {
            self.eventMainImageArray = [UIImage?](count: objects.count, repeatedValue: nil)
            for i in 0...objects.count - 1 {

                let object: AnyObject = objects[i]
                let mainImage = object["mainImage"] as! PFFile
                //dispatch_group_enter(self.group)
                mainImage.getDataInBackgroundWithBlock({
                    (imageData: NSData!, error: NSError!) -> Void in
                    if (error == nil) {
                            let mainImage = UIImage(data:imageData)
                            self.eventMainImageArray[i] = mainImage
                            print("getEventImages appended")   
                    }
                    else {
                        print("error!!")
                    }
                })
            }
        }
        print("getEventImages leave")
        dispatch_group_leave(self.group)
    }
} 

func getEventInfo() {
    print("eventInfo enter")
    dispatch_group_enter(group)

let query = PFQuery(className: "events")
    query.orderByAscending("eventDate")
    query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error:   NSError!) -> Void in
        self.eventNameArray = [String?](count: objects.count, repeatedValue: nil)
        self.eventInfoArray = [String?](count: objects.count, repeatedValue: nil)
        self.eventDateArray = [NSDate?](count: objects.count, repeatedValue: nil)
        self.eventTicketsArray = [String?](count: objects.count, repeatedValue: nil)

        if error == nil {
            for i in 0...objects.count - 1 {
                let object: AnyObject = objects[i]
                let eventName = object["eventName"] as! String
                let eventInfo = object["eventInfo"] as! String
                let eventDate = object["eventDate"] as! NSDate
                let eventTicket = object["Tickets"] as! String

                self.eventNameArray[i] = eventName
                self.eventInfoArray[i] = eventInfo
                self.eventDateArray[i] = eventDate
                self.eventTicketsArray[i] = eventTicket
                print("event info appended")
            }
        }
        print("event info leave")
        dispatch_group_leave(self.group)
    }
}

And my dispatch_group_nofity

 dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
        print("Finished reloadDataFromServer()")
        self.tableView.reloadData()
        self.refreshControl?.finishingLoading()
    }
}

The problem is that its hit and miss if the data gets retrieved quick enough before dispatch_group_leave(self.group) is called leading to reloading the tableview data too soon. I need to get this so the dispatch_group_leave gets called when the appending is completed.

user4671001
  • 309
  • 2
  • 4
  • 12
  • Why are you unpacking the PFObjects and storing the attributes in multiple arrays? Why not simply store the PFObjects themselves in an array and then access the objects attributes when you create the table row? Storing the array of events would also avoid having to issue the same query for the events twice and then you wouldn't need the dispatch group. It would also use less time, data and fewer Parse transactions – Paulw11 Dec 14 '15 at 19:48
  • When looking at examples this is how most people went about doing it. Also I'm guessing that in the future when I need to update and change certain objects it will be easier doing it for the single object rather than having to grab and update the whole pfobject again. What do you think? – user4671001 Dec 14 '15 at 21:21
  • Could you give me a example of what you mean? I understand what you are saying but I'm not too sure on how I would use this when creating the row and also editing and updating certain objects – user4671001 Dec 14 '15 at 21:24
  • No, just hold the PFObject, then if you want to update it, simply update the attribute and call one of the `save` methods on it. No need to fetch it again. – Paulw11 Dec 14 '15 at 21:24

1 Answers1

1

There is no need for two methods to retrieve the data, no need to unpack the data into multiple arrays and no need to use dispatch groups.

All you need is a simple method to retrieve your event data

var events:[PFObject]=[PFObject]()

func getEventInfo() {
    let query = PFQuery(className: "events")
    query.orderByAscending("eventDate")
    query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error:   NSError!) -> Void in
        if error==nil {
            self.events=objects as! [PFObject]
            self.tableView.reloadData()           
        } else {
            print("Something went wrong! - \(error)"
        }
        self.refreshControl?.finishingLoading()
    }
}

Then, you haven't shown your cellForRowAtIndexPath but you would have something like

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! MyTableViewCell
    let event=self.events[indexPath.row]

    cell.eventName.text=event["eventName"] as? String
    cell.eventInfo.text=event["eventInfo"] as? String
    if let mainImageFile=event["mainImage"] as? PFFile {
        mainImageFile.getDataInBackgroundWithBlock({
            (imageData: NSData!, error: NSError!) -> Void in
            if (error == nil) {
                let mainImage = UIImage(data:imageData)
                cell.mainImage= mainImage
            }
            else {
                print("error!!")
            }
    }
    return cell;
}

You can use a PFImageView or a framework like SDWebImage to handle image caching and putting a placeholder image in place while the image is loaded.

If you want to update an event is as easy as

 var event=self.events[someindex];
 event["eventName"]=newValue
 event.saveInBackground()
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • you may want to call the UI stuff on the main thread: it's never been clear to me whether or not Parse completion handlers are on it. – pickwick Dec 14 '15 at 21:52
  • Parse completion handlers are dispatched on the main queue and now that the framework is open source you can verify this for yourself if you are so inclined. – Paulw11 Dec 14 '15 at 21:59
  • Just gave this a go, really cleaned up my code! Thanks – user4671001 Dec 16 '15 at 21:27
  • After mainImageFile.getDataInBackground for the cell. How would I then capture all of these to be able to move them into the detail view in the func didSelectRowAtIndexPath? I was doing this with the array I created before hand but not too sure how to go about it now. Also I have a second image I must getDataInBackground ready for the detail view. Calling this in didSelectRow would be incorrect right? Thanks Paul!! – user4671001 Dec 19 '15 at 19:56
  • You can use a framework like SDWebImage which will handle the caching for you and then just fetch the image again in your detail view. The second time it will come from cache and be provided quickly. I would fetch the second image in the `viewDidLoad` or perhaps a property observer in your detail view controller. – Paulw11 Dec 19 '15 at 20:14
  • Could you give me a example of how to save the image inside the cellForRow to use in detail view. I tried appending it to an array to use in the didSelectRow, but there is an issue since cellForRow is used a lot you end of with duplicate of images being saved when scrolling – user4671001 Dec 20 '15 at 14:59