I have implemented the following function (attached to the "register button") within my registration view controller as seen below. The function relies upon Parse and is seamless in general.
However I am encountering the following issues at present:
If a user inserts an invalid email address by mistake; the error string listed under "else" is activated (which is good) but the username and password entered above are registered regardless.
Users are able to leave the password field blank.
Any help whatsoever, especially pertaining to issue 1, would be immensely appreciated.
// Register user.
- (IBAction)registerUser:(id)sender
{
PFUser *user = [PFUser user];
user.username = self.mobileTextField.text;
user.password = self.passwordTextField.text;
user.email = self.emailTextField.text;
// Show loading HUD.
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!error)
{
[self performSegueWithIdentifier:#"userRegistered" sender:self];
}
else
{
NSString *errorString = [[error userInfo] objectForKey:#"error"];
UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[errorAlertView show];
// Dismiss loading HUD.
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
}
}];
// End editing.
[self.view endEditing: YES];
});
}
You have a number of problems here.
a, you absolutely do not need to and should not take a new thread. Parse does that for you. You must change it.
b, you may be looking for the magic formula in parse "if ( (!succeeded) || error)..."
c, you should really locally check that the email is valid before sending it. (ie, you can't enter "xyz#hotmail" or something not sensible as an email.)
ie, you need to write a routine like "checkThisEmailIsValid". if you need help with his yell out. note that it's not that easy conceptually. you understand that parse will try to verify the email right? ie it will send one of those emails "new user, click here to verify your email!" You're familiar with that?
d, a great secret is the 202 error code
Here's some example code from a production app, hope it helps!
-(void)_actuallyJoin
{
... do things like check the email is valid
... in this app the username is the lowercase email
PFUser *nuser = [PFUser user];
nuser.username = [self.email.text lowercaseString];
... in this app, the email is the email, password is the password
nuser.email = self.email.text;
nuser.password = self.password.text;
[APP huddie];
APP.hud.labelText = #"Registering ...";
APP.hud.detailsLabelText = #"1 of 3 ...";
... that is just MBProgressHUD.
[nuser signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if ( (!succeeded) || error)
{
[APP.hud hide:YES]; .. that's MBProgressHUD
.. usually, blank the form while app is connecting
self.email.text = #"";
self.password.text = #"";
self.confirmPassword.text = #"";
if ( error.code == 202 )
{
[PFAnalytics trackEvent:#"newAccount"
dimensions:#{ #"result":#"emailAlreadyUsed" }];
[self woe:#"That email address is already in use...."];
[PFUser logOut]; .. don't forget that
return;
}
[PFAnalytics trackEvent:#"newAccount"
dimensions:#{ #"result":#"connectionWoe" }];
[self woe:#"Couldn't connect. Please try later"];
return;
}
NSLog(#"step one rego success");
[self _actuallyJoinStepTwo];
... now continue to save other information
... for example user's address, age, avatar photo etc.
}];
}
Related
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.
My facebook login with parse is working perfectly with no issues but the access token that is generated is not showing permission for friendlist although I gave that permission at the time of login. I came to know when I used facebook Graph API 'Friendlists'(fbID/friendlists) and the response array is empty. So, also run Graph API explorer with the same access token generated. It does not show me any error and data array is same empty and a debug message with
"The field 'friendlists' is only accessible on the User object after the user grants the 'read_custom_friendlists' permission"
This is the method I am using
WLLoginViewController *login = [[WLLoginViewController alloc]init];
login.fields = PFLogInFieldsFacebook;
NSArray *permission = [[NSArray alloc]initWithObjects:#"email",#"read_custom_friendlists",#"publish_actions",#"user_location",#"user_hometown",#"user_website",#"user_about_me",#"user_photos",#"user_friends",#"read_custom_friendlists", nil];
login.facebookPermissions = permission;
WLLoginViewController has inherited PFUserLoginManager and I am calling it from some other class.
- (void)logInViewController:(PFLogInViewController *)logInController didLogInUser:(PFUser *)user {
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(#"permissions%#",logInController.facebookPermissions);
if(result) {
if ([result valueForKey:#"hometown"]) {
NSString *nn = [[result valueForKey:#"hometown"] valueForKey:#"name"];
[user setValue:[[result valueForKey:#"hometown"] valueForKey:#"name"] forKey:#"hometown"];
}
if ([result valueForKey:#"location"]) {
[user setValue:[[result valueForKey:#"location"] valueForKey:#"name"] forKey:#"location"];
}
[user setObject:[result valueForKey:#"id"] forKey:kWLUser_FacebookId];
[user setObject:[result valueForKey:#"name"] forKey:kWLUser_Name];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if([PFInstallation currentInstallation]) {
// save in background current installation.
[[PFInstallation currentInstallation] setObject:user forKey:#"user"];
[[PFInstallation currentInstallation]saveInBackground];
}
}];
[[ParseManager sharedInstance]saveDeviceToken:[[NSUserDefaults standardUserDefaults]objectForKey:#"DeviceToken"]];
[self dismissViewControllerAnimated:YES completion:nil];
}else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Could not login" message:#"Could not login to facebook, please try again" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
}];
}
This is the method which is which is running when the user return to the app from facebook.The nslog in the code is showing the permission perfectly which I gave.
And finally this is the method for handling facebook request
-(void)handleFacebookFriendsRequest {
NSString *queryParams = #"id,name,picture.width(350).height(250),location,hometown,likes.limit(100000),statuses.limit(1),languages";
[queryParams stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#",[FBSession activeSession].permissions);
NSLog(#"%#",[[FBSession activeSession].permissions description]);
NSLog(#"%#",[FBSession activeSession].accessTokenData.accessToken);
[FBRequestConnection startWithGraphPath:[NSString stringWithFormat:#"me/friends?fields=%#",queryParams] completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
//change For getting everything out
NSLog(#"%#",[result objectForKey:#"data"]);
[[ApplicationDataModel sharedInstance]setFacebookFriendsList:[result objectForKey:#"data"]];
[self facebookRequestDidLoad:result];
} else {
[self facebookRequestDidFailWithError:error];
}
}];
}
I am stuck badly need help. Thanks in advance
The read_custom_friendlists is not a default permission, as a result you have to go through the approval process for this feature (https://developers.facebook.com/docs/facebook-login/permissions/v2.2)
To submit items for approval go to:
developers.facebook.com -> My Apps -> Status & Review
Caveat: "People listed in the Roles section of your App Dashboard - including Admins, Developers and Testers - can grant any permission without review. If only people in these Roles will use your app, you don't need to submit it for Login Review."(https://developers.facebook.com/docs/apps/faq)
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.
I am trying to reset my User's password by verifying their Email and Username. Parse only provides you with an Email check. I am trying to verify the User's Username too before proceeding.
My code is not working. I know it is because I set the password to nil but I do not know any other way.
If anyone knows another way, it would be great if you could help me out, thanks.
self.stringEmailAddress = [self.outletEmailTF.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
self.stringUsername = [self.outletUsernameTF.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([self.stringEmailAddress length] == 0 || [self.stringUsername length] == 0) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Oops!" message:#"Make sure you enter a valid Username & Email Address" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
} else {
[PFUser logInWithUsernameInBackground:self.stringUsername password:nil block:^(PFUser *user, NSError *error) {
if (user) {
[PFUser requestPasswordResetForEmailInBackground:self.stringEmailAddress
block:^(BOOL succeeded, NSError *error) {
if (succeeded)
{
NSLog(#"Reset Successful!");
self.outletEmailTF.text = #"";
self.outletLabel.text = #"Password reset has been sent!";
}
else
{
NSLog(#"Reset Unsuccessful!");
self.outletLabel.text = #"Email Address unrecognized!";
}
}];
} else {
NSLog(#"No good!");
}
}];
}
You can't do it by trying to log a user in without a password. Instead, do a query for a user with the specified email address and username and check how many results you get.
You shouldn't really need to check the username as the password reset is by email, and if the users email is compromised then so is the username...
I downloaded the Parse example AnyPic. I have started looking into it for some ideas for my new app that will use Parse.
When AnyPic first opens up it requires the user to log in with facebook. I have been able to do some testing on the simulator, but now that I have started on the device I cannot log in through facebook. When I try it stays on the log in screen and just changes the log in button to log out. I have stepped through the code and found that the facebook userID is coming back nil. All the other facebook info seems to be returning values.
NSString *accessToken = [[[FBSession activeSession] accessTokenData] accessToken];
NSDate *expirationDate = [[[FBSession activeSession] accessTokenData] expirationDate];
NSString *facebookUserId = [[[FBSession activeSession] accessTokenData] userID];
if (!accessToken || !facebookUserId) {
NSLog(#"Login failure. FB Access Token or user ID does not exist");
return;
}
The facebokUserId comes back nil, while the others have information. I have tried hard coding the facebook user ID to what the simulator gives when it succeeds. This has not worked. I have also tried substituting my own facebook app IDs into the app to see, and it still not work.
I am new with working with social stuff, is there something I'm missing to set up facebook? Or has anyone worked with this example know how to fix it or be able to skip the facebook login?
I ended up remaking a new facebook app ID. I subbed the new info into parse and the app. I also figured out that the wrong facebook account was logged in on the device. Once I logged into the facebook account that made the new facebook app ID it worked right away.
I was having this same issue when running Anypic on a device and submitted a support ticket with Facebook. They confirmed that it's an issue when signing in with the Facebook application and recommended using PFFacebookUtils logInWithPermissions as a workaround until they're able to look into it further. I changed the handleFacebookSession method within PAPLogInViewController.m to the code below and was finally able to login with a device!
- (void)handleFacebookSession {
if ([PFUser currentUser]) {
if (self.delegate && [self.delegate respondsToSelector:#selector(logInViewControllerDidLogUserIn:)]) {
[self.delegate performSelector:#selector(logInViewControllerDidLogUserIn:) withObject:[PFUser currentUser]];
}
return;
}
NSArray *permissionsArray = #[ #"public_profile",
#"user_friends",
#"email"];
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
// Login PFUser using Facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
if (!user) {
NSString *errorMessage = nil;
if (!error) {
NSLog(#"Uh oh. The user cancelled the Facebook login.");
errorMessage = #"Uh oh. The user cancelled the Facebook login.";
} else {
NSLog(#"Uh oh. An error occurred: %#", error);
errorMessage = [error localizedDescription];
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Log In Error"
message:errorMessage
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"Dismiss", nil];
[alert show];
} else {
if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
} else {
NSLog(#"User with facebook logged in!");
}
if (!error) {
[self.hud removeFromSuperview];
if (self.delegate) {
if ([self.delegate respondsToSelector:#selector(logInViewControllerDidLogUserIn:)]) {
[self.delegate performSelector:#selector(logInViewControllerDidLogUserIn:) withObject:user];
}
}
} else {
[self cancelLogIn:error];
}
}
}];
}