Im currently using following code to display an array inside of an UItextView / label / textfield :
PFQuery *query = [PFQuery queryWithClassName:#"appMsg"];
[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) {
NSString *besked = object[#"besked"];
msgRecieved0.text = besked;
NSLog(#"Object values %#",[objects valueForKey:#"besked"]);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
i have tried to set the NSString to :
NSArray *besked = object[#"besked"];
msgRecieved0.text = besked[0];
msgRecieved1.text = besked[1];
msgRecieved2.text = besked[2];
But again i get an error.
i have tried all i know, and i don't know what i should do to display a "list" of the arrays. I would like to show the first 10 values coming from the array
PFObject will not respond to Dictionary style element addressing. You must use keyValue coding style code.
Instead of NSString *besked = object[#"besked"];, try this:
NSString *besked = [object objectForKey:#"besked"];
Edit - This might be what you need, if the Parse object is an array.
NSArray* besked = [object objectForKey:#"besked"];
NSString* str0 = [besked objectAtIndex:0];
NSString* str1 = [besked objectAtIndex:1];
NSString* str2 = [besked objectAtIndex:2];
Related
I have a PFQuery that gets the current participants of a particular event:
PFQuery *getcurrentparticipants = [PFQuery queryWithClassName:#"Event"];
[getcurrentparticipants selectKeys:#[#"Participants"]];
[getcurrentparticipants whereKey:#"objectId" equalTo:ObjectID];
[getcurrentparticipants findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *newParticipantsArray = [[NSMutableArray alloc]init];
if([objects[0] valueForKey:#"Participants"] == nil){ // If object retrieved in objects is null. If there are 0 participants
[newParticipantsArray addObject:PFUser.currentUser.username];
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}else{ // STEP 5
for(int i=0;i<objects.count;i++) {
[newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]];
}
[newParticipantsArray addObject:PFUser.currentUser.username];
NSLog(#"Part to upload %#", newParticipantsArray);
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
This is how the method works:
Create a PFQuery object
Query the Participants Class for an specific ObjectId
If no error, then we create a NSMutable array
If no participants are in Parse then we insert the current user as participant.
Else, insert all participants in the mutable array and add currentuser at the end of the array.
Then upload it again to Parse
My problem is in step 5:
When I perform the tasks in the else, the column in Parse looks like this :
[["Participant 1"],"Participant 2"]
But I would like to have it like this:
["Participant 1","Participant 2"]
What I have tried:
I tried things like putting the arrays like this. [newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]]; and similar combinations, of course without luck.
It’s hard to say for sure since I can’t see the structure of your data but are you sure the value held in
[[objects objectAtIndex:i] valueForKey: #“Participants”]
Is a single user and not itself an array of users? The plural key “participants” seems to suggest it’s an array of users which would also explain the result you’re getting.
If in fact the value returned for the "Participants" key is an array, you can add the objects in it to your mutable array by doing the following:
NSArray* participants = [[objects objectAtIndex:i] valueForKey:#"Participants"]
[newParticipantsArray addObjectsInArray:participants];
This uses the addObjectsInArray: method of NSMutableArray to add the objects from the old array into the new one.
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 a mutable array named "eventnameArray". What i want is to add objects. This objects to add come from a Parse query.
This is the mutable array:
eventArray = [[NSMutableArray alloc]init];
This is the Parse query, which works.
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query selectKeys:#[#"EventName"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %lu scores.", (unsigned long)objects.count);
// Do something with the found objects
[eventnameArray addObject:[objects valueForKey:#"EventName"]];
NSLog(#"%#", eventnameArray);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
I suspect the problem is in the addObject call, which is not being performed correctly, or maybe in the whole PFQuery structure.
To show the array in the tableviewcell, i do this:
cell.label.text = [self.eventnameArray objectAtIndex: [indexPath row]];
EDIT :
I replaced addObject for addObjectsFromArray like this:
[eventnameArray addObjectsFromArray:[objects valueForKey:#"EventName"]];
But i still can't get it to work.
EDIT 2:
I replaced EDIT 1 with :
for (NSObject *object in objects){
NSString *name = [object valueForKey:#"EventName"];
[eventnameArray addObject:name];
}
But the label still doesn't show anything.
EDIT 3 :
The solution was just a matter or reloading data like this:
[self.tableView reloadData];
I guess what you're looking for is to replace addObject: with addObjectsFromArray: because parse returns an array to you so calling valueForKey:#"EventName" will return an array and you want to add all of those names to your eventArray.
So:
[eventnameArray addObjectsFromArray:[objects valueForKey:#"EventName"]];
You probably want to add the EventName of each object in the objects array to your eventnameArray, not the "EventName" value of the objects array.
Try this instead:
for (NSObject *object in objects){
NSString *name = [object valueForKey:#"EventName"];
[eventnameArray addObject:name];
}
//EDIT: Addition for a complete fix. Reload the tabeview afterwards.
[self.tableView reloadData];
I am currently using parse to develop an iPhone app and I got this code:
I have a Class called "Event" and has a field called "EventName"
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query selectKeys:#[#"EventName"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %lu scores.", (unsigned long)objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
}
}];
This works perfectly, but i get the object's IDs instead of the event names which is what i want.
EDIT :
So far I got this inside the IF statement
self.eventArray = [objects valueForKey:#"EventName"];
for(int i=0; i<10 ; i++){
NSLog(#"%#", eventArray[i]);
}
by doing that the question got solved.
All you have to do is use the objects array that was retuned from finding the objects and pass them into your array by using valueForKey.
So it would be
if (!error) {
self.yourEventNameArray = [objects valueForKey:#"EventName"];
}
I am quering Parse and get an array back
PFQuery *query = [Points query];
[query whereKey:#"city" equalTo:[SharedParseStore sharedStore].chosenCity];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
I want to sort the array into a dictionary based on the district value that is in the Point object so that i can use a tableview with sections (district section names)
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
for (Points *point in objects) {
[dict setValue:point forKey:point.district];
}
block(dict, error);
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
The problem is it only adds 1 value of course
So to make it easy to understand:
The objects coming from Parse are Point objects with the following property's:
Name,District,City
I want to create a NSdictionary with the districts (so I need to collect them out of the objects first because I don't know them) as a key and the value for that key is an array with the points that are in that district.
I don't know beforehand what the districts will be. They will need to be picked from the objects array that is returned from Parse.
My final object I want to create is a nsdictionary with arrays of points for every distinct district(that is the key).
Example:
[#"districtA" : an array with Point objects that have districtA in their district property, etc etc]
what is the best way to do this because i really can't see how to do it ?
You answered your own question when you say:
I want to create a NSdictionary with the districts as a key and the
value for that key is an array with the points that are in that
district.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableDictionary *dict = [#{} mutableCopy];
for (Points *point in objects) {
// if no point for that district was added before,
// initiate a new array to store them
if (![dict containsKey:point.district])
dict[point.district] = [#[] mutableCopy];
[dict[point.district] addObject:point];
}
block(dict, error);
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];