I'm having an issue retrieving the a profile picture from a PFFile column in the Parse User class. I read in the docs that you need to use the PFUser object to query the User class. Right now no image is showing up. Here is my code:
PFUser *user = [PFQuery getUserObjectWithId:[PFUser currentUser].objectId];
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
_profileImage.file = [object objectForKey:#"profilePicture"];
}];
(1) The current user's PFUser object is in fact [PFUser currentUser] so all that code in your first line could be replaced with:
PFUser *user = [PFUser currentUser];
(2) Everything else looks OK, but if you're using a PFImageView, your [profileImage loadInBackground]; call should also be within the fetchIfNeededInBackgroundWithBlock: async block.
Related
I am using Parse as my backend for my app. Once the user logs into their account I am trying to get the next view to say "Welcome, (First Name)" at the top. However, I cannot seem to figure out how to accomplish this even using Parse's online documents. Their site directed me here for further assistance. I have tried using their query feature, but could not figure it out. In other words, I am trying to pull the current logged in user's first name, from the database and display it once logged in.
Current code:
PFQuery *query = [PFUser query];
[query whereKey:#"firstName" equalTo:currentUser]; // find user's first name
NSArray *firstName = [query findObjects];
Previous code:
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
// do stuff with the user
Welcome.text = [NSString stringWithFormat:#"Welcome,", currentUser];
Your overcomplicating things.
You don't have to execute a query every time the view loads, instead you should put this in a plist or NSUserDefaults as not to use an API request simply to display the current users name.
However, you can do the following to the current users username :
if ([PFUser currentUser]) {
Welcome.text = [NSString stringWithFormat:#"Welcome, %#", [PFUser currentUser].username];
}
First of all you should check if you actually sign-up and/or logged into Parse with this kind of function:
[PFUser signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
}];
[PFUser logInWithUsernameInBackground:#"My username" password:#"My password" block:^(PFUser *validUser, NSError *error) {
}];
Check this link: https://parse.com/docs/ios_guide#users-signup/iOS
After you did this, whenever you you want to retrieve your user information,
[PFUser currentUser] is the right way to call some information:
Say for example you want to retrieve the objectId you can get it like this:
NSString *str = [PFUser currentUser].objectId;
Or say you want to set a custom value like this:
NSString *str = #"My custom object";
[PFUser setObject:str forKey:#"MyCustomObject"];
[PFUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
}];
Than you retrieve it like this:
NSString *str = [PFUser objectForKey:#"MyCustomObject"];
You should really check the documentation and example by Parse that are really well explained ! ;)
(Here are some tutorials/Examples by Parse: https://parse.com/tutorials)
How is the transition from your login to your main view set up? Are they both two different controllers?
If so, you should look into NSNotificationCenter...
In your MainViewController, implement
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadObjects)
name:#"loginFinished"
object:nil];
And loadObjects will be
- (void)loadObjects
{
Welcome.text = [NSString stringWithFormat:#"Welcome %#", [[PFUser currentUser] objectForKey:"userNameField"]];
}
* You need to parse the PFUser object to access its fields. Its just a dictionary so supply it a key 'username' or whatever, and you receive a value 'myusername'. *
Then in your LoginViewController, within your [PFUser logInWithUsernameInBackground:password:block
Implement this
[PFUser logInWithUsernameInBackground:#"My username" password:#"My password" block:^(PFUser *validUser, NSError *error) {
if (!error) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"loginFinished" object:nil];
}
}];
But this is basically how you should setup your login->main flow. Learn NSNotifications, Delegation, and maybe KVO... Learning these will make you a understand how data can be passed around in the iOS/Mac environment.
Heres an analogy for all of them:
NSNotification: A teacher(NSNotification poster) announces a test to all his students(NSNotification observer), or at least the one's who are currently in class, students missing class aren't observing.
Delegate: A student finishes a test and informs the professor(delegate).
KVO: A student(KVO poster) completes a question and raises their hand where the teacher or even other students could be KVOs (key-value observers) and act on their action.
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
I'm trying to delete the image connected to the current user from the imageOne column in Parse.com. From the user class.
PFQuery *query = [PFUser query];
[query selectKeys:#[#"imageOne"]];
[query getObjectInBackgroundWithId:[[PFUser currentUser] objectId] block:^(PFObject *object, NSError *error) {
if (!error) {
[object deleteInBackground];
}
}];
My code doesn't work and the console logs this error "User cannot be deleted unless they have been authenticated via logIn or signUp".
How can I fix this?
Seems like the problem comes from the fact that object (image) comes from the user class, am I right?
Why are you doing a query for all users and then doing the delete for just the current user, that's the worst possible way to structure the query (and most likely to fail).
If the current user isn't in the first 100 returned your above code would never find a match.
This sort of query should instead be done using getObjectInBackgroundWithId:block:, but in the case of the current user you already have the object, just do this:
[[PFUser currentUser] deleteInBackground];
If instead you just want to delete information in a column, use the following:
PFUser *currentUser = [PFUser currentUser];
[currentUser removeObjectForKey:#"imageOne"];
[currentUser saveInBackground];
I would like write a PFUser object by the currentUser, i've added the ACL based on the Parse developer guide, but i still get an error:
'User cannot be saved unless they have been authenticated via logIn or signUp'
_ My code:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:self.bBo];
PFObject *friendData = [query getFirstObject];
PFUser *user = (PFUser *)friendData;
PFACL *userACL = [PFACL ACL];
user.ACL = userACL;
[userACL setWriteAccess:YES forUser:[PFUser currentUser]];
PFRelation *friendRelation = [user relationforKey:#"array"];
[friendRelation addObject:[PFUser currentUser]];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"Error %# %#", error, [error userInfo]);
}
}];
I think i did everything correct, so i can't figure out what can be the problem. So if you made earlier something like this or know where is the problem, i would really appreciate any suggestions.
For security reasons, Parse won't allow you to save any changes to a user that is not currently logged in.
If you want to be able to make and save changes to user, you need to use Cloud Code and the Master Key to get around this roadblock.
I have had multiple problems like this before, and every time I've been forced to use a workaround via Cloud Code.
Here's an example of a workaround I did for creating a friends relationship between two users:
[PFCloud callFunction:#"editUser" withParameters:#{#"userId": user.objectId}];
The above code statement is in xcode, and executes the function I have added to my Cloud Code file.
Then, here's what my Cloud Code file looks like:
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
var currentUser = request.user;
var relation = user.relation("friendsRelation");
relation.add(currentUser);
Parse.Cloud.useMasterKey();
user.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
The above code uses the Master Key to save changes to both the currently logged in user, and to the user who's objectId was passed into this function.
Code details:
var relation
This is just a variable I'm creating to hold what the following fucntion returns:
user.relation("friendsRelation");
In the above function, "friendsRelation" is the name of my PFRelation key in Parse.
Now that we have a valid relation object contain in our variable called relation, we execute this function with an argument of our currentUser object.
After that, all that's left is saving everything. I don't program with javascript, but I was still able to come up with the above solution by looking at the Parse Cloud Code docs, and searching around on their support forums.
If you take my template from above, and make some small changes, then you should be able to easily accomplish what you need. You just have to jump through these extra hoops because the Parse SDK doesn't want a situation where someone can login and somehow make changes to another user's account, whether by their own fault or a developer's mistake.
EDIT:
Here is the code to add the relationship for the current user:
PFRelation *friendsRelation = [[PFUser currentUser]relationforKey:#"friendsRelation"];
PFUser *user = [self.parseUsers objectAtIndex:indexPath.row];
[friendsRelation addObject:user];
[currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
}
}];
And then you can call the Cloud Code method right after:
[PFCloud callFunction:#"editUser" withParameters:#{
#"userId": user.objectId
}];
i need to get a list of f.e the 15 nearest users using my app. The current location of the current user is stored like this:
PFGeoPoint *currentLocation = [PFGeoPoint geoPointWithLocation:newLocation];
PFUser *currentUser = [PFUser currentUser];
[currentUser setObject:currentLocation forKey:#"location"];
[currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
NSLog(#"Saved Users Location");
}
}];
Now i'd like to retrieve the users nearby via PFQuery like so:
- (NSArray *)findUsersNearby:(CLLocation *)location
{
PFGeoPoint *currentLocation = [PFGeoPoint geoPointWithLocation:location];
PFQuery *locationQuery = [PFQuery queryWithClassName:#"User"];
[locationQuery whereKey:#"location" nearGeoPoint:currentLocation withinKilometers:1.0];
locationQuery.limit = 15;
NSArray *nearbyUsers = [locationQuery findObjects];
return nearbyUsers;
}
Unfortunately it won't work. My array seems to have no entries. Can somebody clear things up for me, how the use the query the right way ?
Cheers, David
(also posted at: https://www.parse.com/questions/pfquery-to-retrieve-users-nearby)
First a quick comment
The code for creating a geo point is a "long running process" you will probably see this appearing in the console as you are running it on the main thread. This means the app is blocked (frozen) until the geo point is returned.
You would be better off using the code...
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
// Now use the geopoint
}];
This is the same for the findObjects query. You should be using...
[locationQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// use the objects
}];
Actual answer
I imagine this is a read access issue. As you are accessing the User table which, by default, has no public read access.
Are you setting the default read access in the app delegate something like this...
PFACL *defaultACL = [PFACL ACL];
[defaultACL setPublicReadAccess:YES];
[PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES];
Also, maybe try loosening off the constraints. 1km is a very small radius to be checking.
Ah, something else I just spotted. [PFQuery queryWithClassName:#"User"]; is using the wrong class name.
It should be #"_User".
However, a better solution would be to use the class to generate the query...
PFQuery *userQuery = [PFUser query];
When you subclass the PFObject class properly it has this method which will generate the correct query for you.