When I'm changing for example a bool in the Parse backend I can not retrieve this value in my iOS app. Please see the following screenshot where the first bool of the user object was changed manually and the second was changed from the app.
The first one does not work whereas the one (that was changed from the app) does work.
I'm getting the value with the following piece of code:
[[PFUser currentUser] objectForKey:#"is_pro"]
In the first case the return object is always nil (where I've changed the bool manually).
After you've made changes to your user's Parse table, call
[[PFUser currentUser] fetch]; //synchronous
or
[[PFUser currentUser] fetchInBackground]; //asynchronous
or
[[PFUser currentUser] fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) { //asynchronous with completion block
before
[[PFUser currentUser] objectForKey:#"is_pro"]
to get an updated copy of your [PFUser currentUser] object.
Note: refresh and refreshInBackgroundWithBlock: are now deprecated and have been replaced with fetch and fetchInBackgroundWithBlock:
Update:
As Ryan Kreager pointed out in the comments, it's probably better/more efficient to use fetchIfNeeded, fetchIfNeededInBackground, or fetchIfNeededInBackgroundWithBlock: "since they will use the local cache when available."
Examples in Swift:
First and foremost, please have a look at isDataAvailable and isDirty.
var user = PFUser.currentUser()!
// Gets whether the PFObject has been fetched.
// isDataAvailable: true if the PFObject is new or has been fetched or refreshed, otherwise false.
user.isDataAvailable()
// Gets whether any key-value pair in this object (or its children) has been added/updated/removed and not saved yet.
// Returns whether this object has been altered and not saved yet.
user.isDirty()
Also it is probably better/more efficient to use fetchIfNeeded, fetchIfNeededInBackground, or fetchIfNeededInBackgroundWithBlock: "since they will use the local cache when available."
// Synchronously fetches the PFObject data from the server if isDataAvailable is false.
user.fetchIfNeeded()
// Fetches the PFObject data asynchronously if isDataAvailable is false, then sets it as a result for the task.
user.fetchIfNeededInBackground()
// Fetches the PFObject data asynchronously if isDataAvailable is false, then calls the callback block.
user.fetchIfNeededInBackgroundWithBlock({
(object: PFObject?, error: NSError?) -> Void in
// Code here
})
Otherwise you can also use fetch, fetchInBackground, fetchInBackgroundWithBlock if you do not need to use the local cache.
// Synchronously fetches the PFObject with the current data from the server.
user.fetch()
// Fetches the PFObject asynchronously and sets it as a result for the task.
user.fetchInBackground()
// Fetches the PFObject asynchronously and executes the given callback block.
user.fetchInBackgroundWithBlock({
(object: PFObject?, error: NSError?) -> Void in
// Code here
})
When you have pointers you can not use fetch. this is the best way:
PFQuery *userQuery = [PFUser query];
[userQuery includeKey:#"addresses"];
[userQuery includeKey:#"cards"];
[userQuery whereKey:#"objectId" equalTo:[PFUser currentUser].objectId];
[userQuery getFirstObjectInBackgroundWithBlock:^(PFObject * _Nullable object, NSError * _Nullable error) {
}];
Related
I am testing out Parse localDatastore and am struggling with refreshing the local datastore after a new server PFQuery.
The PFQuery works fine and seems to pin the array to the local datastore just fine. When I change the contents of the array on the server, the server PFQuery pulls down the updated array, but the local datastore doesn't seem to update:
- (void)viewDidLoad {
[super viewDidLoad];
// Query Parse
PFQuery *query = [PFQuery queryWithClassName:#"contacts"];
NSArray *objects = [query findObjects];
[PFObject pinAllInBackground:objects block:^(BOOL succeeded, NSError *error) {
if(succeeded) {
NSLog(#"Successfully retrieved %lu records from Parse.", (unsigned long)objects.count);
} else if (error) {
NSLog(#"Error");
}
}];
}
and then a UIButton is used to log the contents of the local datastore to the console:
-(IBAction)showDatastore {
// Query the Local Datastore
PFQuery *query = [PFQuery queryWithClassName:#"contacts"];
[query fromLocalDatastore];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %lu contacts from Datastore.", (unsigned long)objects.count);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
In my sample the original array has 15 objects. Both the count's from each array initially are 15. I then remove an object from the server array and the initial PFQuery count is 14, but the local datastore count remains 15.
Parse's documentation states:
When an object is pinned, every time you update it by fetching or saving new data, the copy in the local datastore will be updated automatically.
But that doesn't seem to be the case... at least not with this recommended code. Is there something i'm missing?
It depends on how you are deleting the object. If you're using deleteEventually, then the deletion will propagate to the LDS
You can query from the local datastore using exactly the same kinds of queries you use over the network. The results will include every object that matches the query that's been pinned to your device. The query even takes into account any changes you've made to the object that haven't yet been saved to the cloud. For example, if you call deleteEventually, on an object, it will no longer be returned from these queries.
But any other method requires explicit unpinning if you want it to work.
deleteEventually is the prefered method I believe.
I have a view controller with inside table and I want to fill her with an array saved on Parse. To download the data I use this code:
PFQuery *query = [PFQuery queryWithClassName:#"myClass"];
[query whereKey:#"X" equalTo:#"Y"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(error==nil){
myArray=[object objectForKey:#"Z"];
NSLog(#"%#",myArray);
}
}];
}
Now I display it inside myarray the data on parse. But if I use arrays to populate the table it is always me empty. I used NSLog and I saw that outside of the method [query getFirstObjectInBackgroundWithBlock: ^ (PFObject * object, NSError * error) my array is always empty.
How can help me?
Fetching data from a remote database takes a little time. The parse functions that take block params run asynchronously. See the comments within your slightly modified code...
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(error==nil){
// this appears first in the file, but runs later
// after the request is finished
myArray=[object objectForKey:#"Z"];
NSLog(#"%#",myArray);
// tell our view that data is ready
[self.tableView reloadData];
}
}];
// this appears second in the file, but runs right away, right
// when the request is started
// while execution is here, the request isn't done yet
// we would expect myArray to be uninitialized
Be sure, in your datasource methods e.g. numberOfRows to answer myArray.count. And use the data in the array myArray[indexPath.row] when building the table view cell.
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 am using Cloud Code in order to add two users to a chatRoom, but my app crashes when I call the Cloud Code function.
In my iOS viewController, a user clicks a button which calls the following method:
-(void)createChatRoom
{
PFQuery *queryForChatRoom = [PFQuery queryWithClassName:#"ChatRoom"];
[queryForChatRoom whereKey:#"user1" equalTo:[PFUser currentUser]];
[queryForChatRoom whereKey:#"user2" equalTo:self.giveItem.itemGiver];
PFQuery *queryForChatRoomInverse = [PFQuery queryWithClassName:#"ChatRoom"];
[queryForChatRoomInverse whereKey:#"user1" equalTo:self.giveItem.itemGiver];
[queryForChatRoomInverse whereKey:#"user2" equalTo:[PFUser currentUser]];
PFQuery *combinedChatRoomQuery = [PFQuery orQueryWithSubqueries:#[queryForChatRoom, queryForChatRoomInverse]];
[combinedChatRoomQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if ([objects count] == 0){
[PFCloud callFunctionInBackground:#"addUsersToChatRoom" withParameters:#{#"user1" : [PFUser currentUser]} block:^(id object, NSError *error) {
[self performSegueWithIdentifier:#"itemToChatSegue" sender:nil];
}];
};
}];
}
And here is the JavaScript function stored in my Parse Cloud.
Parse.Cloud.define("addUsersToChatRoom", function(request, response){
response.success("Jared this method call works, no implement it");
console.log("this is being logged to the console");
var user = response.body.user1;
console.log(user);
});
When I press the button to call this method, my app crashes and I get an output of errors that looks like this:
[PFInternalUtils encodeObject:allowUnsaved:allowObjects:]
[PFInternalUtils encodeObject:allowUnsaved:allowObjects:]
[PFCloud callFUnctionAsync:withParameters:]
[PFCloud callFunctionInBackground:withParameters:block:]
__36-[MyViewController createChatRoom]_block_invoke
__40-[PFTask thenCallBackOnMainThreadAsync:]_block_invoke_2
How can I move forward from this error?
Once the error is resolved, where will I be able to read the Cloud Code output? When I use curl to hit my app's Cloud Code (https://parse.com/docs/cloud_code_guide), I get the given "Hello world!" output, but in this case I'm calling the JS function from my objective-c code and I'm not sure where I can monitor my console output. (Ultimately, I will replace the console output that is currently in my JS function with the actual functionality I am looking for.)
How can I learn to make sense of this error-message output?
PFUser currentUser answers an object, but you must pass a dictionary of serializable types -- like the user id or username -- to callFunctionInBackground:withParameters:. Also, the cloud function should invoke either response.success() or response.error().