0

I have a table view which displays Facebook friend's data. Data is stored on Parse and I'm pulling it down and storing the data in "postArray" using the following query. I have used NSLog to check postArray object's and I am getting the desired results.

What happens is that I get sum of my data stored in postArray to be set to the appropriate labels and then some are just left empty. As I said, I did NSLog postArray and it gives me what I wanted. But when I go to set the data, it doesn't set all the data but just some.

Problem #1--> I need all of the data to be able to be accessible from the array and it's not letting me.

Problem #2 --> It is hit or miss whether it loads anything at runtime or not. If I log in and log out its a coin flip whether it will even load anything. I NSLog postArray and it seems it the array is empty sometimes. I know what you are thinking - "You got to give me something better than "sometimes", but I really can't. It's hit or miss.

Please help me out and tell me what's going on with it!

(and the dispatch_async makes no difference)

What I've been doing =

    [[FBRequest requestForMyFriends] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

        if (!error) {

            friendDict = result;

            friendArray = friendDict[@"data"];

            for (NSDictionary *friendObject in friendArray) {

                friendIDs = [NSMutableArray arrayWithObjects:[friendObject objectForKey:@"id"], nil];

                PFQuery *postQuery = [PFQuery queryWithClassName:@"Post"];
                [postQuery whereKey:@"postedByID" containedIn:friendIDs];
                [postQuery orderByAscending:@"createdAt"];
                [postQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {

                    dispatch_async(dispatch_get_main_queue(), ^{

                        postArray = objects;
                        [self.activityTableView reloadData];

                    });

                }];

            }
        }

        else {

            //there are errors

        }

    }];
1
  • it looks like you're making a query for each individual friend... maybe you want to close your for loop before the query.. Commented Jun 3, 2014 at 21:18

1 Answer 1

3

I can see a couple ways this code can go wrong. First, it looks like postArray gets replaced each time the inner find completes. Second, the structure of the inner find is problematic, launching N asynch calls in rapid succession, then updating a table as each competes.

Consider this alternative: we build a few methods that can run asynch with completion blocks. The simplest one does the PFQuery for posts based on a userId:

- (void)getPostsForId:(NSString *)userId withCompletion:(void (^)(NSArray *, NSError *))completion {

    PFQuery *postQuery = [PFQuery queryWithClassName:@"Post"];
    [postQuery whereKey:@"postedByID" containedIn:@[userId]];
    [postQuery orderByAscending:@"createdAt"];
    [postQuery findObjectsInBackgroundWithBlock:completion];
}

The second one is the interesting one. This solves the problem of making a sequence of asynch calls, letting one finish before launching the next, and only invoking it's completion block when all are complete.

It does this by using the array of user ids as a to-do list and calling itself recursively, collecting the results in a mutable array. When the to-do list is complete (empty), it calls it's own completion block:

- (void)getPostsForIds:(NSArray *)ids results:(NSMutableArray *)results completion:(void (^)(BOOL))completion {

    if (ids.count == 0) return completion(YES);

    NSString *userId = ids[0];
    NSArray *remainingIds = [ids subarrayWithRange:NSMakeRange(1, ids.count-1)];

    [self getPostsForId:userId withCompletion:^(NSArray *posts, NSError *error) {
        if (!error) {
            [results addObjectsFromArray:posts];
            [self getPostsForIds:remainingIds results:results completion:completion];
        } else {
            completion(NO);
        }
    }];
}

Now the method you started with can be simplified. Get the friends list from FB, build an array of their ids, and call your magic function above:

- (void)getFriendsPosts {

    [[FBRequest requestForMyFriends] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
        if (!error) {
            friendDict = result;
            friendArray = friendDict[@"data"];

            NSMutableArray *ids = [NSMutableArray array];

            for (NSDictionary *friendObject in friendArray) {
                [ids addObject:[friendObject objectForKey:@"id"]];
            }
            NSMutableArray *allPosts = [NSMutableArray array];
            [self getPostsForIds:ids results:allPosts completion:^(BOOL success) {

                // EDIT - be sure to use the resulting allPosts as your datasource
                self.theModelForMyTable = allPosts;

                [self.activityTableView reloadData];
                // if !success do an alert or something
            }];
        }
    }];
}

The datasource methods should refer only to the table model, so for example numberOfRowsInSection should answer self.theModelForMyTable.count and cellForRowAtIndexPath should always get the data to configure the cell from self.theModelForMyTable[indexPath.row]

Sign up to request clarification or add additional context in comments.

4 Comments

I bow to thee. You're alternative is superior. However I am having a bit of problem. Your code gave me what I wanted, but the cells "flicker" a couple times in the table view and then crash. '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'. Know what's going on with that? I think it's cause it keeps trying to update.
Can you prove with a breakpoint or NSLog that the reload is called just once? That was my intent for this code. The other important point is to confirm that all of the datasource methods refer to one and only one array, the allPosts array in my code. I can see now that my suggestion was unclear in this regard. Will edit.
That was it. I was using a property NSMutableArray in place of the "allPosts". Once I assigned my property to allPosts it works perfectly. You are awesome dude. For reals.
good to hear. hey just a quick note - if the number of post fetches gets too big, you might need a cloud function to aggregate first then return them all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.