I want to set ImageView hidden or not according to the boolean value true x false, which is located in Parse database. Could you give me some ideas how to do that? Got problems because of having property ImageView in my CustomCell.h
EDIT:
PFObject *yes = [PFObject objectWithClassName:#"MyClass"];
cell.discounts.hidden = [yes[#"yesnocolumn"] boolValue];
I just don`t understand to usage of PFObject, myclass returning (null) value.
EDIT2:
PFQuery *query = [PFQuery queryWithClassName:#"Classname"];
[query whereKey:#"yesnocolumn" equalTo:#1];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
if (!error) {
cell.discounts.hidden = YES;
}else{
cell.discounts.hidden = NO;
}}];
The boolean from the service will look like either #0 or #1, so you can't just test for truthiness, because:
if (#0) {
// this condition is true. that's surprising!
}
So the safe way to test is to say:
cell.imageView.hidden = [myParseObject[#"hidden"] boolValue];
EDIT - Now I understand the misunderstanding. Here's how parse works:
1) Create an object locally:
PFObject *yes = [PFObject objectWithClassName:#"MyClass"];
This object won't have any values initialized. So any bools it contains will be 0, or NO.
2) Set some value locally:
yes[#"yesnocolumn"] = #1; // makes it true
// or = [NSNumber numberWithBool:YES];
3) Save it. (you can also use the data browser to initialize objects manually).
[yes saveInBackground];
4) Get an object. I think this is the part that's giving you trouble... just creating a local object doesn't really do much. The idea of parse is that objects are stored in the cloud. To get them, you need a PFQuery.
PFQuery *query = [PFQuery queryWithClassName:#"MyClass"];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
// this will return all (up to 100 by default, MyClass objects that are
// saved in the cloud
if (!error && results.count) {
NSLog(#"the first object is %#", results[0]);
NSLog(#"the bool is %d", [results[0][#"yesnocolumn"] boolValue];
}
}];
Does this make sense? You can't expect the object to have any values initialized until you either set them locally, or retrieve already initialized remote copies. Also note, if you run the query on the next source line after saveInBackground, you won't get the saved result, since the save won't have finished. Start out saving in one run of your app, then reviewing in the data browser, then do a query.
It's worth doing a thorough review of their docs here.
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!
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.
I have an NSArray of PFObjects and want to fetch all data related to the objects in this array in one go, so afterwards there is no need to make a new call to parse. How can I do this?
My answer is assuming the array you want is contained in a PFObject. You can query for this object and use the include key to include the array contained within that key.
PFQuery *query = [PFQuery queryWithClassName:#"<object's class name>"];
[query whereKey:#"objectId" equalTo:object.objectId];
[query includeKey:#"<array key>"];
If the objects in your array have pointers to other objects within them you can use the dot syntax to fetch everything at once.
[query includeKey#"<array key>.<pointer in object from array key>"];
Run the query once set up and you should retrieve an array of one object since objectIds are unique, within this object will be the array.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
if(error){
// handle error
}else{
if([objects count] > 0){
PFObject *object = objects[0]; // Only one matching object to query
NSArray *array = object[#"<array key>"]; // Array you want
}
}
}];
You can use PFObject's methods family:
+(void)fetchAll:(NSArray*)objects
Check out PFObject documentation on those methods https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/fetchAll:
As far I got your query , this might be-
PFQuery *query = [PFQuery queryWithClassName:#"CLASS_NAME"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error && objects.count != 0)
{
NSLog(#"Successfully retrieved: %#", objects);
for(int i=0; i< objects.count; i--)
{
NSDictionary *dict = [objects objectAtIndex:i];
self.label = [dict objectForKey:#"PROPERTY_KEY"]; //Just example
/* Also modify this code as per you want to fetch properties of some or all */
/* Do as you want to with properties and also change key as per need of column/property you want */
}
}
}];
This way you will get array and also fetch fetch as you want.
I'm querying relation data on parse and I would like the objects to come back ordered by the date they were created. I've had this method work before but haven't been able to get an ordered query using relational data. The query return is in a random order. Thanks in advance! Here's my code:
PFQuery *postQuery = [PFQuery queryWithClassName:#"Post"];
[roomQuery whereKey:#"name" equalTo:self.postName];
NSError *error;
//done on main thread to have data for next query
NSArray *results = [postQuery findObjects:&error];
PFObject *post;
if ([results count]) {
post = [results objectAtIndex:0];
NSLog(#"results were found");
} else {
NSLog(#"results were not found");
}
PFRelation *commentsRelation = [#"Comments"];
[commentsRelation.query orderByAscending:#"createdAt"];
[commentsRelation.query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error Fetching Comments: %#", error);
} else {
NSArray *comments = objects;
}
I'm a little confused by your code,
you create a "postQuery", and call it, but never use any of its data.
There's also a roomQuery that never seems to have been allocated, or used.
You're querying a specific post by its name. Are you controlling its name? If not, you should use id's
what is PFRelation commentsRelation = [#"Comments"];
Probably because it's just a snippet, this stuff is dealt with elsewhere; however, for my answer, I'm assuming that your "comments" field is an array of "Comment" class objects.
Option 1:
PFQuery * postQuery = [PFQuery queryWithClassName:#"Post"];
[postQuery whereKey:#"name" equalTo:self.postName];
// again, possibly an id field would be more reliable
// [postQuery whereKey:#"objectId" equalTo:self.postId];
[postQuery includeKey:#"Comments"];
PFObject * post = [postQuery getFirstObject];// no need to download all if you just want object at [0]
// this will contain your post and all of it's comments with only one api call
// unfortunately, it's not sorted, so you would have to run a sort.
NSArray * comments = [post[#"Comments"] sortedArrayUsingComparator: ^(id obj1, id obj2) {
return [obj1[#"createdAt" compare: obj2[#"createdAt"];
}];
Option 2:
Perhaps a better option is to rework your data structure and instead of associating the comments to the post, you could associate the post to the comments (as in the parse docs)
PFQuery * postQuery = [PFQuery queryWithClassName:#"Post"];
[postQuery whereKey:#"name" equalTo:self.postName];
// again, possibly an id field would be more reliable
// [postQuery whereKey:#"objectId" equalTo:self.postId];
PFQuery * commentQuery = [PFQuery queryWithClassName:#"Comment"];
[commentsQuery whereKey:#"parent" matchesQuery:postQuery]; // when creating a comment, set your post as its parent
[commentsQuery addOrderDescending:#"createdAt"]
[commentQuery findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {
// comments now contains the comments for myPost
}];
Both of the above solutions avoid making extra unnecessary api calls (parse charges based on calls after all!).
I am using Parse.com in IOS Application. In that i am using one Class Background which contains another Class values as row like an array.
I want to update an array of those values without using for loop. I want to update with only one Single PFQuery Call.
Class
Image -- id - image (PFFile) - count (integer)
Background -- id - imagesArr (Array)
The Background class contains image ids in imagesArr. I want to update one row in background. Then i need to update all images whose are imagesArr increment their count column in Image Class.
We can do it by using for loop.
like
[bgQuery findObjectinBackground:^(NSArray * imageIds, NSError *error)
{
if(!error)
{
for(int i= 0; i<imageIds.count; i++)
{
NSString *imageId = [imagesIds objectAtIndex:i];
PFQuery *getImageQuery = [PFquery queryWithClassName:#"Image"];
[getImageQuery getObjectWithIdInBackground:imageId withBlock]
// Code for refresh
}
}
}
We can do it like by using for . But i need to execute n number of PFQueries. I felt it leads to slow the application Performance.
Instead of this can we update all images in Background row imagesArr id with one single PFQuery.
Please help me in this issue.
Thanks in advnace.
This is a bit confusing because you keep saying that you are updating with a query. Queries are for retrieving objects.
With that being said, nested objects save automatically. For example
PFObject * object1 = [PFObject objectWithClassName:#"Object1"];
PFObject * object2 = [PFObject objectWithClassName:#"Object2"];
object1[#"object2ref"] = object2;
[object1 save]; // this should save object 2 as well.
If you do this several times and have an array of Object1's
[PFObject saveAllInBackground:arrayOfObject1objects]; // will save all object 1's, and object 2's
In retrieving objects.
PFQuery * query = [PFQuery queryWithClassName:#"Object1];
[query includeKey:#"object2Ref"]
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// all objects, and object2ref data should be available
}
else {
NSLog(#"Error, %# %#",error,[error userInfo]);
}
}];
Hope this helps, I'm not entirely sure what you're trying to do.