I am new to using parse, and currently adding parse to an app that uses SOAP web services to replace them.
Now I come a little stuck, as due to having it done in MySQL and php I am trying to translate the logic from the tables and code to parse.
I have a function that I have written and it achieves exactly what I am after, however I just think its wrong and can be done better, for starters I am not calling findObjectsInBackground, which I know I need to.
I am trying to take this result and then reload a tableView.
NSMutableArray *activityfeed = [NSMutableArray new];
//get current user
PFUser *user = [PFUser currentUser];
if (user != nil) {
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
//get event that user is involved in
[query whereKey:#"invited" equalTo:user];
//get the events
NSArray *events = [query findObjects];
//loop over the events
for (PFObject *event in events) {
//owner is a pointer to the users class
PFUser *owner = event[#"owner"];
if (![user.objectId isEqualToString:owner.objectId]) {
//ignore the info for the logged in user for now
//invited is a relation so one event has many users
PFRelation *relation = [event relationForKey:#"invited"];
PFQuery *query = [relation query];
[query orderByDescending:#"dateinvited"];
[query addDescendingOrder:#"dateaccepted"];
//get the friends that are involved with the event
NSArray *friends = [query findObjects];
for (PFUser *friend in friends) {//Perform logic checks here and then add to activityfeed}
}
}
}
return activityfeed;
So my logic above gets the current user, then get all the events that user is involved with, then get all the other people involved with that event and then work out what to display.
Is there a more efficient way of doing the above?
Usually we use whereKey:matchesQuery in cases like this. Take some research on this.
This question may help you find out some solution.
Related
Ok so let's say I have a post of an event, and users can click a button to notify that they are attending this event. As of now I have a class called Activity in which I save the current user and the event to this class, so theres 2 columns. If I want to query all users who are attending an event, am I headed in the right direction to do this, or am I doing it complentely wrong?
So far I have:
-(PFQuery*)queryForTable {
PFQuery *activityQuery = [PFQuery queryWithClassName:#"Activity"];
[activityQuery whereKey:#"event" equalTo:self.event];
[activityQuery includeKey:#"going"];
return activityQuery;
}
cellForRowAtIndex:
UILabel *title = (UILabel*) [cell viewWithTag:1];
title.text = [object objectForKey:#"going.username"];
You can actually see what you have been done in the Parse dashboard. That's also their purpose to develop a data browser like this. It's way more convenient.
For your case, you just need to check whether the type is Pointer. Try to click on that if so in the dashboard. It will direct you to the target object.
Would suggest you to read this article first, it's about the relation:
https://parse.com/docs/relations_guide
Then, you should go check the iOS SDK tutorial:
includeKey is definitely what you need to use.
Here is the sample from Parse:
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
// Retrieve the most recent ones
[query orderByDescending:#"createdAt"];
// Only retrieve the last ten
query.limit = 10;
// Include the post data with each comment
[query includeKey:#"post"];
[query findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {
// Comments now contains the last ten comments, and the "post" field
// has been populated. For example:
for (PFObject *comment in comments) {
// This does not require a network access.
PFObject *post = comment[#"post"];
NSLog(#"retrieved related post: %#", post);
}
}];
Your code looks right on so far. Then to retrieve your Activity class values, you can use:
PFQuery *activityQuery = [PFQuery queryWithClassName:#"Activity"];
// Set contraints here, example:
[activityQuery setLimit:100];
[query findObjectsInBackgroundWithBlock:^(NSArray *array, NSError *error) {
if (!error) {
// Success, do something with your objects.
}
}];
I am using the Parse.com iOS SDK, and I don't know what method I need to call to make sure that the PFUser currentUser contains the data for each pointer contained in it. It is probably very simple, but, as a beginner with this platform, I cannot find a solution.
Just to clarify, I am trying to get the username item in the following. It looks like it is always null, even though I am calling [[PFUser currentUser] fetchIfNeeded] before proceeding:
[[[PFUser currentUser] objectForKey:#"partner"] objectForKey:#"username"];
Thank you,
Andrea
You can do it in a single query by using includeKey:
PFQuery *query = [PFUser query];
[query includeKey:#"partner"];
[query includeKey:#"anotherPointerColumnName"];
[query getObjectInBackgroundWithId:[[PFUser currentUser] objectId]
block:^(PFObject *populatedUser, NSError *error) {
PFObject *partner = populatedUser[#"partner"];
PFObject *another = populatedUser[#"anotherPointerColumnName"];
}];
You can also use deeper levels, so if partner contained a pointer in it you needed called deeperPointerColumnName:
[query includeKey:#"partner.deeperPointerColumnName"];
Then in your completion block you can read it after you get the partner:
PFObject *partner = populatedUser[#"partner"];
PFObject *deeper = partner[#"deeperPointerColumnName"];
Well, I found a workaround by myself. It looks like the solution is to call the fetch method on the pointer before proceeding:
[[[PFUser currentUser] objectForKey:#"partner"] fetch];
If there is a cleaner and swifter way of doing it (automatically for instance) I would really appreciate any advice.
I will try to explain myself as best as I can.
My app has a login & sign Up using the Parse API .
after signing or logging the User see a page with a Button and a Text Field which he can write Numbers in it ,
let's say he write , for example: 1234.
the button job is to save the data from the text field
Now i'm sure my Button method isn't the best way, but it's work :-)
NSString *whoIsTheUser = [PFUser currentUser];
NSString *phoneNumbers = phoneNumberField.text ;
NSString *fullData = [NSString stringWithFormat:#" %# , %#", whoIsTheUser, phoneNumbers];
NSLog(#" %#, %# ", whoIsTheUser, phoneNumbers);
PFObject *addValues= [PFObject objectWithClassName:#"phoneNumber"];
[addValues setObject: fullData forKey:#"numbers"];
[addValues saveInBackground];
So now what i'm having is the data saved to parse site under class of Numbers (forKey:#"numbers")
and it's look like this :
<PFUser:hqExhaYaIN:(null)> {email = "buffo#yahoo.com";username = buffo;} , 1234
what I'm trying to do is to add another button which going to check if theres another user
who wrote the same numbers and then send an alert or do something .
I have tried to read about the PFQuery but didn't find any solution.
is it even possible to run Query in the classes name ? or maybe theres another way to do it ( within the parse site ) ?
thank you so much for any suggestion !
It looks like you need to split up fullData in your parse objects and do something like this
PFQuery* numQuery = [PFQuery queryWithClassName:#"phoneNumber"];
[numQuery whereKey:#"numbers" equalTo:phoneNumbers];
[numQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if(!error)
{
for(PFObject *numObject in objects)
{
//Do what you need to do here
}
}
else
{
//handle error
}
}
EDIT - this means you would save your PFObjects like this
PFObject *addValues= [PFObject objectWithClassName:#"phoneNumber"];
[addValues setObject: phoneNumbers forKey:#"numbers"];
[addValues setObject: whoIsTheUser forKey:#"theUser"];
[addValues saveInBackground];
i hope it's going to help someone like it's helps me , if you want to query for info without the user info use those lines
PFQuery* numQuery = [PFQuery queryWithClassName:#"phoneNumber"];
[numQuery whereKey:#"numbers" equalTo:phoneNumbers];
[numQuery whereKey:#"theUser" notEqualTo:[PFUser currentUser]];
[numQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
all I had to do is add notEqualTo and CurrentUser :-)
Perhaps this is not the right place for this, but I am sure that many users here are familiar with Parse framework for iOS. Basically I am having issues with a query, all I want to do is check if a username already exists (they do so at login) except I need to do it in order to set up a relationship between the current user and another user. Currently my method is:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:username.text];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"query returned with result");
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
}
else {
NSLog(#"Nope");
}
}];
username is just a string containing the word entered in plain text from UITextField. But no matter what I put in, the query seems to go through and I get a message of success. I even tried iterating through the objects as shown in the for loop and I get nothing logged. What is going on here?
EDIT
Just for clarification, username is simply taken from a IBOutlet UITextField *username from the view controller which takes in the username from input. I have tested to make sure that it is being taken correctly. If I enter "foo", I can log the username.text and it will be "foo", however I have no registered users named "foo" so I do not understand why the query is returning without error.
You will get message of success even there is no object found.
(Try log objects.count)
If so, you should check your spelling on Key Value or Query Value.
They are case-sensitive.
so i have a bunch of objects on parse.com. class name is "MainInfo" with geo points in a column named geoPoint.
I am getting the users location by adding the following to my .h file:
#property (nonatomic, strong) PFGeoPoint *userLocation;
Then adding the following to viewDidLoad:
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error) {
if (!error) {
self.userLocation = geoPoint;
[self loadObjects];
}
}];
and performing the rolling queryForTable:
- (PFQuery *)queryForTable
{
// User's location
PFGeoPoint *userGeoPoint = self.userLocation;
// Create a query for places
PFQuery *query = [PFQuery queryWithClassName:#"MainInfo"];
// Interested in locations near user.
[query whereKey:#"geoPoint" nearGeoPoint:userGeoPoint];
// Limit what could be a lot of points.
query.limit = 10;
// Final list of objects
_placesObjects = [query findObjects];
return query;
}
Xcode gives me the error *** setObjectForKey: object cannot be nil (key: $nearSphere)
I've got no idea what I am doing wrong, as far as I can see it should work.
I worked along with the parse documentation to get me this far. Here is a link
When you make the geoPointForCurrentLocationInBackground call it has a completion block. This completion block marks the point at which you have the required information to populate the table view (or you know that there is an error and you should do something else). So, you shouldn't display / load query data into the table view until the completion block is called. Other wise you don't have the required information to complete the query.
You could display an activity indicator while you're waiting. Or, it might be better to get the userLocation before even showing this view so you always have the information for the query when you get here.
The error arises because you're passing a nil value to whereKey:nearGeoPoint: as self.userLocation is unlikely to be set the first time the view loads. You will want to do two things:
In your queryForTable method, check if self.userLocation is nil. If it is, return nil. This acts as a no-op and the table won't show any data just yet.
- (PFQuery *)queryForTable
{
if (!self.userLocation) {
return nil;
}
// User's location
PFGeoPoint *userGeoPoint = self.userLocation;
// Create a query for places
PFQuery *query = [PFQuery queryWithClassName:#"MainInfo"];
// Interested in locations near user.
[query whereKey:#"geoPoint" nearGeoPoint:userGeoPoint];
// Limit what could be a lot of points.
query.limit = 10;
// Final list of objects
_placesObjects = [query findObjects];
return query;
}
In your geoPointForCurrentLocationInBackground: completion block, once the self.userLocation value is set, you will want to call [self loadObjects]. This will tell the PFQueryTableViewController to run your query again, and this time around self.userLocation will not be nil, allowing you to construct your original query. Fortunately, you've already performed this step, but I've included it here in case anyone else has the same question.