swiftparse-platformpfobject

The method does not enter for loop Parse Swift


I use parse to query current user's friend list and the friend request user and when user press each cell of the friend request, The app will add that friend back and delete the selected friend request so I query friend list and friend request and use "addedArray" as friend requests and "duplicate" as array of current user's friend list and use for loop to find the duplicate of friend list and friend request and delete that friend from addedArray so the current user will se the latest friend requests

Here's my code in swift

   func queryAdded(){
    let query = PFQuery(className: "Request")
    let user = PFUser.currentUser()?.relationForKey("Friends")
    let query2 = user?.query()

    query.whereKey("To", equalTo: PFUser.currentUser()!)
    query.findObjectsInBackgroundWithBlock {
        (objects, error) -> Void in
        if error == nil{
        for object in objects! {
            print("query")
            let username = object.valueForKey("FromUsername") as! String
            self.userCellAdded = username
                self.addedArray.append(username)
                print(username)
               print(self.addedArray.count)

            }
            print("READY")
            print(self.addedArray.count)
            self.tableView.reloadData()
        }
        else{
           /* dispatch_async(dispatch_get_main_queue()){
                //reload the table view
                query.cachePolicy = PFCachePolicy.NetworkElseCache
            }*/

            print("errorrrr")
        }
    }

    query2!.findObjectsInBackgroundWithBlock{(objects,error) -> Void in
        if error == nil {
            for object in (objects)!{
                if let username = object["username"] as? String {
                    self.duplicate.append(username)
                    print("duplicate")
                    print(username)
                    print("size")
                    print(self.duplicate.count)
                }
            }
        }
    }

    for self.iIndex = 0 ; self.iIndex < self.addedArray.count ; ++self.iIndex {
        for self.jIndex = 0 ; self.jIndex < self.duplicate.count ; ++self.jIndex {
            print("in for loop")
            if self.addedArray[self.iIndex] == self.duplicate[self.jIndex] {
                self.addedArray.removeAtIndex(self.iIndex)
                self.tableView.reloadData()
                print("find")
            }
        }
    }
}

The problem is The method queryAdded() does not run for loop for me and I don't understand why

The duplicate array and the addedArray have value and size but still it didn't go inside the for loop


Solution

  • Your problem is that your for loop is depending on the results of two asynchronous operations. What happens is that your app starts these two background queries and then immediately starts the for loop. Since there is no data yet from the queries, the for loop has no data to work on.

    You can either solve this by creating a "pyramid hell" by nesting your operations (bad), or you can use a framework to achieve the same as Promises would provide for JavaScript (good).

    Since you're using Parse, you have such a framework already; namely the Bolts Framework. You could then perform these operations sequentially using tasks (BFTask).

    Example from the Bolts readme:

    var query = PFQuery(className:"Student")
    query.orderByDescending("gpa")
    findAsync(query).continueWithSuccessBlock {
      (task: BFTask!) -> BFTask in
      let students = task.result() as NSArray
      var valedictorian = students.objectAtIndex(0) as PFObject
      valedictorian["valedictorian"] = true
      return self.saveAsync(valedictorian)
    }.continueWithSuccessBlock {
      (task: BFTask!) -> BFTask in
      var valedictorian = task.result() as PFObject
      return self.findAsync(query)
    }.continueWithSuccessBlock {
      (task: BFTask!) -> BFTask in
      let students = task.result() as NSArray
      var salutatorian = students.objectAtIndex(1) as PFObject
      salutatorian["salutatorian"] = true
      return self.saveAsync(salutatorian)
    }.continueWithSuccessBlock {
      (task: BFTask!) -> AnyObject! in
      // Everything is done!
      return nil
    }
    

    You could then first prepare both your queries and then start the chain of tasks:

    query1.findObjectsInBackground().continueWithSuccessBlock {
        (task: BFTask!) -> BFTask in
        var objects = task.result() as NSArray
        for object in objects {
            //collect your usernames
        }
        return query2.findObjectsInBackground()
    }.continueWithSuccessBlock {
        (task: BFTask!) -> AnyObject! in
        var objects = task.result() as NSArray
        for object in objects {
            // collect your usernames from relation
        }
        // Call a function containing the for loop that is currently not running
        return nil
    }