Is it possible to use a PFQueryTableView with a User relation? For example I can easily list user relations (favorited items in this case) in a regular Tableview like so:
PFRelation *relation = [self.currentUser relationForKey:#"favorites"];
[[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// There was an error
} else {
// NSLog(#"%#", objects);
salesArray = nil;
salesArray = [[NSMutableArray alloc]initWithArray:objects];
[self.tableView reloadData];
}
}];
But PFQueryTableView requires you to put in a parseClassName. So would I put in the Users class here then overwrite objectsLoad method or something to obtain a specific logged in users favorites?
Not sure if this was the best solution but I created a Dummy class to avoid PFQueryTableView from automatically downloading data. I basically created an empty Class on Parse named Dummy. Then I just set my objects into self.objects and then [self loadObjects]; to refresh the tableview.
Related
I am using this query to find users, it works but it just shows me the first user. I want it to show me the user with the text of an UITextField.
How can I do that ?
(I have a textfield and there I type in a name and then it should show the parsed users with the name)
PFQuery *query = [PFUser query];
NSArray *users = [query findObjects];
userQuerys.text = users[0][#"username"];
Thanks very much
This code will fetch you all the PFUsers in which username is equal to the name parameter:
- (void)searchUsersNamed:(NSString *)name withCompletion:(void (^)(NSArray *users))completionBlock {
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:name];
[query findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// we found users with that username
// run the completion block with the users.
// making sure the completion block exists
if (completionBlock) {
completionBlock(users);
}
} else {
// log details of the failure
NSLog(#"Error: %# %#", error, [error description]);
}
}];
}
An example, if you need to update the UI with the result, for example, a table:
- (void)someMethod {
// we will grab a weak reference of self to perform
// work inside the completion block
__weak ThisViewController *weakSelf = self;
//replace ThisViewController with the correct self class
[self searchUsersNamed:#"Phillipp" withCompletion:^(NSArray *users) {
//perform non-UI related logic here.
//set the found users inside the array used by the
//tableView datasource. again, just an example.
weakSelf.users = users;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//pefrorm any UI updates only
//for example, update a table
[weakSelf.tableView reloadData];
}];
}];
}
A small note: the completionBlock here wont run if there is an error, but it will run even if no users were found, so you gotta treat that (if needed. in this example, it was not needed).
Avoid running non-UI related logic on that mainQueue method, you might lock the Main thread, and that`s bad user experience.
I am developing a chat app in iOS .There I need to add friends of the currentUser into an array and then send it to parse and save it there as an array.
As the user should add the their friend only one time and after that I need to do some functionalities there.
So How can I check that the user is already added as friend as well as how can I retrieve the array of friends?
Please if anyone can suggest me something then it will be a great help for me as I am new to iOS and Parse.
You can create a join table on Parse which stores who is friends with who.
With a PFQuery you can then filter it to get only the friends related to a certain person. You could call this table "Friendship", which stores 2 User objects, meaning, they are friends.
Update
You would have a PFObject subclass called Friendship. the Friendship object would hold 2 things, one is the User and the other is the Friend. Both should be PFUser type.
You don't need to go into parse for creating the join table, if you create a new object in code and save it, parse will create a table for it. So if you create the first Friendship object and save it, parse will keep it in it's own table of Friendships.
Saving a Friendship would be just like this:
- (void)addFriend:(PFUser *)friend {
Friendship *friendship = [[Friendship alloc] init];
friendship.user = self.user;
friendship.friend = friend;
[friendship saveInBackground];
}
This is just a simple example.
To fetch a list of friends from the user:
- (void)fetchFriendsList {
//create a query for friendships
PFQuery *friendsQuery = [[PFQuery alloc] initWithClassName:[Friendship parseClassName]];
//filter it to find only friends for the user
[friendsQuery whereKey#"user" equalTo:self.user];
[friendsQuery findObjectsInBackgroundWithBlock:^(NSArray *friendships, NSError *error) {
if (!error) {
//do something with the list of friendships
self.friends = [friendships valueForKey:#"friend"];
//the above line create an array with only the friend value of each Friendship object inside the array
} else {
NSLog(#"error fetching friends: %#", [error description]);
}
}];
}
To find out if the friend is already added/exists:
- (void)friendExists:(PFUser *)friend withCompletionBlock:(void (^)(BOOL))completionBlock {
//create a query for friendships
PFQuery *friendsQuery = [[PFQuery alloc] initWithClassName:[Friendship parseClassName]];
//filter it to find only friends for the user
[friendsQuery whereKey#"user" equalTo:self.user];
[friendsQuery whereKey#"friend" equalTo:friend];
[friendsQuery findObjectsInBackgroundWithBlock:^(NSArray *friendships, NSError *error) {
if (!error) {
//if we get one result here, it means it already exists
BOOL alreadyExists = friendship.count > 0;
if (completionBlock) { //making sure there is a block as parameter
completionBlock(alreadyExists);
}
} else {
NSLog(#"error fetching friends: %#", [error description]);
}
}];
}
You need to have a block in there to treat the results, since making a PFQuery request is not instant.
Also , when adding more whereKey:, they are treated as "AND", which means, it will only return objects that comply with all the whereKeys.
That's basically it. I assume you know how to subclass the PFObject, if not, you can take a look at the documentation.
I am new with Parse and Ios-development.
I develop a ios-app that use Parse as backend.
I have got the main-function to work now, but i have a BIG problem.
I want to create a separate class for my API-handling to Parse. As i set it up now i have my parse-code directly in my view-controllers and as far as i know that not that nice coding.
But, the issue is to handle the background-jobs. Let say if i want to do a GET from the server, this can be done in a background-thread, just using "findObjectsInBackgroundWithBlock"
The problem is when i move this method to a separate API-class. Then my ViewController ask my API-class to get all the objects an the API-class will return it as soon its done. It will nor run in the background, i cant return a NSMutableArray with objects to the viewController until the fetch is done.
I have thinking that i maybe can get the data from parse synchronously in my API-class by using [query findObjects:&error] , as long as i figure out how to create my get-method in the API-class to run asynchronously.
I have try to create my API-method as a asynchronously method using blocks but will not run in background on a separate thread. (I am new to blocks an dont evan no if thats the correct way to crate a method that will run in a separate thread when using it)
Here is my API-method (Class: APIClient)
+ (void) GETAllShoppingGroups:(void (^) (NSMutableArray*))completionBlock{
//Create a mutable array (nil)
NSMutableArray *shoppingGroupsArray = nil;
//Create query for class ShoppingGroupe
PFQuery *query = [ShoppingGroupe query];
//Filter - find only the groups the current user is related to
[query whereKey:#"members" equalTo:[PFUser currentUser]];
//Sort Decending
[query orderByDescending:#"createdAt"];
//Tell Parse to also send the real member-objects and not only id
[query includeKey:#"members"];
//Send request of query to Parse with a "error-pointer"and fetch in a temp-array
NSError *error = nil;
NSArray *tempArray = [NSArray arrayWithArray:[query findObjects:&error]];
//Check for success
if (!tempArray) {
NSLog(#"%#", error);
NSLog(#"ERROR: %#", [error userInfo][#"error"]);
return completionBlock(shoppingGroupsArray);
} else {
//Seccess
shoppingGroupsArray = tempArray.mutableCopy;
completionBlock(shoppingGroupsArray);
}
}
Here is my ViewController Class (Class: ShoppingGruopViewController)
- (void) getAllObjects{
//Init array if nil
if (!self.shoppingGroupeArray) {
self.shoppingGroupeArray = [[NSMutableArray alloc]init];
}
//Remove old objects
[self.shoppingGroupeArray removeAllObjects];
//Get objects
[APIClient GETAllShoppingGroups:^(NSMutableArray* completionBlock){
if (completionBlock) {
[self.shoppingGroupeArray addObjectsFromArray:completionBlock]; }
[self.tableView reloadData];
}];
}
I am having trouble saving a PFRelation I have this code:
//set up the query
PFQuery *query = [PFQuery queryWithClassName:#"messageBank"];
[query whereKey:#"username" equalTo:name];
__weak User *weakSelf = self;
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(error) {
NSLog(#"No such user");
handler(NO, error,NO,NO);
}
else{
[weakSelf.friendsRelation addObject:object];
[weakSelf.friends addObject:object];
//save in the background
[weakSelf.messageBank saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(error) {
NSLog(#"Save error");
}
else {
NSLog(#"no error");
}
}];
handler(YES,nil,NO,NO); //no errors
//so the friend is added to the friends array, all we need to do is reload the table data don't need to init the array again, the relation is also added to the relation item so don't need to init that again
}
}];//end block
My code finds the messageBank object fine but it won't save it to the PFRelation friends. It doesn't even attempt to call [weakSelf.messageBank saveInBackgroundWithBlock.... weakSelf.messageBank is the local PFObject and weakSelf.friends is it's PFRelation. Anyone have any ideas what could be going wrong here? If I have a PFRelation in class A is it okay to have pointers in that relation to other objects in class A? Does it need to be in a different class? Any help would be much appreciated!!!
Here's a cleaned up version of the code that fetches an object and adds to its relation, and saves it...
PFQuery *query = [PFQuery queryWithClassName:#"messageBank"]; // by convention class names should be capital MessageBank, but using yours
[query whereKey:#"username" equalTo:name]; // better form is self.name assuming it is an attribute of self
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
// see note below about weakSelf
// assume self is a PFObject subclass with two relations
// (and generated setters) called friendsRelation and friends
[self.friendsRelation addObject:object];
[self.friends addObject:object];
// notice we save self here. that's who changed in the two preceding lines
[self saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(!error) {
// success
} else {
// handle error
}
}];
} else {
// handle error
}
}];
Please notice that there's no need to declare a __weak copy of the self pointer (though, there's no harm in it). That idiom is used to avoid a retain cycle between self and the block's owner. You need it only when self is the blocks owner (directly or indirectly). This isn't the case with parse's completion blocks.
I load a "Rooms" UICollectionView with specific images that the logged in user has selected in a previous view controller, by populating the "imageFilesArray" and telling the UICollectionViewCells to use its data:
-(void) retrieveSelectedImagesForRooms
{
//parse query where we search the selectedImage array column and return any entry where the array contains the logged in user objectid
PFQuery *getRooms = [PFQuery queryWithClassName:#"collectionViewData"];
[getRooms whereKey:#"selectedImage" equalTo:[PFUser currentUser].objectId];
[getRooms findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[roomsCollection reloadData];
}
}];
}
The next page has to show the specific lights that user has selected for that previously selected room image. So I add the row's objectid I've just selected to a new column on Parse, called "clickedRoom", when the room is selected:
-(void)selectedRoom:(PFObject*)object
{
[object addUniqueObject:object.objectId forKey:#"clickedRoom"]; //put object id into clickedRoom column on Parse to save what room you specifically chose so that the light images correspond to only that room
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!error){}
else{}
}];
}
- (void)collectionView:(UICollectionView*)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self selectedRoom:[imageFilesArray objectAtIndex:indexPath.row]];
[self performSegueWithIdentifier:#"myLights" sender:self];
}
Now, in the "Lights" page I need to show ONLY the light images that have the selected room's objectid in that "clickedRoom" column. I believe it's the same principle as how I retrieve the room images, but I can't figure out what I should be querying for, something like:
-(void) retrieveCorrespondingImagesForLights
{
PFQuery *getLights = [PFQuery queryWithClassName:#"collectionViewData"];
[getLights whereKey:#"clickedRoom" equalTo:**MY-PREVIOUSLY-SELECTED-ROW**.objectid];
[getLights findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[myLightsCollection reloadData];
}
}];
}
Any suggestions please?!
The retrieveCorrespondingImagesForLights is in a different view controller than your roomsCollection, correct? If so, then you will need to pass the object id of the selected room to the new view controller that is segued to at [self performSegueWithIdentifier:#"myLights" sender:self];
Take a look here Pass Index Number between UITableView List segue
In your case, you should add a property to your destination view controller (I'll call it LightsViewController) to capture the object, or objectId if that's all you need for the query. I would suggest something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"myLights"]) {
// note that "sender" will be the cell that was selected
UICollectionViewCell *cell = (UICollectionViewCell*)sender;
NSIndexPath *indexPath = [roomsCollection indexPathForCell:cell];
LightsViewController *vc = (LightsViewController*)[segue destinationViewController];
vc.selectedObject = indexPath.row;
}
}
Then, in retrieveCorrespondingImagesForLights:
PFQuery *getLights = [PFQuery queryWithClassName:#"collectionViewData"];
[getLights whereKey:#"clickedRoom" equalTo:self.selectedObject.objectid];
EDIT*
Without understanding your exact implementation details, it seems like you are trying to use Parse to pass data between your view controllers when you'd be better suited to do it natively in your app. Maybe I'm misunderstanding your issue.