Difference between uploading PFFile and PFObject - ios

Somebody could explain me what happens when I upload something to Parse like this:
PFFile *imgFile = [PFFile fileWithName:#"Img.jpg" data:imgData];
[imgFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
} else {
}
}];
If I use this solution where will be the uploaded file? In which class? How can I retrieve it? I'm a bit confused, because I'm using another solution in my projects, but this version would be better because PFFile can be saved with progressBlock.
This is the other way that I'm using, in this case it's obvious the class where I upload it.
PFFile *imgFileObject = [PFFile fileWithData:imgData];
PFObject *photo = [PFObject objectWithClassName:#"ImgClass"];
photo[#"image"] = imgFileObject;
[photo saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
...
} else {
...
}
}
}];
What's the difference between the two solution in practice?

case is using parse as a remote data storage.. kinda like treating parse as your iCloud drive or drobpox. you save a FILE
case is using parse as a remote database.. you don't save a FILE but make a new 'row' in the database (think of it as iCloud KV Store)
=> so PFFile uploaded as remote file (not a db entry thus it has no class)
=> PFObject is a database entry (that can then link to files or even contain data itself)

Related

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

Storing Data on Parse for inApp purchase

In an iOS app, I am trying to use Parse.com in a new(for me) way. I want to store data that is going to be made available to the user once an inApp purchase has been made.
I have gotten to the point I can put a dummy file on the server and get it into my app, once the purchase is done. But here comes my questions:
What kind of file am I supposed to upload on Parse.com? By trial and error I have noticed that a simple text (.txt) file did not work, on the other hand .png or .zip appears to be working extensions. The contents seems irrelevant. I have also read that it has to be one file. So if I want my user to get some text and some sound and a picture, I obviously have to package this into one file and unpackaged it after download. Am I supposed to put a .zip file or some other compressed format? And then how do I uncompress this after download? For example, say I want the user to get:
3 text files: A.txt, B.txt, C.xml
2 sound files: S1.mp3,S2.mp3
3 graphic files: Gr1.png,Gr2.png,Gr3.jpeg
How would I go? Some sample code or tutorial indication would be welcome.
Thanks for any help.
As per the docs you can save any binary data as long as the file doesn't exceed 10MB.
"The most common use case is storing images but you can also use it for documents, videos, music, and any other binary data (up to 10 megabytes)."
Below is an example of uploading three text files. For simplicities sake I have a class called TestClass with columns of type File named File1, File2, and File3.
For each file we will use the saveInBackgroundWithBlock method, so we know when our file has saved successfully and we can associate it with the proper TestClass column.
PFObject *testClass = [PFObject objectWithClassName:#"TestClass"];
NSData *data = [#"This is the first text file!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:#"file1.txt" data:data];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
testClass[#"File1"] = file;
[testClass saveInBackground];
}
}];
data = [#"This is the second text file!" dataUsingEncoding:NSUTF8StringEncoding];
file = [PFFile fileWithName:#"file2.txt" data:data];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
testClass[#"File2"] = file;
[testClass saveInBackground];
}
}];
data = [#"This is the third text file!" dataUsingEncoding:NSUTF8StringEncoding];
file = [PFFile fileWithName:#"file3.txt" data:data];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
testClass[#"File3"] = file;
[testClass saveInBackground];
}
}];

Can two PFObjects point at each other?

