I am a bit new to Xcode and Parse and am creating a query in a outlet collection. Everything looks fine except I am getting an error stating "Property 'text' not found on object of type 'id'"... Here is my code that I have:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
int i = 0;
for (PFObject *object in objects) {
if (i >= [self.EventTitles count]) break;//to make sure we only write up to the max number of UILabels available in EventTitles
(UILabel *) self.EventTitles[i].text = object.objectId;//I assume the "objectId" property of object is an NSString!
i++;
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
Can anyone help me so I can get this build to not fail?
Change this:
(UILabel *) self.EventTitles[i].text = object.objectId;
to:
[(UILabel *)self.EventTitles[i] setText:object.objectId];
Related
Im trying to work with a script thats in objective c i want to make an int i can access throughout view did load to change settings or the viewcontroller but it doesn't work like swift and i get a null out of the pfquery so i tried to make an array and couldn't get anything out of the query with that as well
what is the best way to set up and int variable and set it in the pfquery then use if statements later on
here is some of my code
__block int setting = 0;
PFQuery *query = [PFQuery queryWithClassName:#"test"];
[query whereKey:#"testActive" equalTo:[NSNumber numberWithBool:YES]];
[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);
setting = [[object objectForKey:#"testSetting"] intValue];
// When checked in the loop i get all the right data
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
// When checked here i get settings = 0 when it should be 1
if (setting == 0) {
self.segmentedControl.selectedSegmentIndex = 0;
} else if (setting == 1) {
self.segmentedControl.selectedSegmentIndex = 1;
}
if (setting == 3) {
[self.segmentedControl setEnabled:YES];
}else {
[self.segmentedControl setEnabled:NO];
}
But when i check the setting int its always 0 but if i log in the loop is gives me the correct number
thanks for your help
Thanks for your help but i figured it out..
findObjectsInBackgroundWithBlock was not completing before i did the if statements
so i moved the code into a new function which i ran when the PFQuery was completed giving me the correct int
I'm getting 'This query has an outstanding network connection.'
I know only one query is allowed at time and I think that's what I'm doing here but apparently not..
retrieveFromParse is at viewDidLoad.
-(void) retrieveFromParse{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"photo" equalTo:[PFObject objectWithoutDataWithClassName:#"photoObject" objectId:self.currentObjectID]];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", (int)objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
self.commentArray = [object objectForKey:#"comment"];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
// 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.
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
}
- (id)initWithStyle:(UITableViewStyle)style currentObjectID:(NSString*) currentObjectID {
self = [super initWithStyle:style];
if (self) {
self.currentObjectID = currentObjectID;
self.parseClassName = #"extra";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
self.objectsPerPage = 25;
}
return self;
}
The problem is here:
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
You're calling that after you make the call in the background. In other words, you are trying to modify a query that is currently being made while it's being made in the background - triggering the error. Try this instead:
-(void) retrieveFromParse{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"photo" equalTo:[PFObject objectWithoutDataWithClassName:#"photoObject" objectId:self.currentObjectID]];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", (int)objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
self.commentArray = [object objectForKey:#"comment"];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
// 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.
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
}];
}
I'm trying to update a PFObject that already exists in the cloud by performing a query, adjusting the retrieved object, and then saving it. I initially tried this with the saveInBackgroundWithBlock method, but the block never got called. I moved the save method afterwards, and any code after the save call does not get executed.
I'm running Yosemite with the newest Parse OS X SDK.
-(void)saveCardToCloud:(Card *)card{
if(card.cardID){
PFQuery *cardQuery = [PFQuery queryWithClassName:#"Card"];
[cardQuery whereKey:#"cardID" equalTo:card.cardID];
[cardQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if ([objects count] > 0) {
PFObject *retrievedObject = [objects firstObject];
retrievedObject[#"lastSeenAt"] = card.lastSeenAt;
if(!retrievedObject[#"firstSeenAt"]){
retrievedObject[#"firstSeenAt"] = card.firstSeenAt;
}
retrievedObject[#"officeCheckIns"] = card.officeCheckIns;
retrievedObject[#"shopCheckIns"] = card.shopCheckIns;
[retrievedObject save];
NSLog(#"SAVED IT");
[retrievedObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(!error){
NSLog(#"Updated existing card");
}else{
NSLog(#"Failed to update existing card: %#", error);
}
}];
}
}
}];
}
}
I was setting one of the keys on the PFObject to nil, but Parse wasn't throwing an exception. Fix:
if(card.officeCheckIns) retrievedObject[#"officeCheckIns"] = card.officeCheckIns;
if(card.shopCheckIns) retrievedObject[#"shopCheckIns"] = card.shopCheckIns;
I am using Parse.com to store data for an iOS app. The code below successfully retrieves all values in a nested array belonging to a PFObject "game". However, if I need to query for another array (at the same level as "winners" (say "losers") i cannot get it to work, and not all the values in the array losers gets populated. I suppose i could do them all on the main thread and not try to nest the fetches (nested blocks) but i'm wondering if:
1) Is the way i'm storing my data prohibiting me from using Parse's built in query/fetch functionality properly? Data stored as:
PFObject * newGame = [PFObject objectWithClassName:#"Game"];
NSArray * winner = [NSArray arrayWithObjects:[_allPlayersPFDictionary objectForKey:[playerData objectAtIndex:0]], [playerData objectAtIndex:1], nil];
[_gamePF addObject:winner forKey:#"winners"];
2) Is there a better, cleaner way to do the query and get ALL the values of all nested arrays of data in a query? Again, winners is not a PFObject, but is an array of array of PFObject of 2 different types ([PFObject fetchAll:(NSArray *)winnersArray] does not work, because all objects in the Array must be of the same 'type' of PFObject). I store it this way because each winning player has another PFObject (1 to many) "powers" associated with them.
Here is the query that works but i can't figure out how to add "losers" to it and properly populate all data in the background.
PFQuery * gamesQuery = [PFQuery queryWithClassName:#"Game"];
[gamesQuery orderByDescending:#"createdAt"];
gamesQuery.limit = 30;
[gamesQuery findObjectsInBackgroundWithBlock:^(NSArray * theGames, NSError * error) {
if (error) {
NSLog(#"ERROR: There was an error with the Query to get Games!");
} else {
for (PFObject * aGame in theGames) {
for (NSArray * aWinner in [aGame objectForKey:#"winners"]) {
[[aWinner objectAtIndex:0] fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (error) {
NSLog(#"ERROR: There was an error with the Query to get Player in winnersArray!");
} else {
[PFObject fetchAllIfNeededInBackground:[aWinner objectAtIndex:1] block:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"ERROR: There was an error with the Query to get Powers in winnersArray!");
} else {
[_gamesPF addObject:aGame];
NSLog(#"Games from viewDidLoad %#", _gamesPF);
[_tableView reloadData];
}
}];
}
}];
}
}
}
}];
Well... i feel kinda stupid. Definitely much easier to use Parse in an object oriented manner for the data model. Was able to easily solve it by remodeling the data to be:
Game (PFObject *) has:
--> winners { (PFObject *), (PFObject *), ..., nil }
--> losers { (PFObject *), (PFObject *), ..., nil }
where a winner is created as:
[testWinner1 addObject:power1 forKey:#"power"];
[testWinner1 addObject:power2 forKey:#"power"];
[testWinner1 addObject:[_playerPFDictionary objectForKey:#"Tom"] forKey:#"player"];
Which then makes the query much easier and involves only one background block like so:
PFQuery * gameQuery = [PFQuery queryWithClassName:#"TestGame"];
[gameQuery includeKey:#"winners.player"];
[gameQuery includeKey:#"winners.power"];
[gameQuery includeKey:#"losers.player"];
[gameQuery includeKey:#"losers.power"];
[gameQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"failed");
} else {
NSLog(#"testGame: %#", [objects objectAtIndex:0]);
}
}];
I put "return" in quotes because I don't want to literally return it. I want to do it similar to how you pass a pointer-to-a-pointer for [NSString stringWithContentsOfFile:usedEncoding:error:].
I would like to make parseFiles:error return nil and have the error reference that was passed in contain the first or second error, depending on which one failed. It seems like a Cocoa way to do it?
EDIT: Sorry, I should've been more clear about where I was having the problem. If the first path is bogus, it functions as I want. (I get the error instance outside and it prints.) If the first path is legit, as it filler string below implies, I get EXC_BAD_ACCESS.
But now I fixed it. I need to refer to it as *error inside the parseFiles:error: method and use == nil when checking if it failed. I thought I could just to if (error)...
EDIT 2 Ok, it doesn't work. I'm getting EXC_BAD_ACCESS. I'm not sure what I'm doing wrong with the conditions that check for the errors.
#implementation PassingError
- (id)init {
self = [super init];
NSError *error;
[self parseFiles:#"/untitled.py" error:&error];
if (error != nil) {
NSLog(#"I failed because: %#", error);
}
return self;
}
// Wraps with reading errors.
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1 error:*&error];
// there was a read error
// I need an asterisk here...
if (*error != nil) {
// ...and also one here
NSLog(#"FIRST ERROR: %#", *error);
return nil;
}
// here is where you'd do something that might cause another error,
// I'll just try and read a second file for simplicity
NSStringEncoding enc2;
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2 error:*&error];
// there was a SECOND error
if (*error != nil) {
NSLog(#"SECOND ERROR: %#", *error);
return nil;
}
// return both or whatever
return [NSArray arrayWithObjects:contents1, contents2, nil];
}
#end
Passing pointers around in Objective-C can get confusing. I remember having trouble grasping what needed to be done. When you have a method like this:
- (BOOL) saveValuesAndReturnError:(NSError **) error
{
BOOL success = [self doSomethingImportant];
if (!success && error)
{
// Unsuccessful and error is a valid ptr-to-ptr-to-NSError.
// Basically, someone has given us the address of a (NSError *).
// We can modify what that pointer points to here.
*error = [NSError errorWithDomain:#"myDomain" code:100 userInfo:nil];
}
return success;
}
This is intended to be invoked like this:
// If the caller doesn't care that it failed:
[someObject saveValuesAndReturnError:NULL];
// Or, if the caller wants to get error information on failure
NSError *anError = nil;
BOOL success;
// pass address of our (NSError *)
success = [someObject saveValuesAndReturnError:&anError];
if (!success)
{
// anError now points to an NSError object, despite being initialised to nil,
// because we passed the address of our NSError ptr, the method was able to
// change where `anError` points to.
NSLog (#"An error occurred while saving values: %#", anError);
}
Perhaps a very relevant read in this case is a CIMGF blog post covering exactly this topic.
However...
I remember reading a while ago that methods that return errors via method arguments such as stringWithContentsOfFile:usedEncoding:error: make no guarantee not to modify the error argument for success. In other words, you cannot rely on the value of the error parameter if the method succeeded. In your particular case, it may be better to do:
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1, enc2;
NSError *innerError;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1
error:&innerError];
if (contents1 == nil)
{
if (error) *error = innerError;
return nil;
}
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2
error:&innerError];
if (contents2 == nil)
{
if (error) *error = innerError;
return nil;
}
// do whatever with contents1 and contents2
}