How would I go about displaying a list of all users in the parse.com database in a tableview and then when they click on each table, display that particular user's information.
All I know is that in order to query the users I must use:
PFQuery *query = [PFUser query];
Thank you in advance and any help is much appreciated.
You're really asking a specific question about Parse queries, but it seems like you don't understand queries in general, so just start with a general query against Parse:
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// Something went wrong
} else {
// objects is an array of PFObject containing your results
}
}];
As you figured out, you can also do a user query by making that first line:
PFQuery *query = [PFUser query];
The rest is the same.
Lyndsey Scott is right, this is basic stuff in the docs. I'm posting this here because of the one "gotcha" which is the class name (_User instead of User) if you use the first method.
What you generally will do is call [myTableView reloadData] inside the success block since you now have an array of users. In your didSelectCellAtIndexPath: perform a seque with a new viewcontroller, and in your prepareForSegue method, pass the user object to your pushed view controller so it knows what user to show.
I assume you know how to use table view. So I'll implement it this way:
1.create a property
#property (nonatomic) NSArray *users;
2.In viewDidAppear execute the query:
PFQuery *query = [PFUser query];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.users = objects;
[self.tableView reloadData];
}
}];
3.You need to implement table view code, the most important is cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"userCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
PFUser *user = self.users[indexPath.row];
cell.textLabel.text = user.username;
return cell;
}
Beside that you also need all of the required table view's data source code to tell it the number of cell. If you'll need some help with that just tell.
Related
I have a table called "UserSnapshot" on Parse and of course you get the objectID's as you populate the table.
However, when I query the table for an object from my app I wont have the object ID's but I will have their "UserCode". I have been playing with something like this.
PFQuery *userProfile = [PFQuery queryWithClassName:#"UserSnapshot"];
[userProfile whereKey:#"Code" equalTo:_Code];
[userProfile getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!object) {
// Did not find PFObject
// not executed
} else {
// Found PFObject
// also not executed....huh?
}
}];
But nothing happens. Neither the if or the else is entered. Am I missing something?
Thanks
Does anything print out in the log/console? It's possible that you didn't set your keys properly when initializing Parse in your App Delegate.
PFQuery *userProfile = [PFQuery queryWithClassName:#"UserSnapshot"];
[userProfile whereKey:#"Code" equalTo:_Code];
PFObject *object = [userProfile getFirstObject];
Works!
so in my app, I want to implement user profiles by clicking on a UIButton, I have all the functionality done.
I first added the functionality when the indexPath.section is selected the user information is shown, so then I wanted to do the same thing through a button.
heres my code in -(void)didSelectRow
PFObject *object = [self.objects objectAtIndex:selectedRow];
PFUser *user = [object objectForKey:#"userTookPhoto"];
self.userInfo = user;
self.userInfo is a property PFUser in the .h file
Then in my PrepareSegue I have this :
else if ([segue.identifier isEqualToString:#"homeToProfile2"])
{
transfer.userInformationObject = self.userInfo;
}
I run the app, and i tap on the button to push segue and the app crashes saying that self.userInfo is NULL.
When I NSlog it in didSelectRow, it has the information correct with all the user details,
when I NSlog it in the prepareSegue it crashes as it says it is NULL.
If you want to access PFObjects objects within a PFObject, you need to include within your PFQuery the includeKey: method and pass in the field that the PFObject is...
So if your accessing a PFUser object within a PFObject whose classname is 'Message', you create the query like so...
PFQuery *query = [PFQuery queryWithClassname:#"Message"];
[query whereKey:#"toUser" equalTo:[PFUser currentUser]];
[query includeKey:#"toUser"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
for (PFObject *obj in objects) {
NSLog(#"%#", [obj objectForKey:#"toUser"]);
}
}];
The log statement will return the PFUser object.
Heres a link to an explanation of your problem on Parse Blog
I'm a new user of Parse.com. I have a question about joining classes. Easy with SQL, but I don't find the solution with Parse. Thanks for your help !
I have 2 classes : ProductDatabase and MachineDatabase.
ProductDatabase contains 4 columns : serialNumber, name, description, price.
MachineDatabase contains 3 columns : serialNumber, date, idMachine.
I want to display the following datas : name, description, price, date.
- (PFQuery *)queryForTable
{
PFQuery *productQuery = [PFQuery queryWithClassName:#"ProductDatabase"];
PFQuery *query = [PFQuery queryWithClassName:#"MachineDatabase"];
[query whereKey:#"serialNumber" matchesKey:#"serialNumber" inQuery:productQuery];
[query includeKey:#"name"];
[query includeKey:#"description"];
[query includeKey:#"price"];
[query orderByDescending:#"date"];
return query;
}
// In my (UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
cell.nameLabel.text = [object objectForKey:#"name"];
cell.textLabel.text = [object objectForKey:#"description"];
cell.priceLabel.text = [object objectForKey:#"price"];
NSDate *productDate = [object objectForKey:#"date"];
NSString * dateString = [self timeSincePublished:productDate];
cell.dateLabel.text = dateString;
This code displays the date, but not the name, description, price.
Do you know how I can join these two classes ?
Thanks !
You are thinking it in an RDBS way and it is not ideal for a tool like Parse. You might need a little restructuring of your schema. Ideally, you should try using the Pointers in Parse (assuming your's is a one to many or one to one relation).
So your class structure will be like:
ProductDatabase: objectId, serialNumber, name, description, price, machine
MachineDatabase: objectId, serialNumber, date, idMachine
If serialNumber is not specific to Product and Machine, you can keep it in any one class. The field 'machine' in ProductDatabase should be an object pointer to the particular row in MachineDatabase.
PFQuery *query = [PFQuery queryWithClassName:#"ProductDatabase"];
[query includeKey:#"machine"];
includeKey keyword will expand the relational field machine and add the fields for the respective row in MachineDatabase into the result.
[query findObjectsInBackgroundWithBlock:^(NSArray *products, NSError *error) {
for (PFObject *product in products) {
// Machine row will be an object inside the retrieved product row.
PFObject *machine = product[#"machine"];
NSLog(#"retrieved related machine: %#", machine);
}
}];
I'm currently working on a PFQueryTableView and trying to get it to populate with data from an array that's pulled from ViewDidLoad. UPDATE: I've moved the function to an NSObject and implemented a singleton to be used across multiple classes in an effort to silo the operation away from the view controller. Below is the updated code:
+ (NSArray *)savedTankArray
{
PFUser *userName = [PFUser currentUser];
NSString *userNameString = [userName objectForKey:#"username"];
PFQuery *query = [[PFQuery alloc] initWithClassName:#"SavedTanks"];
[query whereKey:#"userName" equalTo:userNameString];
[query setValue:#"SavedTanks" forKeyPath:#"parseClassName"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
// The find succeeded.
NSLog(#"Successfully retrieved %lu Tanks.", objects.count);
// Do something with the found objects
for (PFObject *object in objects)
{
NSString *tankNameString = [[NSString alloc] init];
NSString *tankCapacityString = [[NSString alloc] init];
tankNameString = [object valueForKey:#"tankName"];
tankCapacityString = [object valueForKey:#"tankCapacity"];
NSLog(#"%#", tankNameString);
NSLog(#"%#", tankCapacityString);
_savedTankArray = [objects objectAtIndex:0];
}
}
else
{
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
NSLog(#"TANK NAME ARRAY: %#", _savedTankArray);
return [_savedTankArray savedTankObjects];
}
While the NSLogs inside of the function work just fine, my problem is a bit expanded now, and I feel as though I'm missing something really simple here.
By the time I get to #"TANK NAME ARRAY: %#"... obviously it's returning null because its outside of the portion that handles the query. This doesn't help me much if I'm trying to bring the data in through another class.
I've tried so much over the past few days and I can't imagine I'm missing something terribly complex. I'm sorry for re-opening this but I can't wrap my head around it at this time.
Any ideas on how I could handle this? I appreciate the help as always.
There may be other trouble, but for sure this line:
tableData = [NSArray arrayWithObjects:objects, nil];
is a mistake. This will create a single-element array whose first element is the array of results. I think you can fix and simplify as:
tableData = objects;
For your question on how to proceed, I think you can carry on in this class the way one would in any table view controller. Answer the table datasource methods by referring to tableData (i.e. it's count for numberOfRowsInSection:, and tableData[indexPath.row] to configure a cellForRowAtIndexPath:, and so on).
New answer for the edited new question:
It appears that the mixup is with calling the asynch service. I'll give two kinds of advice here. First, the simplest possible table-containing view controller that gets its data from an asynch service, and second, a little class that wraps the parse asynch service. First the VC:
// in a vc with a table view .m
#interface MyViewController ()
#property(weak,nonatomic) IBOutlet UITableView *tableView;
#property(strong,nonatomic) NSArray *array; // this class keeps the array
#end
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[ClassThatHandlesMyQuery doQuery:^(NSArray *results) {
self.array = results;
[self.tableView reloadData];
}];
}
See how the query class method in the other class takes a block parameter? This is required because the query happens asynchronously.
// do the normal table view stuff
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.array.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
PFObject *pfObject = self.array[indexPath.row];
cell.textLabel.text = [pfObject valueForKey:#"someStringProperty"];
return cell;
}
That should be pretty much all you need in the vc. Now let's look at your query method. It makes three mistakes: (a) No block parameter to let the caller get the asynch result, (b) it mishandles the array in the query completion block, (c) at the end of the method, it wrongly supposes that a variable _savedTankArray is initialized, in the block. That code appears below the block, but it actually runs before the block runs.\
Let's fix all three problems. First declare a public method:
// ClassThatHandlesMyQuery.h
+ (void) doQuery:(void (^)(NSArray *))completion;
See how it takes a block as param? Now implement:
// ClassThatHandlesMyQuery.m
+ (void) doQuery:(void (^)(NSArray *))completion {
// your query code. let's assume this is fine
PFUser *userName = [PFUser currentUser];
NSString *userNameString = [userName objectForKey:#"username"];
PFQuery *query = [[PFQuery alloc] initWithClassName:#"SavedTanks"];
[query whereKey:#"userName" equalTo:userNameString];
[query setValue:#"SavedTanks" forKeyPath:#"parseClassName"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// the job is MUCH simpler here than your code supposed.
// log the result for fun
NSLog(#"did we get objects? %#", objects);
// hand it back to the caller
// notice there's no array kept in this class. it's not needed
// and it would be awkward to do it at the class (not instance) level
completion(objects);
} else {
NSLog(#"bad news from parse: %#", error);
completion(nil);
}
}
// this is important
NSLog(#"hi mom!");
// watch your log output. 'hi mom' will appear before either message
// from the block. why is that? because that block runs later
// after the network request completes. but the hi mom NSLog runs
// just before the network request starts. this is why it's wrong to expect
// any variable set in the block to be initialized here
}
Believe it or not, that's it. You should be able to write exactly the mini view controller class and the mini query classes as described here, and see data from parse in a UITableView. I suggest you build something just like this (exactly like this) first just to get going
I think I'm overcomplicating this scenario. I'm working through a join table in a many to many situation.
If I find a join, I want to delete it, if I don't find one, I want to add it.
Adding a new join works fine. I can't figure how to delete ..
This is my code. If anyone sees anything wrong - or a better way to do this, please advise.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%s", __FUNCTION__);
//Create query for all current user objects
PFQuery *query = [PFQuery queryWithClassName:#"DDPeopleJoin"];
[query whereKey:#"parseUser" equalTo:[PFUser currentUser]];
[query whereKey:#"serviceKey" equalTo:[currentService valueForKey:#"serviceKey"]];
[query whereKey:#"personKey" equalTo:[currentPerson valueForKey:#"personKey"]];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
// Run the query - if there is an object delete it otherwise, go to JoinPeople
allDeadPeople = [NSMutableArray new];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if (!objects) {
NSLog(#"New Person");
[self joinPeople];
return;
}
NSLog(#"Found a match, erase them");
for (PFObject *object in objects) {
[object deleteInBackground];
}
[self refreshJoins:self];
}
}];
}
Firstly, instead of using enumerate block to delete each object, you can use the following:
[PFObject deleteAllInBackground:objects];
Secondly, You might get trouble with [self refreshJoins:self coz it will run before all object can be deleted. You should put that in the -deleteInBackground:block to make sure [self refreshJoins:self]` works properly