I have two PFObject, I'll call them A and B for simplicity.
I create A and save it.
I create B, set a property on it called "a" pointing to A and save B
I then create a property on A called "b" and then save A
When I try to do that something does not seem to work right, I am trying to understand if it's something else in my code or if Parse does not allow me to have two PFObjects that point at each other.
Can two PFObjects point at each other?
They can indeed. I am assuming that you're not waiting for the save to complete, and it fails because of that.
If you create A and save it, create B pointing to A and save B, then you can point to B on A and save that too. The saves must be complete though, in between operations.
Regardless, it's not a recommended approach.
PFObject *post = [PFObject objectWithClassName:#"Post"];
PFObject *comment = [PFObject objectWithClassName:#"Comment"];
[post saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
[comment setObject:post forKey:#"post"];
[comment saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
[post setObject:comment forKey:#"comment"];
[post saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
// Both objects should be linked now.
}];
}];
}];
They can't (as far as I know).
I came across the same issue. My workaround was to have B store the objectId of A, and A store a pointer to B.

Caching PFFile data from Parse

My app is a messaging style app and in it you can "tag" another user. (A bit like twitter).
Now, when this message is displayed, the avatar belonging to the person(s) who was tagged is displayed with that message.
The avatar of the user is stored as a PFFile against the PFUser object.
I'm loading it something like this...
PFImageView *parseImageView = ...
[taggedUser fetchIfNeededInBackgroundWithBlock:^(PFObject *user, NSError *error) {
parseImageView.file = user[#"avatar"];
[parseImageView loadInBackground];
}];
This all works fine.
The load if needed part of the code will most of the time not touch the network as for the majority of the time it has the user data cached.
However, the load in background part that gets the image and puts it into the image view runs every single time. There doesn't seem to be any caching on the PFFile data at all.
Even after downloading the same user's avatar numerous times it still goes to the network to get it.
Is there a way to get this data to cache or is this something I'll have to implement myself?
PFFile will automatically cache the file for you, if the previous PFQuery uses caching policy such as:
PFQuery *query = [PFQuery queryWithClassName:#"MyClass"];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
To check whether the PFFile is in local cache, use:
#property (assign, readonly) BOOL isDataAvailable
For example:
PFFile *file = [self.array objectForKey:#"File"];
if ([file isDataAvailable])
{
// no need to do query, it's already there
// you can use the cached file
} else
{
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error)
{
if (!error)
{
// use the newly retrieved data
}
}];
}
Hope it helps :)
In the end I created a singleton with an NSCache and queried this before going to Parse.
Works as a quick stop for now. Of course, it means that each new session has to download all the images again but it's a lot better now than it was.
You can cache result of PFQuery like below code..And need to check for cache without finding objects in background everytime..while retrieving the image.It has some other cache policies also..Please check attached link also..
PFQuery *attributesQuery = [PFQuery queryWithClassName:#"YourClassName"];
attributesQuery.cachePolicy = kPFCachePolicyCacheElseNetwork; //load cache if not then load network
if ([attributesQuery hasCachedResult]){
NSLog(#"hasCached result");
}else{
NSLog(#"noCached result");
}
Source:https://parse.com/questions/hascachedresult-always-returns-no
Hope it helps you....!

How to save a basic Core Data store to Parse.com?

I have looked and looked in Parse docs, SO and Google, and can not find an example of storing a plain ol' Core Data SQLite file to Parse.com. Initially I just want to store the Core Data file as a backup; eventually I want to add FTASync and then ability for others to utilize the stored Core Data file from this iOS app.
Is there an example of doing this without using a PFObject? Can someone point me to a place in the Parse docs where I can find out how to do this?
No, you cannot do this without any PFObject. Theoretically you can save backups just with
- (void)createBackupFromSQLiteStorageAtPath:(NSString*)path
{
NSString *name = [[NSDate date] description]; // for example, stringified date will act as name
PFFile *backup = [PFFile fileWithName:name contentsAtPath:path];
[backup saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (error)
{
// handle
}
else
{
// success
}
}];
}
But! If you want to access it from parse's fileserver you'll need to keep PFFile objects somehow (you can also store PFFile's url property - but it's hack) - and here's the case where PFObject comes to help. Assuming you have backed up your store already:
- (void)storeBackupFile:(PFFile*)file
{
PFObject *backup = [PFObject objectWithClassName:#"Backup"];
[backup setObject:file forKey:#"file"];
[backup setObject:[PFUser currentUser] forKey:#"user"];
[backup saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error)
{
if (error)
{
[backup saveEventually];
}
else
{
// success
}
}];
}
So after this you'll have Backup object in parse database, with link to backup file and user that created backup.
Some more considerations:
1) It's good to organize such backup as NSOperation subclass.
2) It's bad idea to store backups with Parse in such way. File storage on Parse is very expensive resource. Also, PFFile has local cache - your storage will be duplicated each time you make backup, so app's size will increase dramatically with often backups.

Resources