Query Object Titles to Labels - ios

I have a Parse query that runs to gather the 10 closest Arcades in your area, and I am trying to have them display those object titles in 10 separate labels. I have the following code which gathers the 10 closest and logs them, and I am trying to start by displaying the objectId in the labels but cannot figure out how to display them all and not just 1. Any suggestions?
PFQuery *query = [PFQuery queryWithClassName:#"Arcade"];
CLLocation *currentLocation = locationManager.location;
PFGeoPoint *userLocation =
[PFGeoPoint geoPointWithLatitude:currentLocation.coordinate.latitude
longitude:currentLocation.coordinate.longitude];
query.limit = 10;
[query whereKey:kPAWParseLocationKey nearGeoPoint:userLocation withinMiles:kPAWWallPostMaximumSearchDistance];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
NSString *EventTitle = object.objectId;
EventTitle1.text = EventTitle;
for (UIImageView *imageView in self.imageViews) {
__block UIImage *MyPicture = [[UIImage alloc]init];
PFFile *imageFile = [object objectForKey:#"test"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error){
if (!error) {
MyPicture = [UIImage imageWithData:data];
imageView.image = MyPicture;
}
}];
}
for (UILabel *EventLabel in self.EventTitles){
EventLabel.text = object.objectId;
}
}
UPDATE: I have created two collection outlets, however when they display they only display the final object queried, not all 10 of them? Am I doing something wrong?

Your problem is EventTitle1.text = EventTitle;, because you explicitly reference the label. What you should be doing is updating the labels in sequence. This could be done by having the labels in an array (perhaps an IBOutletCollection) and using the iteration index. Or you could tag all of the labels and then look them up (again, using the iteration index).
But, your intended solution isn't simple and doesn't scale. It would be better to use a table view (Parse SDK even gives you an easy way to populate a table view from a query).

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.

How to run a loop of parse queries?

