I'm creating a UITableView with posts. If one of the tableview's rows is tapped, a detail page is shown where comments can be made to the post. I'm using the Parse.com framework to realize this.
I save the comments like this in the detailview:
PFObject *comment = [PFObject objectWithClassName:#"Comment"];
comment[#"content"] = _textViewComment.text;
comment[#"post"] = _object;
comment[#"user"] = currentUser;
[comment saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(!error){
UIAlertView* alertSave = [[UIAlertView alloc]initWithTitle:#"Saved" message:#"Your comment is saved" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertSave show];
[self setToDefault:_textViewComment];
}else{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Warning" message:#"Comment not saved" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}];
where _object is the current post.
What I want to do now is sort the first tableview by number of comments. But I'm only making a relation from the comment to the post and not the other way around.
Is there a way to query the posts and sort them by number of comments?
To do this, you should relate comments to posts via an array on Post.
Add a "comments" col of type array on Post. When you fetch the post, use includeKey: on the #"comments" key. Then change your update code to add a comment like this...
// in your code, I think "_object" is a post. calling it "post" here instead...
NSMutableArray *comments = [[post objectForKey:#"comments"] mutableCopy];
[comments addObject:comment];
[post setObject:comments forKey:#comments"];
// then save the post object. parse will save the related comment.
[post saveInBackgroundWithBlock ...
Double check everything in the data browser. Then, after the next fetch of posts, you can sort by comment count...
[query findObjectsInBackgroundWithBlock:^(NSArray *array, NSError *error) {
if (!error) {
NSComparator comparator = ^(Post *postA, Post *postB) {
NSNumber *countA = #([postA objectForKey:#"comments"].count);
NSNumber *countB = #([postB objectForKey:#"comments"].count);
return [countA compare:countB];
};
NSArray *sortedArray = [array sortedArrayUsingComparator:comparator];
// use sortedArray as your tableview datasource
}
}];
Related
Don't know what to do with it. but i loaded data and load table when data is loaded,
[manager POST:path parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
arryGlobal = [NSMutableArray new];
[arryGlobal addObject:responseObject];
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"]){
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
else if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"0"]){
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//here is place for code executed in error case
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error while sending"
message:#"Sorry, try again."
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
NSLog(#"Error: %#", [error localizedDescription]);
}];
It works perfectly two times, but when i go for 3rd time call this webservice it load data, get data, table successfully reloaded, but it not change content in table, it appear as it is in 2nd time.
SO what happens there ?? When I scroll table, then in cellForRowAtIndexPath array that i use to pass in table is contain data of 2nd time called Webservice.
EDIT:
i added this table view VievController in other view like :
MainCategory *objMain = [[MainCategory alloc] initWithNibName:#"MainCategory" bundle:nil];
[objMain LoadData:tag];
objMain.view.frame = CGRectMake(0, 0, self.bgViewCat.frame.size.width, self.bgViewCat.frame.size.height);
[self.bgViewCat insertSubview:objMain.view atIndex:1];
[self addChildViewController:objMain];
[objMain didMoveToParentViewController:self];
Try This
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"])
{
[arryGlobal removeAllObjects];
arryGlobal=[NSarray alloc]init];
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
the block:success() already run in main queue;you needn't use dispatch_async(dispatch_get_main_queue();
and arryGlobal is a kind of NSMutableArray;why you use the function called "thevalueforKey:" to get the value ?
I think maybe you should print the responseObject to check your data
ok so ther's error on :
[self.bgViewCat insertSubview:objMain.view atIndex:1];
Instead of this I use this and everything is fine, don't know why?:
[self.bgViewCat addSubview:objMain.view];
I'm trying to compare an NSString to an NSArray from parse with an if statement. Here's what I tried:
-(void)queryParseMethod {
//PFQuery *query = [PFQuery queryWithClassName:classNameString];
PFQuery *query = [PFQuery queryWithClassName:#"SaveClass2"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
_barcodeArray = [[NSArray alloc]initWithArray:objects];
//[_tableView reloadData];
}else{
NSLOG(#"ERROR");
}
}];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self queryParseMethod];
NSIndexPath *indexPath;
int barcodInt = indexPath.row %1000000;
PFObject *barcodeObject = [_barcodeArray objectAtIndex:barcodInt];
NSArray *secondBarcodeArray;
secondBarcodeArray = [barcodeObject objectForKey:#"RNG1"];
if ([[secondBarcodeArray objectAtIndex:barcodInt] isEqualToString:_label.text]){
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"FOUND" message:#"THE BARCODE IS CORRECT!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
}else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"NOT FOUND" message:#"THE BARCODE IS INCORRECT!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
};
}
But it always displays the "NOT FOUND" alert View and I'm sure that the classes are right and the columns are also right but it wont work. Any help will be appreciated.
As long as your _barcodeArray is assigned in background thread (where, I believe runs findObjectsInBackgroundWithBlock:, at point where You're trying to get object barcodeObject from _barcodeArray latter is empty (it can be or can be not). You have classic race condition. If you really need to run this find objects block in background, you should consider using some sync mechanism - like dispatch_semaphore. Or, as I presume, you don't need to call findObjectsInBackgroundWithBlock: and use its synchronous counterpart instead, if present.
The blocks are asynchronous so you can't know when block will run on main thread.
I read your code and I found that you are fetching object asynchronously. It means execution wont wait for that method findObjectsInBackgroundWithBlock: to complete.
So the solution is use the synchronous versions of this methods like...
- (NSArray *)findObjects;
- (NSArray *)findObjects:(NSError **)error;
the above method stops the execution further util the findObject: method not get executed.
Use this
-(void)queryParseMethod {
PFQuery *query = [PFQuery queryWithClassName:#"SaveClass2"];
NSError *error = nil;
[query findObjects:&error] {
if (!error) {
_barcodeArray = [[NSArray alloc]initWithArray:objects];
} else {
//error
}
}
I hope this will work for you...good luck...!! :)
so my application loads in a user's contacts and stores their phone numbers but I want to query these phone numbers against ones stored in Parse to determine whether any of their contacts are using the application.
So, I know how to work through every phone number as a single query by using something like the code below inside a for loop,
NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i));
//parse query for any matches to the phone number
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"phoneNumber" equalTo:phonenumberfieldfriend.text];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d users.", objects.count);
// Do something with the found objects
if (objects.count == 0) {
//uialert letting the user know that no phone number matches the query
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No User"
message:#"No user matches this phone number"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
phonenumberfieldfriend.text = #"";
}
//if there is only one number matching the query
if (objects.count ==1) {
for (PFObject *object in objects) {
NSLog(#"%#", objects);
usernamefriend =[ object objectForKey:#"username"];
numberfriend = [object objectForKey:#"phoneNumber"];
firstnamefriend = [object objectForKey:#"firstName"];
lastnamefriend = [object objectForKey:#"lastName"];
emailfriend = [object objectForKey:#"email"];
add.hidden=true;
phonenumberfieldfriend.hidden=true;
confirmuser.hidden=false;
NSLog(#"one user entered %#",usernamefriend);
}
}
//if there is more than one phonenumber matching the query as
//the user to input the friends username
//instead
if (objects.count>1) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"More than one user!"
message:#"More than one user with this number please enter a username instead!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
phonenumberfriend.text=#"Please enter a username";
add.hidden=true;
adduser.hidden=false;
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];}
but I would like to know whether it is possible to search all the contacts phone numbers with a single query?
You could do this numerous ways, provided I understand what your vision is:
One way is simply preform the query and simply do :
query getFirstObjectInBackground:
https://parse.com/docs/ios/api/Classes/PFQuery.html#//api/name/getFirstObjectInBackground
this narrows it down to only one phone number that matches your query parameters.
or getObjectInBackground:
So in other words just delete findObjects and replace with getFirstObjectInBackground and the first result that matches will be returned
Currently, I am attempting to optimize my getMutualFriends method. When I open my 'Friends' view controller, I execute the getMutualFriends method for every friend the user currently has... Which is NOT optimal...but was the easiest solution...
Heres what I did:
[CBUtility queryForFriends:[PFUser currentUser] block:^(NSArray *friends, NSError *error) {
[self.friendsActivityIndicator stopAnimating];
if (error) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil cancelButtonTitle:#"Okay"
otherButtonTitles:nil]
show];
return;
}
if ([friends count] == 0 || !friends) {
[self.friendsTable addSubview:self.friendsEmptyView];
return;
}
self.friends = [NSMutableArray arrayWithArray:friends];
[self.friendsTable reloadData];
[self.friendsEmptyView removeFromSuperview];
int i = 0;
//
// THIS IS THE PART THAT SUCKS!!!
//
for (PFObject * friendObject in self.friends) {
[CBUtility queryForMutualFriends:[friendObject objectForKey:kCBFriendToUserKey] block:^(int mutualFriends, NSError *error) {
if (error) {
[[[UIAlertView alloc] initWithTitle:#"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil] show];
return;
}
CBFriendsCell *cell = (CBFriendsCell *)[self.friendsTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[cell setMutualFriends:mutualFriends];
}];
i++;
}
}];
And heres what +(void)queryForMutualFriends looks like:
+ (void)queryForMutualFriends:(PFUser *)user block:(void (^)(int number, NSError *error))completionBlock
{
PFQuery *usersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
[usersFriends whereKey:kCBFriendFromUserKey equalTo:user];
[usersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];
PFQuery *currentUsersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
[currentUsersFriends whereKey:kCBFriendFromUserKey equalTo:[PFUser currentUser]];
[currentUsersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];
[currentUsersFriends whereKey:kCBFriendToUserKey matchesKey:kCBFriendToUserKey inQuery:usersFriends];
[currentUsersFriends countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
if (!error) {
completionBlock(number, error);
return;
}
completionBlock(-1, error);
}];
}
So instead of running the loop and passing individual PFUser objects into the getMutualFriends method, I'd like to pass an array of friends into the method and return an array of dictionary objects whose keys are 'user' and 'count' with their respective values (e.g. #[#{#"user":somePFUser, #"count":5}, #{#"user":anotherPFUser, #"count":20}];
I mean, this works fine at the moment but takes up way too much API requests...
Anyone got ideas with how to setup the PFQuery?
EDIT:
Here was a link to a SQL query that solves the same problem
No apparently, you cannot... But you can limit the amount of times you query to the server by instead of querying for mutual friends when you retrieve the mutual friends like I did, you instead cache the results into memory...
I solved this issue by making the query in cellForIndexPath when setting a cells attributes. When the cell is loaded, I check cache first to see if the query has already been made, if it has then I get the cache data... If it hasn't then I make a query to the servers... Only issue I see is that it doesn't update... I figure I can clear cache every minute or so, so the user gets updated automatically instead of pressing a reload button.
so I am making an app that searches for friends locations saved on parse.com. Before I begin, here is the method in question.
- (void)saveFriendLocationInfo {
PFQuery *query = [PFQuery queryWithClassName:#"Locations"];
[query orderByDescending:#"createdAt"];
[query whereKey:#"userString" equalTo:currentFriend];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *parseCurrentFriend, NSError *error) {
if (!parseCurrentFriend) {
NSLog(#"The getFirstObject request failed.");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Uh oh..."
message:#"Looks like that user doesnt exist. Might want to double check that."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[self.navigationController popToRootViewControllerAnimated:YES];
[alertView show];
} else {
NSLog(#"ObjectID = %#", parseCurrentFriend.objectId);
parseCurrentFriend[#"title"] = friendTitle;
NSLog(#"Saved Title");
parseCurrentFriend[#"description"] = friendDescription;
NSLog(#"Saved Description");
parseCurrentFriend[#"latitude"] = friendLatitude;
NSLog(#"Saved Latitude");
parseCurrentFriend[#"longitude"] = friendLongitude;
NSLog(#"Saved Longitude");
}
}];
}
So this code all works fine at retrieving the required object. It reaches the NSLog line in the "else" statement where it is supposed to display the object ID. This displays the correct object id for the object that I am trying to receive. Now heres the weird part. The code seems to stop there. Nothing happens when trying to save the properties (title, description, lat, long) to the respective NSStrings; none of those other NSLog's below that are executed. But the weird thing is that there are no errors and the app keeps running as if that code below the first NSLog wasn't even there. Any insight into why this isn't working.
One problem you have is that you never save the object. After you modify all of the values, you need to save the PFObject.
[parseCurrentFriend saveInBackground];
Edit: Since you are trying to extract the values, then you'd need to do it like below.
friendTitle = parseCurrentFriend[#"title"];
friendDescription = parseCurrentFriend[#"description"];
friendLatitude = parseCurrentFriend[#"latitude"];
friendLongitude = parseCurrentFriend[#"longitude"];