Downloading images with parse causes unrecognized selector error - ios

I am trying to download images from Parse and when I am finished downloading all images from parse my program crashes with the error:
NSNull getDataInBackgroundWithBlock:progressBlock:]: unrecognized selector sent to instance.
I have tried adding some conditions such as
if(![object objectForKey:#"goal_image"]) //If it is nil => exit
But it is still crashing. Here is my code:
PFQuery *query = [PFQuery queryWithClassName:#"Goal"];
[query getObjectInBackgroundWithId:[object objectId] block:^(PFObject *object, NSError *error) {
if(!error)
{
PFFile * imageFile = [object objectForKey:#"goal_image"];
if (imageFile) {
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error2) {
if (!error2) {
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"Images/Goals/%#",[object objectId]]];
[data writeToFile:jpgPath atomically:YES];
}
} progressBlock:^(int percentDone) {
if (percentDone == 100) {
NSLog(#"Download Completed");
}
}];
}
}
}];

tl;dr
You should check for NSNull aside from checking for nil.
PFFile * imageFile = object[#"goal_image"]; // note the modern Obj-C syntax
if (imageFile && ![image isEqual:[NSNull null]]) {
...
}
Explanation
NSNull is different from nil, the first one being an object.
Since NSArray and NSDictionary only hold objects, nil cannot be stored in such containers and that's why NSNull is used, typically to represent a null value returned in the JSON response.
Sending messages to nil fails silently, whereas sending an unrecognized selector to the NSNull singleton will result in a crash.
Also remember that objectForKey: will return nil in case the key is not found in the dictionary.

I know this is old, but just in case anyone else is having this problem. For me, the above answer didn't work. What worked for me was enabling local datastore in app delegate. Honestly, not sure why this works, but I thought I should share
// Enable Local Datastore
[Parse enableLocalDatastore];

Related

Problems in calling Parse getdataInthebackground in a for loop

Hi I am trying to query some files from my Parse database and I want the files to be sorted according to the updateAt time. I have the following code. The query works and the results are sorted according to my condition, but when I load the files using getDataInBackground and then add to an array. The files are not sorted and they appear to be random in the array.
So My questions are
What can I do to make sure the files in the array are in the same order as the query results?
Any way to check the files/images against the objectID in the completion block of getDataInBackground?
p.s. I don't want to use getData since I don't want it to block the main thread.
Thank you very much in advance
PFQuery *query = [PFQuery queryWithClassName:#"Photo"];
[query orderByDescending:#"updateAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *photoStacks, NSError *error)
{
if (!error) {
// The find succeeded.
for (PFObject *photoImage in photoStacks) {
PFFile *userImageFile = photoImage[#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData];
// need to check object id before adding into the stack to make sure the order is right
[photoImageStacks addObject:image];
if ([photoImageStacks count] == photoStacksCount)
{
[photoPile setArray:photoImageStacks];
}
}
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
use breakpoint and trace first photoImageStacks and second after response you should call reload method if you are using tableview or some delegate or fire a notification so that you can update ui accordingly after successful response.

Saving an retrieving sound on Parse.com

I am trying to download some short sound file on Parse.com in an iOS application.
The file has previously been saved using the following code:
NSData *soundData = [NSData dataWithContentsOfURL:myURL];
parse_Sound = [PFFile fileWithName:#"XXYYZZ"
data:soundData];
[parse_Sound saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
NSLog(#"sound-upload NG”);
} else {
NSLog(#"sound-upload OK");
}];
}
}];
Seeing the message on the debugging console, it appearently works.
Now what kind of code do I need to run to retrieve(download) the sound file?
I have browsed the net, but found nothing clear and working.
To get data back from the server you need to need to run a query asking for that object but you haven't associated the uploaded file with a column in any Class yet. Uploading a PFFile is iOS is a two step process:
1) Upload the PFFile to the server
2) In the callback associated the PFFile with a column in a data object
NSData *soundData = [NSData dataWithContentsOfURL:myURL];
parse_Sound = [PFFile fileWithName:#"XXYYZZ"
data:soundData];
[parse_Sound saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
NSLog(#"sound-upload NG”);
} else {
NSLog(#"sound-upload OK");
PFObject *soundStuff = [PFObject objectWithClassName:#"Sounds"];
soundStuff[#"soundFile"] = parse_Sound;
[soundStuff saveInBackground];
}];
}
}];
Now to get the data back you would run a query on the Sounds class that will have the sound data in the soundFile column:
PFQuery *query = [PFQuery queryWithClassName:#"Sounds"];
[query whereKey:#"someKey" equalTo:#"someValue"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
PFFile *soundFile = object[#"soundFile"];
NSData *soundData = [soundFile getData];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
I haven't tested any of this code but it at least demonstrates the steps needed and should get you started.
Here are the examples from the documentation:
PFObject *jobApplication = [PFObject objectWithClassName:#"JobApplication"]
jobApplication[#"applicantName"] = #"Joe Smith";
jobApplication[#"applicantResumeFile"] = file;
[jobApplication saveInBackground];
Then to get the data back:
PFFile *applicantResume = anotherApplication[#"applicantResumeFile"];
NSData *resumeData = [applicantResume getData];
Notice that file is being associated with the applicantResumeFile column of the JobApplication class so that the file data can be retrieved in queries.
You need to keep a reference to that file somewhere (ideally in a column of the PFObject it belongs to).
If you don't keep a reference you're out of luck and you can't retrieve already uploaded files that have no association to any object.
I suggest you read through the Parse documentation for files on iOS https://www.parse.com/docs/ios_guide#files/iOS

NSKeyedUnarchiver unarchiveObjectWithData: in Parse block function never returns

I am requesting a file representation of an NSDictionary from Parse.com (yes I know there are other more obvious ways to store dictionaries) and then in the completion block the data gets unarchived and assigned to a property.
When this code executes it never seems to return from the unarchive line. Nothing freezes, there is no error or exception. The app continues running as though everything is fine. If I set break points at the unarchiver line and the line after it, the first breakpoint gets hit but the second never does.
I can confirm that the function has returned the expected amount of data for this file.
PFFile *definitionFile = appObject[#"myFile"]; //PFFile reference from a PFObject
[definitionFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
if (data) {
//A breakpoint on the following line does fire and show that there is data being given to the unarchiver
self.pendingDefinition = (NSDictionary *) [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
//////Nothing beyond this point gets executed////////
[self handleNewDefinition];
} else {
NSLog(#"ERROR");
}
}];
As it turned out the data was being stored as a plist and I needed to use plist serialization to extract it:
if (data) {
NSError *error;
NSPropertyListFormat format;
NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:&format error:&error];
if(!plist){
NSLog(#"Error: %#",error);
}

NSInvalidArgumentException', reason: 'Can't use nil for keys or values on PFObject. Use NSNull for values.'

Try to create a Parse bases iOS application that saves object in background using a "Share Button" and this code:
- (IBAction)share:(id)sender
{
if (self.chosenImageView.image)
{
NSData *imageData = UIImagePNGRepresentation(self.chosenImageView.image);
PFFile *photoFile = [PFFile fileWithData:imageData];
PFObject *photo = [PFObject objectWithClassName:#"Photo"];
photo[#"image"] = photoFile;
photo[#"whoTook"] = [PFUser currentUser];
photo[#"title"] = self.titleTextField.text;
[photo saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (!succeeded)
{
[self showError];
}
}];
}
else
{
[self showError];
}
[self clear];
[self.tabBarController setSelectedIndex:0];
}
Getting the "NSInvalidArgumentException', reason: 'Can't use nil for keys or values on PFObject. Use NSNull for values.'" Error.
Please Help......
This is a pretty fundamental part of Key-Value Coding (regardless of your use of Parse or not). Please review Apple's documentation on KVC, NSDictionary, and NSArray.
In this particular case, one of these values: photoFile, [PFUser currentUser], or self.titleTextField.text is nil.
You should check your input for validity, like so:
if (photoFile != nil) {
photo[#"image"] = photoFile;
} else {
// handle the case where 'photoFile' doesn't exist.
}

PFObject won't save PFFile PArse

I can't seem to set a Pffile object as a value for a Pfobject key in Objective-C. I'm trying to save NSData from an AVAudioPlayer in a PFfile.
If I do the folllowing:
NSData * audioData=[self.shoutInfoArray objectAtIndex:1];
PFFile * audiofile=[PFFile fileWithName:#"shoutData" data:audioData];
bool saved=[audiofile save]; //This bool is positive, so it does save!?
[shout fetchIfNeeded];
shout[#"audioData"]=audiofile; //BUGGY LINE
I get the following error:
Error: invalid type for key audioData, expected bytes, but got file
(Code: 111, Version: 1.2.20)
Couldn't find why?
Clear your database. I mean drop column audioData. It seams something wrong with types.
To Save a PFFile object as a part of PFObject:Also check file size should not be greater than 10MB.
PFFile * audiofile=[PFFile fileWithName:#"shoutData.aif" data:audioData];
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
//Use async call.
// It is compulsory to save the PFFile object first and then used it with PFObject
if(succeeded)
{
PFObject *shout = [PFObject objectWithClassName:#"UserData"];
shout[#"audioData"] = audiofile;
[shout saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(error)
{
NSLog(#"error %#",error.localizedDescription);
}
else
{
if(succeeded)
{
NSLog(#"object save on parse");
}
}
}];
}
}];

Resources