Querying a column in Parse - ios

I have an iOS app that allows users to save GeoPoints to a Parse.com database table. Its a Google Maps app that lets users submit Lat/Long coordinates that will be displayed on the map once the Points have been approved by an admin type person.
I created a table called Point on Parse.com to save this information and the table has the following fields in addition the default ones that are created for you:
approved (String)
point (GeoPoints)
When a new row in the table is created (new Point is submitted by a user), "approved" is always set to "0" and "point" will contain the Lat/Long of a point that has been uploaded.
Once the "point" has been approved by an admin, "approved" is set to "1".
I query the table to display all the GeoPoints that have been approved (set to "1").
I can get all the rows that has been set to "1" but I dont know how to access the "point" field. I need to get the "point" for each row to display it to a map.
Here is what I have to query the table for all Points where "approved" is set to "1"
// Query the Database to get Points
PFQuery *query = [PFQuery queryWithClassName:#"Point"];
[query whereKey:#"approved" equalTo:#"0"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
// The find was successful
NSLog(#"Successfully Retrieved %d Number of Points", objects.count);
// Display the found Points as Markers
for (PFObject *object in objects)
{
NSLog(#"%#", object.objectId);
// Tried to do this but does not work
NSLog(#"%#", object.point);
}
}
else
{
// Show errors
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
I tried the following once I had an array of the objects but this does not work.
// Tried to do this but does not work
NSLog(#"%#", object.point);
Can anyone please help?

I think what you want is:
PFGeoPoint *point = object[#"point"];
Refer to the PFGeoPoint class reference for more information.

You can also go with subclassing for ease of use. Refer to iOS Guide Subclassing. Don't forget to register the subclass in your AppDelegate.
//Point.h
#import "Parse/Parse.h"
#interface Point : PFObject <PFSubclassing>
#property (nonatomic) BOOL approved;
#property (nonatomic, strong) PFGeoPoint *geoPoint;
+ (void)findApprovedPointsWithBlock:(PFArrayResultBlock)resultBlock;
#end
//Point.m
#import "Point.h"
#import <Parse/PFObject+Subclass.h>
#implementation Point
#dynamic approved;
#dynamic geoPoint;
+ (NSString *)parseClassName {
return #"Point";
}
+ (void)findApprovedPointsWithBlock:(PFArrayResultBlock)resultBlock {
PFQuery *query = [PFQuery queryWithClassName:#"Point"];
[query whereKey:#"approved" equalTo:#YES];
[query findObjectsInBackgroundWithBlock:^(NSArray *points, NSError *error) {
resultBlock(points, error);
}];
}
#end
// Somewhere in your code
[Point findApprovedPointsWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (Point *object in objects) {
NSLog(#"%#", object.objectId);
NSLog(#"%#", object.geoPoint);
}
}
else {
}
}];

Related

Parse.com returns empty objects

I have an NSArray called "malls" that contains a large number of NSDictionaries (each a specific mall) that I uploaded to Parse.com. I want my users to be able to access this information to create map annotations.
I've tried to do this in 2 different ways:
I tried uploading the entire array as a property of a single object:
this is the upload:
in the dataBank.h file:
#property (strong, nonatomic) NSMutableArray* malls;
in the .m file
PFObject *obj = [PFObject objectWithClassName:#"malls"];
obj[#"mallsData"] = self.malls;
[obj saveInBackground];
I try to get the data from parse:
-(NSMutableArray *)createAnnotationsFromParse
{
__block NSMutableArray* data = [[NSMutableArray alloc]init];
__block NSMutableArray* annots = [[NSMutableArray alloc]init];
PFQuery* query = [PFQuery queryWithClassName:#"malls"];
[query getObjectInBackgroundWithId:#"Eaib9yfTRe" block:^(PFObject *object, NSError *error) {
data = [object objectForKey:#"mallsData"];
annots = [self createAnnotations:data];
}];
return annots;
}
The problem is getObjectInBackground is asynchronous and always returns before getting the data from the server. I tried moving the "return annots" inside the code block but that gives the following error: "incompatible block pointer types".
I uploaded 5 "mall" objects to class "malls2". Each object has 2 properties- name and address:
for(int i = 0; i < 5; i++)
{
PFObject *mallsObj = [PFObject objectWithClassName:#"malls2"];
mallsObj[name] = [[self.malls objectAtIndex:i]objectForKey:name];
mallsObj[address] = [[self.malls objectAtIndex:i]objectForKey:address];
[mallsObj saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(succeeded)
NSLog(#"yay");
else
NSLog(#"%#", error.description);
}];
}
then I try to get it back:
-(NSMutableArray *)createAnnotationsFromParse
{
__block Annotation* anno = [[Annotation alloc]init];
__block NSMutableArray* annots = [[NSMutableArray alloc]init];
PFQuery* query = [PFQuery queryWithClassName:#"malls2"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(error)
NSLog(#"%#", error.description);
else
{
for(int i = 0; i < [objects count]; i++)
{
//createAnnotationWithTitle is a func in a different class that creates the annotation
anno = [anno createAnnotationWithTitle:[[objects objectAtIndex:i] objectForKey:name] andAddress:[[objects objectAtIndex:i]objectForKey:address]];
}
[annots addObject:anno];
}
}];
return annots;
}
I get 5 objects but they're all empty.
It's a basic misunderstanding about asynchronous methods with block parameters. The trick is to get out of the habit of thinking that code that appears later in a source file runs later. The assumption works in this function:
- (void)regularFunction {
// these NSLogs run top to bottom
NSLog(#"first");
NSLog(#"second");
NSLog(#"third");
}
This will generate logs: first, second, third. Top to bottom, but not in this one:
- (void)functionThatMakesAsynchCall {
// these NSLogs do not run top to bottom
NSLog(#"first");
[someObject doSomeAsynchThing:^{
NSLog(#"second");
}];
NSLog(#"third");
}
That function will generate logs - first, third, second. The "second" NSLog will run well after the "third" one.
So what should you do? Don't try to update the UI with results of a parse call until after it completes, like this:
// declared void because we can't return anything useful
- (void)doSomeParseThing {
// if you change the UI here, change it to say: "we're busy calling parse"
PFQuery* query = [PFQuery queryWithClassName:#"malls2"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if(!error) {
// change the UI here, say by setting the datasource to a UITableView
// equal to the objects block parameter
}
}];
// don't bother changing the UI here
// don't bother returning anything here
// we just started the request
}
But what if doSomeParseThing is really a model function, whose only job is to fetch from parse, not to know anything about UI? That's a very reasonable idea. To solve it, you need to build your model method the way parse built their's, with block parameter:
// in MyModel.m
// declared void because we can't return anything useful
+ (void)doSomeParseThing:(void (^)(NSArray *, NSError *))block {
PFQuery* query = [PFQuery queryWithClassName:#"malls2"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
block(objects, error);
}];
}
Now your view controller can call, leave the query work to your model and the UI work to the vc:
// change UI to 'busy' here
[MyModel doSomeParseThing:^(NSArray *objects, NSError *error) {
// update UI with objects or error here
}];
Figured it out. It looked like I was getting "empty objects" (can be seen here postimg.org/image/ot7ehn29b ) but once I tried to access data from the objects I saw there was no problem. Basiclly I was tricked by the PFObjects in the array showing "0 objects" and assumed it meant they came back from Parse.com empty. Here's how I checked, just for reference:
PFQuery *query = [PFQuery queryWithClassName:#"malls2"];
NSArray *array = [query findObjects];
NSLog(#"%#", [[array objectAtIndex:0] objectForKey:#"name"]; // I have a string property called "name" in my Parse object.

View Parse Data on WatchKit Table

I am trying to get my app working on WatchKit. I use Parse, and want to simply show the value of "Request" in my PFObject as a row in a WatchKit Table. I have the table and row set up and the row NSObject class simply has 1 label in it, where the "Request" field will populate. Here is what I have in my InterfaceController for the Watch.
#import "InterfaceController.h"
#import "TheTable.h"
#import <Parse/Parse.h>
#import "BlogView.h"
#interface InterfaceController()
#property (nonatomic, retain) PFObject *theObject;
#property (nonatomic, retain) NSMutableArray *rowTypesList;
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[Parse setApplicationId:#"MYID"
clientKey:#"MYKEY"];
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate
{
PFQuery *query = [PFQuery queryWithClassName:#"Prayers"];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSArray *array = [objects objectAtIndex:0];//Selects the first "object" from all the "objects"
array = [array valueForKey:#"Request"];//Makes a NSArray from the "pairs" values
_rowTypesList = [array mutableCopy];//Converts the array to a NSMutableArray
NSLog(#"%#", _rowTypesList);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}]; [self setupTable];
}
- (void)setupTable
{
NSLog(#"%#", _rowTypesList);
[_theActualTable setRowTypes:_rowTypesList];
for (NSInteger i = 0;_theActualTable.numberOfRows; i++)
{
NSObject *row = [_theActualTable rowControllerAtIndex:i];
TheTable *importantRow = (TheTable *) row;
[importantRow.textRowLabel setText:???]; //THIS IS PROBLEM AREA, HOW DO I GET TEXT HERE FROM THE MUTABLEARRAY
}
}
#end
How do I get the value from the array for that row into the label?
I don't understand Objective C that much, but I get an idea.
You need to either call setupTable from the ParseQuery callback and pass it the results array, or either iterate that array in the callback and in each iteration call mytable.label.setText(array[i]["property"])
Hope it makes sense.

Accessing Items in NSArray from PFObject

I'm building an application on Parse.com's backend for iOS that has simple follower / following logic to it. I.e. I want the user to be able to find other users and follow their profiles. I can currently retrieve all of the users in the database using a general PFQuery. Tapping on a users profile image "follows" that person. The data is set up so that an array called isFollowing has a unique object added to it with each new person that the user chooses to follow. It's adding it by the users objectId.
However, here's where my bump in the road is; for a separate screen I only want to return the number of people I, as a user, am following. This is the query to access the data:
- (void)viewDidAppear:(BOOL)animated
{
PFQuery *followingQuery = [PFUser query];
[followingQuery whereKey:#"objectId" equalTo:[PFUser currentUser].objectId];
[followingQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
NSLog(#"User test: %#", [objects valueForKey:#"isFollowing"]);
PFQuery *imagesQuery = [PFUser query];
[imagesQuery whereKey:#"objectId" containedIn:[objects valueForKey:#"isFollowing"]];
[imagesQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
NSLog(#"Username? %#", [objects valueForKey:#"username"]);
}
else
{
NSLog(#"Something went wrong. %#", error);
}
}];
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
[super viewDidAppear:animated];
}
Currently, user test: %# gives the output:
2014-07-28 10:17:58.537 YouStreamSport[11276:60b] User test: (
(
bHul1vkkmB,
brsN8PRUBO
)
)
However, when I run objects.count it's returning one object. Because of this I'm unable to iterate through the array object to find and return only the proper profile images. Since the array object is returning as 1, but there are 2 objects in the array itself, how can I access the items in that array? Any attempts to access using [objectAtIndex: ...] result in a crash.
Thanks for the input in advance.
I wasn't sure how to access a single item in an array within an array, but thanks to the help of #Droppy, I was able to solve the problem. This is the code I am now using:
- (void)viewDidAppear:(BOOL)animated
{
PFQuery *followingQuery = [PFUser query];
[followingQuery whereKey:#"objectId" equalTo:[PFUser currentUser].objectId];
[followingQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
self.array = [objects valueForKey:#"isFollowing"];
NSLog(#"User test: %#", self.array);
for (int i = 0; i <= self.array.count; i++)
{
NSLog(#"User %d: %#", i, [[[objects valueForKey:#"isFollowing"] objectAtIndex:0] objectAtIndex:i]);
}
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
[super viewDidAppear:animated];
}
The resulting output is as follows:
2014-07-28 10:53:16.155 appName[11416:60b] User 0: bHul1vkkmB
2014-07-28 10:53:16.156 appName[11416:60b] User 1: brsN8PRUBO
Thank you!
As written you will only display one objects username. Instead of trying to access them by index try iterating through the results like below. I'd also recommend not using the variable objects for both the inner and outer query. Try this out:
[imagesQuery findObjectsInBackgroundWithBlock:^(NSArray *myObjects, NSError *error)
{
if (!error){
if (myObjects.count) {
for (PFObject *item in myObjects){
NSLog(#"Username: %#", [item valueForKey:#"isFollowing"]);
}
}
}else{
NSLog(#"Something went wrong. %#", error);
}
}];

Parse retrieve columns from 2 different objects

I've been using sql forever and i'm now stepping into using parse. I've reach a problem regarding the database structure. i have 2 objects called stores and users. i've added the stores objectId to each user so that i can identify which user is to which store. I do not know if there is another way, but this was what i could think of.
The issue is that i want to retrieve all the users and the store names which is connected to the users. How can achieve this using parse?
My code at the moment.
PFQuery *query = [PFQuery queryWithClassName:#"users"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
}
NSLog(#"%#", objects);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
It does sound like the aim is to fetch related objects, but my understanding about how to do that differs from #Dima. You do it like this...
PFQuery *query = [PFQuery queryWithClassName:#"users"];
// this tells the query to eagerly fetch the related store object
[query includeKey:#"store"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// handle error, or ...
for (PFObject *object in objects) {
// object is from the users table, and object[#"store"] will contain
// the complete, related store object, eagerly fetched...
NSLog(#"the store object for %# is %#", object, object[#"store"]);
}
}];
If I understand correctly, you just want to fetch a related object? The documentation covers this.
You should be able to fetch a related object like this:
PFObject *store = user[#"store"]; // reference to related object but may need to be fetched
[store fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
// at this point you have fetched the store or you already have it
NSString *name = store[#"name"];
}];

Parse specific, how to save query results on an NSArray

Im very new to iOS and PFQuery and I need your help please
IM trying to store the array of objects obtained form PFQuery into a local NSArray, Im trying to do it inside if (!error) but it does not leave the block, once the block terminates so does the values for it on my array.
//InstallersDirectory.m
#interface InstallersDirectoryTVC ()
#property (nonatomic, strong) NSArray *supervisors;
#end
//more code goes here
- (void)viewDidLoad
{
[super viewDidLoad];
PFQuery *query = [PFQuery queryWithClassName:#"InstallersInfo"];
[query whereKey:#"supervisor" equalTo:#"yes"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
self.supervisors = [objects valueForKey:#"supervisor"];
}
}else {
NSLog(#"Error, %# %#",error,[error userInfo]);
}
}];
Everything works inside the block, like self.supervisors.count or NSLog, but it doesn't leave the block at all. Could you please tell me how I can get those values store definitely to self.supervisors?
Thanks !!
edit | comment
If you still need help on this issue, here is a suggestion or two:
first, self.supervisors is a NSArray. As a NSArray it has to be fully initialized and populated at creation time. So, even though you are iterating through your results, you are creating a new array each time which means only the last iteration would likely be stored in self.supervisors.
try this instead,
#property (nonatomic,strong) NSMutableArray *supervisors;
in your block:
[self.supervisors addObject:object];
//note: this will put the entire returned object in your mutable array
then later outside your block:
to list all the supervisors:
for (PFObject *supervisor in self.supervisors) {
NSLog(#"supervisor info:%#",supervisor);
}
To get the 4th supervisor in the list:
PFObject *superVisorNumber4 = [self.supervisors objectAtIndex:4];
To get the phone number from that supervisor (making this up :)
NSString *phone = [superVisorNumber4 objectForKey:#"phone"];
hope that helps

Resources