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];
}
}];
Related
Is there a way to directly send a list of users an image using parse on Xcode? I am able to send the image using a query, but I'm worried that searching through every image that is stored in my class, matching it to the user that sent it, and then retrieving that image is going to take a while and I would like it to be there quickly.
This is the way I am currently saving the images to the database:
PFUser *currentUser = [PFUser currentUser];
NSString * username = currentUser.username;
NSData * imageData = UIImageJPEGRepresentation(myImageView.image, 1.0f);
PFFile * newImageFile = [PFFile fileWithName:#"image.jpeg" data:imageData];
PFObject * newImage = [PFObject objectWithClassName:#"Images"];
[newImage setObject:newImageFile forKey:#"imageFile"];
[newImage setObject:name forKey:#"username"];
[newImage setObject:object forKey:#"sendImageToFollowing"];
[newImage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {}
Saving the image to Parse.com and sending a push that references that image in the push's payload is the way to go, for two reasons:
It's impossible to send more than a few kb via a push message. They are very small by design, so you have to pass a reference to something.
I'm guessing you want the image saved anyways :)
Save the image, get the objectId from Parse, send that as part of the custom push notification payload. You can create a custom dictionary and send it with your push. For more on that, see here:
https://www.parse.com/docs/ios/guide#push-notifications-customizing-your-notifications.
If you want to get the objectId very quickly, you can get it from the query itself when it succeeds:
[newImage saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
//Get the image objectId here
NSString *objectIdString = newImage.objectId;
}
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)
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....!
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.
So I am trying to allow two users to swap files that each has in their Google Drive. That involves knowing the ID of the other person's file and using the API calls to retrieve it. Both files sit in folders that have been shared to anyone/public.
Trouble is when I execute the code below I am finding that each user can only use the downloadUrl corresponding to the file they own - the others return a 404. In this case either "mine" or "theirs" depending on the account I'm logged into.
// _driveService and its authorizer setup elsewhere
// Retrieve the metadata then the actual data
NSString *mine = #"0B4Pba9IBDsR3T1NVTC1XSGJTenc";
NSString *theirs = #"0B4n9OyY8tqWpNlNaN1dUc3FsNG8";
NSString *get = [NSString stringWithFormat:#"https://www.googleapis.com/drive/v2/files/%#",theirs];
[_driveService fetchObjectWithURL:[NSURL URLWithString:get] completionHandler:^
(GTLServiceTicket *ticket, GTLDriveFile *file, NSError *error)
{
if (error != nil)
NSLog(#"Error retrieving metadata: %#", error);
else
{
// Download the actual data
GTMHTTPFetcher *fetcher = [_driveService.fetcherService fetcherWithURLString:file.downloadUrl];
[fetcher beginFetchWithCompletionHandler:^
(NSData *data, NSError *error)
{
if (error != nil)
NSLog(#"Error retrieving actual data: %#", error);
else
{
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Content: %#", content);
}
}];
}
}];
Error retrieving actual data: Error Domain=com.google.HTTPStatus Code=404 "The operation couldn’t be completed. (com.google.HTTPStatus error 404.)"
What am I doing wrong here? If it's a permissions thing, why am I allowed to get the metadata?
Note this is for an iOS app and both files were created and uploaded from the app using the official client API (rev 353).
Hah, so it seems the devil is in the detail I left out of the question. When creating the authorizer the scope I was providing is kGTLAuthScopeDriveFile, which was the default in an example and I forgot all about it when everything else thus far worked fine. Apparently I need to use kGTLAuthScopeDrive instead (the differences are explained here)
The logic seems a bit flawed here though, I mean I don't want access to other files that weren't created with the app, I just want access to a public file somebody else created with the app...