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.
Related
I have two tables TrendingUsers and Follow. Functionality required is like fetch users from TrendingUsers table and offer to follow, provided fetched user is not from user's follow list. If user is already get followed then skip.
Follow table has columns follower and leader.
PFQuery *followTableQuery = [PFQuery queryWithClassName:#"Follow"];
[followTableQuery whereKey:#"follower" equalTo:[PFUser currentUser] ];
[followTableQuery whereKey:#"leader" equalTo:#"fetchedUserObject" ];
[followTableQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if (objects.count) {
//if following objects array will have single object
}
else
{
//not following to #"fetchedUserObject" user
}
}
}
];
This will confirm me that currentUser is following #"fetchedUserObject" user or not.
Now I want to integrate this to the TrendingUsers table query to fetch only such users that currentUser is not following.
You can simply use nested queries, the docs from Parse are usually a good starting point. Here is a sample code, from what I understood from your question, this should do the trick.
//This is our current user
PFUser *user = [PFUser currentUser];
//The first query, querying for all the follow objects from the current user
PFQuery *followingQuery = [PFQuery queryWithClassName:#"Follow"];
[followingQuery whereKey:#"follower" equalTo:user];
//Now we query for the actual trending users, but we do not want the query to return the users (who are in the #"leader" key) that have been found by the first query
PFQuery *trendingQuery = [PFQuery queryWithClassName:#"TrendingUsers"];
[trendingQuery whereKey:#"objectId" notEqualTo:user.objectId]; //don't return the current user
[trendingQuery whereKey:#"objectId" doesNotMatchKey:#"leader" inQuery:followingQuery]; //I'm supposing that #"leader" is containing the objectId of the specific user that is part of the follow object with the current user
[trendingQuery setLimit:1000];
[trendingQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
//...
}];
I may have not understood your data structure completely, so you may have to exchange one or more keys in the above code, but basically, this is how you would do this.
Working on an App that allow user to see what local people have posted (businesses, schools, or just people). I am able to post the information and save it to parse. I am also able to download user data near current location, but it issue is that my helper method does not do it right away when the app first launches.
I have tried to call the helper method below in the viewDidLoad,viewWillAppear and viewDidAppear and no luck. The postArray is always null when the user initially opens the application and goes to the map. If I move to another screen and come back to the mapVC the data is there...(I NSlog the postArray) and all the posts near the current location print out. I would like to have this data initially right when the map presents the user's location.
Questions.
Where should I call my helper method? When the app launches I want to
have the data so i can display it on the map.
Is there another method that I need to write?
Is there something wrong with the current method.
- (void)loadLocalPosts {
NSLog(#"Querying for Local Posts");
[activityIndicator startAnimating];
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError
*error) {
geopoint = geoPoint;
[query whereKey:#"location" nearGeoPoint:geoPoint];
[query setLimit:50];
[query addDescendingOrder:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
postArray = objects;
}];
}];
}
The problem here is that postArray is populated in a background thread and your application initializes faster than it can populate the array. This is common in asynchronous programming. The way to fix this, is by asking the map to refresh in the main thread.
- (void)loadLocalPosts {
NSLog(#"Querying for Local Posts");
[activityIndicator startAnimating];
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError
*error) {
geopoint = geoPoint;
[query whereKey:#"location" nearGeoPoint:geoPoint];
[query setLimit:50];
[query addDescendingOrder:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
postArray = objects;
dispatch_async(dispatch_get_main_queue(),^
{
// update view properties, refresh etc.
});
}];
}];
}
Note: All view related modifications should always happen in the main thread. Hence the dispatch_get_main_queue(). Now the main loop could however, be doing view related operations, hence a synchronous call will crash the application. Hence dispatch_async is used. This will add the block to the next run loop to be executed after the current one.
I am trying to update a chat object I have saved with Parse, and although it works sometimes, it's not consistent. If I clear the object out of data on the browser side, it will work a few times, but then I get the error:
Error: object not found for update (Code: 101, Version: 1.3.0)
Here is the code I am using, although I've tried many ways. This code is nearly identical to the Parse documentation.
PFObject *currentChatroom = _currentChatroom;
NSString *objID = currentChatroom.objectId;
PFQuery *query = [PFQuery queryWithClassName:#"Chats"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:objID block:^(PFObject *fetchedChat, NSError *error) {
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
fetchedChat[#"lastTextSent"] = lastTextWithUser;
fetchedChat[#"lastTextSentDate"] = date;
[fetchedChat saveInBackground];
}];
For good measure, Here is the Parse recommendation:
PFQuery *query = [PFQuery queryWithClassName:#"GameScore"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:#"xWMyZ4YEGZ" block:^(PFObject *gameScore, NSError *error) {
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
gameScore[#"cheatMode"] = #YES;
gameScore[#"score"] = #1338;
[gameScore saveInBackground];
}];
The Code works sometimes, so I know that's not the issue. I'm just not sure what is.
The code I used to fix this problem was allowing each user of the object (chatroom in this case) to have ACL Permissions to edit (writeAccess) the PFObject when it was first created. In order to do this, I used the code:
PFObject *newChatroom = [PFObject objectWithClassName:#"Chats"];
// Create ACL to allow both users to edit/update the chatroom
PFACL *multipleUserRights = [PFACL ACL];
// _currentFriend is one user in the chatroom
[multipleUserRights setReadAccess:YES forUser:_currentFriend];
[multipleUserRights setWriteAccess:YES forUser:_currentFriend];
// Give the current user permission as well
[multipleUserRights setReadAccess:YES forUser:[PFUser currentUser]];
[multipleUserRights setWriteAccess:YES forUser:[PFUser currentUser]];
newChatroom.ACL = multipleUserRights;
I found similar questions to this, and some had similar solutions, but not with the error 1.3.0, so I wouldn't consider it a duplicate.
My app needs to find the user's current location, which I have done. Then it needs to find other users that are near the current user's location. I am using Parse for this. So far this is what I have to get the user's current location and it is working so far.I don't understand how to find the other user near the current user's location though. Can someone please help me out? I'd really appreciate it.
if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
[self updateUserInformation];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
if (!error) {
NSLog(#"User is currently at %f, %f", geoPoint.latitude, geoPoint.longitude);
[[PFUser currentUser] setObject:geoPoint forKey:#"currentLocation"];
[[PFUser currentUser] saveInBackground];
} else {
NSLog(#"%#", error);
return;
}
}];
From the parse docs:
// User's location
PFGeoPoint *userGeoPoint = userObject[#"currentLocation"];
// Create a query for places
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
// Interested in locations near user.
[query whereKey:#"location" nearGeoPoint:userGeoPoint];
// Limit what could be a lot of points.
query.limit = 10;
// Final list of objects
placesObjects = [query findObjects];
placesObjects will be an array of objects ordered by distance (nearest to farthest) from userGeoPoint.
https://www.parse.com/docs/ios_guide#geo-query/iOS
I have several Classes one of which one is User and another one is TestObject. If I query User (which I learned by trial & error that it should queried as _User) I get the correct record count, but if I query TestObject I get 0. This happens for some Classes but not for all. Why is that?
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error: %# %#", error, [error userInfo]);
} else {
NSLog(#"Successfully retrieved %d scores.",objects.count);
}
}];
This returns:
2012-10-19 13:55:03.239
TableViewParseDotCom[5497:10103]
Successfully retrieved 24 scores.
But if I change the line, to:
PFQuery *query = [PFQuery
queryWithClassName:#"TestObject"];
I get 0 count, but I know I have 45 records. Why?
The most common reason for this is you've queried for objects which you don't have access to. Double check that if these objects have an ACL you are logged in as the same user.
Along with checking the ACL's for the objects you're querying, you should also take a look at the Settings in the Parse dashboard. If you don't want to force users to log in ensure that "Allow Anonymous Users" is set on On.
Also, in the data browser for the TestObject object, click on the "More" button and then select "Permissions" from the dropdown. Ensure your settings are correct for "Find" and "Get" - set to public with no roles/users to start with to help your debugging. That should ensure that you can query your TestObject objects.
Don't forget to set the read permissions. These can be programatically set as below:
PFACL * defaultACL = [PFACL ACL];
[defaultACL setPublicReadAccess:YES];
[PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES];
I just ran two queries, one on my users and one on another class (imageClass) and they both returned fine.
User query:
// Remember for users we can run a user query instead of needing to specify the class
PFQuery * userQuery = [PFUser query];
[userQuery whereKey:#"username" equalTo:currentUser.username];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError *error) {
}];
Other class query:
PFQuery * imageQuery = [PFQuery queryWithClassName:#"bImageClass"];
[imageQuery findObjectsInBackgroundWithBlock:^(NSArray * objects, NSError *error) {
}];
Here is a picture of my classes in Parse.
So I would make sure you have the public access set correctly (in my project this is set just before a query runs) as the code you are using for the queries looks fine.