I'm trying out the "Sample Blog App" on Parse Server for iOS and cannot figure out what is the smartes way to fetch all child objects of another class (together with the parent objects).
The "Sample Blog App" (which creates automatically when you create a new account) contains the classes Comment
and Post
. The Comment class contains a relation to the Post class as shown below (from the dashboard), but there is no relation in the opposite direction.
Now, I want to fetch all posts and all the comments related to each post. The code below does that, but I'm assuming there must be a smarter way...? If you know how, please share. Thanks in advance!
- (void)fetchPosts {
NSString *commentsKey = @"comments";
NSString *postKey = @"post";
PFQuery *query = [PFQuery queryWithClassName:@"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error == nil) {
NSMutableArray *array = [[NSMutableArray alloc]init];
for (PFObject *comment in objects) {
PFObject *post = [comment objectForKey:postKey];
NSDictionary *existingPostDict = [[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K = %@", @"post.objectId", post.objectId]] firstObject];
if (existingPostDict) {
// update comments
NSArray *comments = [[existingPostDict objectForKey:commentsKey] arrayByAddingObject:comment];
// create new dictionary and replace the old one
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:[existingPostDict objectForKey:postKey], postKey, comments, commentsKey, nil];
[array replaceObjectAtIndex:[array indexOfObject:existingPostDict] withObject:newPostDict];
}
else {
// first post, create a new dict
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:post, postKey, @[comment], commentsKey, nil];
[array addObject:newPostDict];
}
}
self.posts = array; // assuming: @property (nonatomic, strong) NSArray *posts;
}
else {
NSLog(@"Error fetching posts: %@", error.localizedDescription);
}
}];
}
This is how I did it, using findObjectsInBackground
together with continueWithSuccessBlock:
methods (for better error handling one can choose continueWithBlock:
instead):
- (void)fetchPosts {
/**
create "post" and "comment" queries and use a BFTask-method from
Bolts.framework to chain downloading tasks together (bundled with Parse SDK)
*/
NSMutableArray *posts = [NSMutableArray new];
PFQuery *postQuery = [PFQuery queryWithClassName:@"Post"];
[[[postQuery findObjectsInBackground] continueWithSuccessBlock:^id(BFTask * task) {
[posts addObjectsFromArray:task.result];
PFQuery *commentsQuery = [PFQuery queryWithClassName:@"Comment"];
return [commentsQuery findObjectsInBackground];
}] continueWithSuccessBlock:^id(BFTask * task) {
/**
loop through posts and filter out comments with the same objectId in post,
then create a dictionary with post and related comments. done! :)
*/
NSMutableArray *postsAndComments = [NSMutableArray new];
for (PFObject *post in posts) {
NSArray *comments = [task.result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K == %@", @"post.objectId", post.objectId]];
[postsAndComments addObject:@{@"post":post, @"comments":comments}];
}
/**
note: BFTask-blocks not running in main thread!
*/
dispatch_async(dispatch_get_main_queue(), ^{
self.posts = postsAndComments; // assuming: @property (nonatomic, strong) NSArray *posts;
});
return nil;
}];
}