I am making an app that registers users and allows them to add friends etc. So I have a LoginViewController where I retrieve the array of user's friends' objectIds when the login is successful. This function is called.
- (void) getFriendList
{
NSString * objectID = [PFUser currentUser].objectId;
NSLog(#"%#", objectID);
PFQuery *query = [PFUser query];
[query getObjectInBackgroundWithId:objectID block:^(PFObject *username, NSError *error) {
sharedClass.sharedInstance->retrievedFriends = username[#"friendsIds"];
friendListLoaded = YES;
[self getFriendsUsernames];
}];
Here i get an array object that contains object ids of all the friends this user has. As you can see when getFriendList is completed, it calls another function called GetFriendsUsernames. This function is supposed to retrieve profile pictures and usernames of those friends so I can populate the Friend List view later.
-(void) getFriendsUsernames
{
NSMutableArray * objectIDs = [[NSMutableArray alloc] initWithArray: sharedClass.sharedInstance->retrievedFriends];
PFQuery *query = [PFUser query];
int friendsCount = [objectIDs count];
for(int i = 0; i<=friendsCount;i++)
{
[query getObjectInBackgroundWithId:objectIDs[i] block:^(PFObject *username, NSError *error) {
[sharedClass.sharedInstance->friendsUsernames addObject:username[#"username"]];
[sharedClass.sharedInstance->friendsProfilePictures addObject:username[#"ProfilePicture"]];
}];
NSLog(#"%#", sharedClass.sharedInstance->friendsUsernames );
}
}
But this seems to be unsuccessful because nothing is logged on the console where it should log username of retrieved friend whenever one query gets finished. Am I doing this the right way? My array count is right so loop runs to the number of friends a user has. It also prints the username of first object in objectIds array if i put the logging command in the loop.
Your NSLog runs immediately after your query objects are queued for execution. Therefore, even if the code is running correctly (and I suspect it might not be), you'll never get the correct results you're after logged to the console.
If you have your Parse classes designed in a certain way, you could collapse this into 1 query by using the include method on PFQuery. This assumes that you've created an array of Pointers on your User object, named "friends". If you actually store the objectId (i.e., the string value) of each friend, this code won't work.
I'm not using Swift yet, so here's how I'd write this query in Objective-C:
- (void)getFriendList {
PFUser *currentUser = [PFUser currentUser];
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:currentUser.objectId];
[query includeKey:#"friends.username"];
[query includeKey:#"friends.ProfilePicture"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *user, NSError *error) {
if (error != nil) {
// Process the error
} else }
// You've got your user and the data you wanted
}
}];
}
Found it. Not the prettiest way out there but well it does what i want. gets me arrays of usernames and profile pictures of every objectID contained in array of friends.
NSMutableArray * objectIDs = [[NSMutableArray alloc] initWithArray: sharedClass.sharedInstance->retrievedFriends];
PFQuery *query = [PFUser query];
PFFile * imageFile;
//UIImage *profilePictureData;
int friendsCount = [objectIDs count];
for(int i = 0; i<friendsCount;i++)
{
PFObject * username = [query getObjectWithId:objectIDs[i]];
[sharedClass.sharedInstance->friendsUsernames addObject:username[#"username"]];
[sharedClass.sharedInstance->friendsEmailAdresses addObject:username[#"email"]];
//NSLog(#"%#",username[#"ProfilePicture"]);
imageFile = [username objectForKey:#"ProfilePicture"];
NSData *imageData = [imageFile getData];
UIImage *imageFromData = [UIImage imageWithData:imageData];
[sharedClass.sharedInstance->friendsProfilePictures addObject:imageFromData];
NSLog(#"%#", sharedClass.sharedInstance->friendsUsernames );
}

How can i perform a PFRelation query using parse in ios

I have created a relation between two tables
LocationClass Table In this table i have a column with location images with the relation of another table ie., Assets
![This is location Class Image, If user selected a locationName, need to get the locationImages(viewRelation)][1]
Assets Table, It contains Images for each relation Now my query is how to get the data from relation database. Here if user select a location means i need to get a group of images for the relevant location ![This is my Assets Table, here need to retrieve the images based on the selected location from the locationClass Table][2]
Till now, I have done with this format
PFQuery *queryObj = [PFQuery queryWithClassName:#"LocationClass"];
sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
// Run the query
[queryObj whereKey:#"locationImages" equalTo:[PFObject objectWithoutDataWithClassName:#"Assets" objectId:#"aAPzhdO4w6"]];
[queryObj findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
[locationArray addObjectsFromArray:objects];
sharedDelegate.locationsArray = locationArray;
[locationDropDown reloadData];
}
}];
In my location array for a single object contain
<__NSArrayM 0xaee4eb0>(
<LocationClass:ZtOP9voUak:(null)> {
LocationId = WilliamsBurgId;
LocationName = WilliamsBurg;
locationImages = "<PFRelation: 0xaf53c30>(<00000000>.(null) -> Assets)";
}
)
Solution for to pass the data from PFRelation Object
// should pass the main table name
PFQuery *query = [PFQuery queryWithClassName:#"LocationClass"];
// should pass object id for the selected row
PFObject *getImageObject = [query getObjectWithId:#"need to pass object id from the maintable"];
locationImagesArray = [[NSMutableArray alloc]init];
// To acess the data from the relation object
PFRelation *relationObj = [getImageObject relationForKey:#"locationImages"];
PFQuery *query1 = [relationObj query];
[query1 findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
[locationImagesArray addObjectsFromArray:results];
for (int imgCount = 0; imgCount < [locationImagesArray count]; imgCount ++) {
PFFile *getImage1 = [[locationImagesArray valueForKey:#"Image"] objectAtIndex:imgCount];
[getImage1 getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error)
{
if (imageData!=nil) {
UIImage *image = [UIImage imageWithData:imageData];
activityImage.image = image;
NSLog(#"location image output");
NSLog(#"location image output : %#", activityImage.image);
}
}];
}

Parse, getting pointer objects stored in an array along with primary object query

i have a "Station" object, it contains a column named "imagePointers", the array is populated as such:
[{"__type":"Pointer","className":"Image","objectId":"4Xtj32BOQy"},{"__type":"Pointer","className":"Image","objectId":"7DHCt7cx0O"}]
The pointers point at another object named Image, but they are stored as an array at the station object.
In iOS i query the station object as such:
PFGeoPoint *geoPoint = [PFGeoPoint geoPointWithLatitude:coordinate.latitude
longitude:coordinate.longitude];
[query whereKey:#"location" nearGeoPoint: geoPoint];
[query includeKey:#"imagePointers.Image"];
//[query orderByDescending:#"createdAt"];
return query;
When i receive the station object, the imagePointers field is empty? Completely empty
It seems like what you're trying to do is not supported by Parse.com. See this question.
Something like this might get you part of the way there. I'm using PFFiles to store images and then using the URL for the PFFile to load them in UIImageView with AFNetworking category
[query includeKey:#"imagePointers"];
[query findObjectsInBackgroundWithBlock:^(NSArray *stations, NSError *error) {
NSArray *imagePointers = stations[0][#"imagePointers"];
for (PFObject *images in imagePointers) {
[image fetchIfNeeded];
// add image to local array or something
}
}];
// it's possible you might need to do something like this...
PFObject *station = stations[0];
[station fetchInBackgroundWithBlock:^(PFObject object, NSError error) {
NSArray *imagePointers = stations[#"imagePointers"];
for (PFObject *images in imagePointers) {
[image fetchIfNeeded];
}

Remove an item from a column type of array in Parse (iOS SDK)

I have table name "Events". In that table I have a column of type array of string. I'm struggling with how I can delete only one element from that column. Consider the image below, I want to delete all the occurrences of "iYYeR2a2rU" from the "usersIncluded" column, without deleting the rows.
I've used the removeObject:(id) forKey:(NSString *) and it didn't work.
This is how I'm trying to achieve it:
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
NSArray *eventObjects = [query findObjects];
[query whereKey:#"usersIncluded" equalTo:[self.uniqeFriendList objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
for (int i = 0; i<objects.count; i++) {
PFObject *event = [eventObjects objectAtIndex:i];
[event removeObject:[self.uniqeFriendList objectAtIndex:indexPath.row] forKey:#"usersIncluded"];
}
}];
}
The self.uniqeFriendList is a mutable array containing the ids that I want to delete from the 'usersIncluded' column.
Thanks in Advance
I think you're using the right method (removeObject:forKey: should do exactly what you want) but I think you're working with objects from the wrong array. You're performing your query twice, and within the findObjectsInBackgroundWithBlock: you're working with the array from the first time you called it... Try this:
PFQuery *query = [PFQuery queryWithClassName:#"Events"];
[query whereKey:#"usersIncluded" equalTo:[self.uniqeFriendList objectAtIndex:indexPath.row]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
for (int i = 0; i <objects.count; i++) {
PFObject *event = [objects objectAtIndex:i]; // note using 'objects', not 'eventObjects'
[event removeObject:[self.uniqeFriendList objectAtIndex:indexPath.row] forKey:#"usersIncluded"];
}
[PFObject saveAll:objects];
}];
}

Resources