iOS-Parse How to retrieve an object given a username? - ios

I have two ViewControllers, in the first one, there are 3 key-value pairs in each PFObject, save them to parse after clicking a button. In the second ViewController, I want to create another property and save it to the same PFObject. Here is my code:
in the first ViewController:
- (void)next
{
PFObject *thisuser = [PFObject objectWithClassName:#"User"];
thisuser[#"name"] = [PFUser currentUser].username;
thisuser[#"institution"] = institution.text;
thisuser[#"major"] = major.text;
[thisuser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (succeeded)
{
GuideViewController2 *GVC2 = [[GuideViewController2 alloc]initWithNibName:#"GuideViewController2" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:GVC2];
nav.modalPresentationStyle = UIModalPresentationFullScreen;
nav.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:nav animated:YES completion:nil];
NSLog(#"success");
}
else
{
NSLog(#"nope");
}
}];
}
and in my second view controller, the user can upload a profile photo, i want this photo to be saved in the same PFObject. So is there a way to retrieve an object using [PFUser currentUser].username property? How do i get this user object under the User class in order to add a photo key-value pair?
thx.

You can just query for the user:
PFQuery *query = [PFUser query];
[query whereKey:USERNAME_KEY equalTo:username];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error)
{
if (!error) {
//You found the user!
PFUser *queriedUser = (PFUser *)object;
}
}];
Supposing the username is actually unique! USERNAME_KEY would be your username field and username the actual username.

Related

Changing field in PFUser from other PFUser, Parse iOS

I am attempting to change the value of another PFUser field, from another unauthenticated PFUser in Parse, however I cant seem to do so. I am attempting to increase the number of 'hours' of one user from another user. Here is how I am attempting to do so:
PFUser *currentUser = [PFUser currentUser];
PFACL *ACL = [PFACL ACLWithUser:[PFUser currentUser]];
[ACL setPublicReadAccess:YES];
PFUser *selectedUser = [self.formValues objectForKey:#"user"];
NSLog(#"User ID: %#", selectedUser.objectId);
PFObject *volunteerSheet = [PFObject objectWithClassName:#"VolunteerSheet"];
volunteerSheet[#"userID"] = selectedUser.objectId;
volunteerSheet[#"fromID"] = currentUser.objectId;
volunteerSheet[#"volunteerTitle"] = [self.formValues objectForKey:#"title"];
volunteerSheet[#"location"] = [self.formValues objectForKey:#"location"];
volunteerSheet[#"volunteerHours"] = [self.formValues objectForKey:#"hours"];
volunteerSheet[#"volunteerDescription"] = [self.formValues objectForKey:#"description"];
volunteerSheet.ACL = ACL;
[volunteerSheet saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// The object has been saved.
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:selectedUser.objectId
block:^(PFObject *user, NSError *error) {
[user incrementKey:#"volunteerHours" byAmount:(NSNumber*)[self.formValues objectForKey:#"hours"]];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
if (succeeded) {
NSLog(#"Succeeded");
}else{
NSLog(error.description);
}
}];
}];
NSLog(#"Saved");
[self dismissViewControllerAnimated:YES completion:^{
UIAlertView * alert =[[UIAlertView alloc ] initWithTitle:#"Success!"
message:#"Hours Sent succesfully."
delegate:self
cancelButtonTitle:nil
otherButtonTitles: nil];
[alert addButtonWithTitle:#"Okay"];
[alert show];
}];
} else {
// There was a problem, check error.description
NSLog(#"Error: %#",error.description);
}
}];
Users automatically have security put in place that disallow modifying another user (clps or alcs). Maybe consider moving to cloud code and calling use master key?
Users have ACLs set up to only allow themselves to change their values. To get around this, you either have to change the ACL every time you create a user to allow any user to have write permissions for them (VERY RISKY AND NOT SECURE AT ALL), or you need to call a cloud function that calls Parse.Cloud.useMasterKey() , which overrides all permissions, and will enable you to make the changes. Obviously, you should still be doing some sort of security check to make sure that these changes are allowed / proper changes before putting them through.

How do I get notified when a PFObject is saved to my Parse backend?

I have followed the Quick Start Guide for parse:
https://parse.com/apps/quickstart#parse_data/mobile/ios/native/existing
I'm using this code to save a PFObject:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackground];
Below is what it looks like when my PFObject is saved to my Parse backend:
I would like to receive some sorta notification when this happens. Like an email, a text or a push notification. Anything that works.
Parse's recommended way of doing it is using Cloud Code. Once, you configure it, in the main.js file add the following function:
Parse.Cloud.afterSave("Posts", function(request) {
// Gets called when a new object is added to the 'Posts' class.
// From here you can proceed to send push notifications
});
The afterSave method it's triggered after a new object is added to the specified class. If you want to manage, whether or not an object should be saved, look into beforeSave.
Quick example I just put together: example:
-(void)saveLesson {
lesson = [PFObject objectWithClassName:#"lessons"];
PFObject *photo = [PFObject objectWithClassName:#"photoslessons"];
[photo setObject:[PFUser currentUser] forKey:#"userId"];
[photo setObject:self.photoFile forKey:#"lessonimage"];
[photo setObject:self.smallPhotoFile forKey:#"smalllessonimage"];
[photo setObject:self.thumbnailFile forKey:#"thumblessonimage"];
[photo setObject:self.originalFile forKey:#"originallessonimage"];
PFACL *publicACL = [PFACL ACL];
[publicACL setPublicWriteAccess:YES];
[publicACL setPublicReadAccess:YES];
photo.ACL = publicACL;
self.photoPostBackgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.photoPostBackgroundTaskId];
}];
[photo saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Photo uploaded");
NSNumber *userCount = [[NSNumber alloc] initWithInt:0];
lesson[#"textBody"] = [[[self contentView] descriptionText] text];
lesson[#"title"] = [[[self contentView] title] text];
lesson[#"likes"] = userCount;
lesson[#"times_viewed"] = userCount;
lesson[#"featured"] = #"false";
lesson[#"userId"] = [PFObject objectWithoutDataWithClassName:#"_User" objectId:[PFUser currentUser].objectId];
lesson[#"lessonimage"]= photo;
[lesson saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
PFQuery *query = [PFQuery queryWithClassName:#"activesessions"];
[query whereKey:#"teacherid" equalTo:[PFUser currentUser]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (object!=nil) {
[object setObject:lesson forKey:#"lessonid"];
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[(NSHLiveStudentProfileViewController*)self.navigationController.viewControllers[0] thisIsAHack];
[[self navigationController] popToRootViewControllerAnimated:true];
}
}];
}}];
} else if (error) {
NSString * alertTitle = #"Error Saving Lesson";
UIAlertController * alert = [UIAlertController alertControllerWithTitle:alertTitle message:#"Please make sure to include both a title and description." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { }];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:^{}];
}
}];
} else {
NSLog(#"Photo failed to save: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Couldn't post your photo" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Dismiss", nil];
[alert show];
}
[[UIApplication sharedApplication] endBackgroundTask:self.photoPostBackgroundTaskId];
}];
}
So, that's a lot of code, but here's what we are going to do to help you out, lets add a method to send yourself an email:
First off, download load the pod mailgun:
then do this:
Mailgun *mailgun = [Mailgun clientWithDomain:kMailGunAPIClientDomain apiKey:kMailGunAPIKey];
Also, sign up for a mail gun account, and send an email to yourself
Then in the code above, whenever and object is saved with "success" in the block like this:
first off, obviously change this:
#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
to your mail address and then fill in the portions that require your API key and such that you get from MailGun, mailgun is cheap and this works for you immediately as soon as you set up the mail gun account (I dont work for mailgun, I'm jut a happy user)
[lesson saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[mailgun sendMessageTo:#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
from:#"Excited User <apeterpan.notarealemailaddress#gmail.com>>"
subject:#"Mailgun is awesome!"
body:#"and life is great!"];
boom!,
Here's the MailGun pod: https://github.com/rackerlabs/objc-mailgun
Sign up for a free account at: http://www.mailgun.com/
Test it out, the neat thing about this, is it's auto trigger and you can put whatever you want inside the email call that I've placed here, you also need to get AFNetworking:
get it here: https://github.com/AFNetworking/AFNetworking
This method doesn't require any sort of javascript cloudcoding and you can even substitute the body of your email for the informaiton you want to send to yourself. If you want to send yourself the USER ID of the person who just triggered your method, then there you have it, the email will be sent.
So you have this:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackground];
Change it to this:
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"successfully SAVED!");
}
}];
this is your custom save function:
-(void)customSaveFunction {
PFObject *testObject = [PFObject objectWithClassName:#"TestObject"];
testObject[#"Bookmarks"] = #"Restore Successful";
[testObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"successfully SAVED!");
Mailgun *mailgun = [Mailgun clientWithDomain:kMailGunAPIClientDomain apiKey:kMailGunAPIKey];
[mailgun sendMessageTo:#"Peter Pan <peterpan.notarealemailaddress#gmail.com>"
from:#"Excited User <apeterpan.notarealemailaddress#gmail.com>>"
subject:#"Mailgun is awesome!"
body:#"and life is great!"];
}
}];
}
I showed the save lesson example as something where you can send multiple emails with multiple information in the email for differnet save triggers, but this code is useless to me now, this was used for major testing purposes only, so the custom save function that I posted above these few sentences is how you would do it yourself, or just put that same code in your viewcontroller or whereever you save your objects.

How to retrieve certain images from Parse.com

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.

Delete item from the database of Parse.com (iOS App)

I'm using Parse.com for my iOS application 8 ...
In parse database I created a new class called "Relationships", what I'm trying to do is prefix the user of my app to send a friend request to another user. I'm not using PFRelation because I need that friend request is not automatic, but accepted by the user.
In short, the user sends the request richeista of friendship and this remains within the class "Relationship" with the status "Waiting" until the subscriber does not accept the request.
Now I'm able to do everything I can:
User pointer to register the two (receiver and forwarder's friend request)
Insert the request status "pending"
My problem is that if my user does not want more 'send the request can not' delete ..
I tried using the ["name of PFObject" deleteInBackground] but I can not delete anything ...
Can you help me figure out how to delete the newly created data from the database to parse?
#pragma mark ADD FRIENDS
-(void)addFriendUserButtonPressed:(UITableViewCell *)customCell {
NSIndexPath *indexPath = [self.tableViewFindUser indexPathForCell:customCell];
PFObject *richiesta = [PFObject objectWithClassName:#"Relation"];
if (!isFiltered) {
PFUser *userFiltered = [self.userArray objectAtIndex:indexPath.row];
if (![self Is_InAttesa:userFiltered]) {
[richiesta setObject:userFiltered forKey:#"To_User"];
[richiesta setObject:[PFUser currentUser] forKey:#"From_User"];
[richiesta setObject:#"Pending" forKey:#"STATUS"];
[richiesta saveInBackground];
} else {
//[richiesta removeObject:[PFUser currentUser] forKey:#"From_User"];
//[richiesta setObject:userFiltered forKey:#"STATUS"];
//[richiesta saveInBackground];
}
}
else {
PFUser *userNotFiltered = [self.userFiltrati objectAtIndex:indexPath.row];
[richiesta setObject:userNotFiltered forKey:#"To_User"];
[richiesta setObject:[PFUser currentUser] forKey:#"From_User"];
[richiesta setObject:#"Pending" forKey:#"STATUS"];
[richiesta saveInBackground];
}
}
This is the Boolean method that I created to recognize (through a query) if users are present in the list of pending friend requests
-(BOOL)Is_InAttesa:(PFUser *)user_inattesa {
for (PFUser *userInAttesa in amiciInAttesaMutableArray) {
if ([[[userInAttesa objectForKey:#"To_User"]objectId] isEqualToString:user_inattesa.objectId]) {
return YES;
}
}
return NO;
}
Here is a method for deleting object from parse.
-(void)deleteButton {
//Query or retrieving data from dB which you want to delete.
PFQuery *query = [PFQuery queryWithClassName:#"YOUR_CLASS"];
//This string in below case takes name from textfield that user wants to delete. For your case you could modify it as per your need.
NSString *receiver_idStr =#"Id";
NSString *sender_idStr =#"Id";
// below two queries will work as like SELECT * FROM someTable WHERE senderId = 'id' AND receiverId = 'id'
[query whereKey:#"request_sender_id" containsString:sender_idStr];
[query whereKey:#"request_receiver_id" containsString:receiver_idStr];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { //Query fired in background to search in parse for this object with condition provided.
if (!error) {
NSLog(#"Successfully retrieved: %#", objects);
//Now as you got object then you will type cast object from NSArray to PFObject and perform deleteInBackground method on them.
//Also update that UI part ,i.e., remove the request object from UI.
}
else {
NSLog(#"Error: %#", [error localizedDescription]);
}
}];
}
So this way to will be able to delete request object from parse. Also when user who has send the request cancel the request then also u search for that request object and do the same where else on user who receive request u could push notification to remove that request from it's UI.
In case user who receive request delete then it's simply find request object and delete, and update of UI for both sender and receiver.
-(void)addFriendUserButtonPressed:(UITableViewCell *)customCell {
NSIndexPath *indexPath = [self.tableViewFindUser indexPathForCell:customCell];
PFObject *richiesta = [PFObject objectWithClassName:NPFriendClass];
if (!isFiltered) {
PFUser *userFiltered = [self.userArray objectAtIndex:indexPath.row];
if (![self Is_InAttesa:userFiltered]) {
[richiesta setObject:userFiltered forKey:NPFriend_AUser];
[richiesta setObject:userFiltered.objectId forKey:#"OBJECT_USER_ID"];
[richiesta setObject:userFiltered.username forKey:#"Username"];
[richiesta setObject:[PFUser currentUser] forKey:NPFriend_DaUser];
[richiesta setObject:#"Richiesta In Attesa" forKey:NPFriendRequestStatus];
[richiesta saveInBackground];
} else {
PFQuery *query = [PFQuery queryWithClassName:NPFriendClass];
[query whereKey:NPFriend_DaUser equalTo:[PFUser currentUser]];
[query whereKey:NPFriendRequestStatus equalTo:#"Richiesta In Attesa"];
[query whereKey:#"OBJECT_USER_ID" equalTo:userFiltered.objectId];
[query includeKey:NPFriend_AUser];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
for (PFObject *object in objects) {
NSLog(#"Successfully retrieved: %#", object);
[object deleteInBackground];
}
}
else {
NSLog(#"Error: %#", [error localizedDescription]);
}
}];
}
}
else {
PFUser *userNotFiltered = [self.userFiltrati objectAtIndex:indexPath.row];
[richiesta setObject:userNotFiltered forKey:NPFriend_AUser];
[richiesta setObject:[PFUser currentUser] forKey:NPFriend_DaUser];
[richiesta setObject:#"Richiesta In Attesa" forKey:NPFriendRequestStatus];
[richiesta saveInBackground];
}
}
Hello Walle thanks again for your help you have been very kind and helpful ...
I fixed it this way and it seems to work ...
The only problem that remains is that it does not update the data immediately so the user can not figure out if you sent the request or not. The tableview is updated only if it does refresh the Tableview or change viewcontroller ..
I tried to redo do the query again as soon as the user sends a friend request but overlapping data and slows down the app ... How can I get the data refresh every minute without calling the query?
The idea of the button selected or not starch could be good? I'm trying but maybe something wrong because I can not get it to work

PresentViewController from NSObject?

Okay so the problem I'm having in one of my functions is when users are logging in through Facebook, and my app is checking wether or not their Facebook username already exists in our app (Parse.com) database.
If so, present a view controller (which is where I'm having a problem since this is in a NSObject) if not, simply write their facebook username to parse username string. Error I get is: No known class method for selector 'presentViewController:animated:completion'
Here is my code:
PFQuery *query = [PFUser query]; //1
[query whereKey:#"username" equalTo:me.username];//2
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {//4
if (!error) {
NSLog(#"Successfully retrieved: %#", objects);
if ([objects count] > 0)
{
// LOOKING TO PRESENT VIEW CONTROLLER HERE?
// TRIED:
// [self presentViewController:[#"RegisterUsername" animated:YES completion:nil];
} else {
[[PFUser currentUser] setObject:me.username forKey:#"username"];
}
} else {
NSString *errorString = [[error userInfo] objectForKey:#"error"];
NSLog(#"Error: %#", errorString);
}
}];
and since this is inheriting from NSObject, it won't allow me to use the presentViewController. Not sure what else to do from here. :-( This view controller was going to make them set a username before continuing with our app. This code is currently in a login function in a header Comms.h (NSOBJECT) for Facebook iOS SDK
NSObject does not declare the method presentViewController:. You'll need to add a UIViewController subclass to the window and then present from that. In the completion block of your dismissViewController you should remove the parent from the window.
Alternatively you could create a delegate protocol for checking the username and then notify a UIViewController of the need to either set the username internally or present a login view.

Resources