Parse unpinFromBackground doesn't work - ios

I'm currently using Parse to save details about users in an app. When the ViewController is loaded for the first time, the user data is retrieved from the server. When the user leaves the ViewController, I use the code [user pinInBackground] to save user variables to the local datastore. When the ViewController is reloaded later, the user variables are set using the data from the local datastore. This works correctly in my app.
However, when the user leaves to a specific view controller, on returning to the original ViewController, I wish for the app to access the data from the server instead of the local datastore. To do this, I use the code:
[user fetchFromLocalDatastoreInBackgroundWithBlock:^(PFObject *object, NSError *error) {
[user unpinInBackgroundWithName:#"MAINPIN" block:^(BOOL succeeded, NSError *error) {
[user fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
NSLog([NSString stringWithFormat:#"personalHeaderString is equal to %#", user[pfPersonalHeader]]);
}];
}];
}];
The NSLog shows that the code is definitely run as intended. However, the NSLog also shows that the data is not unpinned, and instead the app continues to use data from the local datastore, rather than fetching the data from the Parse server as fetch is supposed to do. How can I fix this?
All help appreciated.

Is there a reason that you are fetching from the local datastore before you unpin? I'm guessing that when you pin the result initially you give it the name "MAINPIN" so why not use:
PFObject.unpinInBackgroundWithName:("MAINPIN")
and then run a your "live" query after after this?
More info here: https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/unpinInBackgroundWithName:
Andrew

Related

Parse.com caching with localDataStore enabled

I have caching problems in the iOS-Parse-SDK and I don't know what is wrong. I'm using the local datastore and it runs smoothly. But sometimes when I call a PFCloud function I don't get the current but an old version of an object or at least some fields of it are old (in my case it is a custom object with the field "status"). The strange thing is if I call the same function in the dev console on the Parse site I get the updated object. Is there anything I can do to prevent caching within the app or at least make sure I get the current version of an object?
Example PFCloud call:
[PFCloud callFunctionInBackground:#"importFriends"
withParameters:#{#"phoneNumbers": numbers}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
DDLogDebug(#"Request Send Numbers SUCCESS: %#", result); //result objects are out of date
if (successBlock) successBlock(result);
}else{
DDLogDebug(#"Request Send Numbers FAIL: %#", error);
if (failedBlock) failedBlock(error);
}
}];
It seem like when you use the localdatastore and custom classes for any object you can't overwrite it's setters otherwise parse will never change it again. Even though the setter did exactly what a normal setter would do just a little bit more math afterwards.

Parse doesn't return PFObject

I have a pointer to an object in my [PFUser currentUser] and when I'm trying to access values inside that object I get internal ObjC exception breakpoint.
this is my code:
PFObject *object = [PFUser currentUser][#"localityData"];
NSString *value = [object objectForKey:#"language"];
I get the error on the second line.
Also, Unless I do
[[PFUser currentUser] fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) { ...
every time I ask for
[[PFUser currentUser] objectWithKey:#"SOMEKEY"]
I get nil in return, why is this happening?
Parse doesn't fetch for you automatically, except a few pieces of data in specific scenarios. Like, when a user logs in the current user will be fetched and will include the standard user data - but it won't include any custom data.
If you fetch an object then that objects details will be downloaded, but the details of related objects won't - unless you specifically request it to be included in the query that you make (using includeKey:).
It's generally best to always use fetchIfNeededInBackgroundWithBlock: if you aren't sure whether the data you need has been obtained yet. Not doing this shouldn't cause a crash but it will generally cause you issues with missing information. It's possible that the crash is related to parse using exceptions for navigation in the framework, but that's a guess and you might want to try contacting parse to see if you can get more details.

Save eventually on PFObject with PFFile (Parse Local Datastore)?

Goal
I am trying to save a PFObject that has a PFFile as an attribute. I am using the new Local Datastore for iOS, so I would like to save this PFObject with the saveEventually() method.
The Problem
The problem I am encountering is that the saveEventually() method doesn't seem to like saving the PFFiles. I tried to saveEventually() my object without any PFFile attached, and that worked fine. As soon as my PFFile was reattached, Xcode threw a couple of breakpoint notices (errors?) but did not terminate the app, and it appears as though all went well - however a check on the Parse Data Browser confirms that the save did not go through. Prior to the Local Datastore feature I don't believe this save would have been possible - it would have thrown the "Unable to saveEventually a PFObject with a relation to a new, unsaved PFFile." error. It seems as though the Local Datastore feature has fixed this, as it states in the iOS Local Datastore docs:
"Pinning a PFObject is recursive, just like saving, so any objects
that are pointed to by the one you are pinning will also be pinned.
When an object is pinned, every time you update it by fetching or
saving new data, the copy in the local datastore will be updated
automatically. You don't need to worry about it at all."
I have updated the SDK to the latest version (v1.6.2). Any ideas?
PFFiles still don't support saveEventually see here
That page was last updated : 2015-01-23
You could pinInBackgroundWithBlock and if successful save the PFFile to a temporary folder in you app bundle and delete it when necessary or unpinned
I just released a class which allows to saveEventually a PFFile.
You can find it here :
/*
This example uses an UIImage, but this works with any file writable as NSData
We begin by writing this image in our tmp directory with an uuid as name.
*/
UIImage *nyancat = [UIImage imageNamed:#"nyancat.jpg"];
NSData *imageData = UIImageJPEGRepresentation(nyancat, 0.5);
NSString *filename = [[NSUUID UUID] UUIDString];
NSURL *fileUrl = [PFFileEventuallySaver fileURLInTmpWithName:filename];
[imageData writeToURL:fileUrl atomically:YES];
/*
We create a PFObject (you can pass an array to below function if you need your file to be saved on several objects). If upload works on first time, do what you want with your file, like linking it on your PFobject.
If saving fails, it'll be retried as soon as network is available, on this session or nexts launches of app.
In that case, the pointer at key kPFFILE_MANAGER_OBJECT_FILE_KEY of your PFFObject will be set with the PFFile, then saved eventually within PFFileEventuallySaver
*/
PFObject *object = [PFObject objectWithClassName:kPFFILE_CONTAINER_OBJECT_CLASSNAME];
[[PFFileEventuallySaver getInstance] trySaveobjectAtURL:fileUrl associatedObjects:#[object] withBlock:^(PFFile *file, NSError *error) {
if(!error)
{
NSLog(#"[First try, network is fine] File saved, saving PFObject");
object[kPFFILE_MANAGER_OBJECT_FILE_KEY] = file;
[object saveEventually];
NSLog(#"Try again disabling your network connection");
}
else
{
NSLog(#"No network, connect back your wifi, or relaunch app. Your file will be sent");
}
} progressBlock:^(int percentDone) {
NSLog(#"[First try, network is fine] Sending file %d/100%%", percentDone);
}];

How to update an object in Parse

So I am trying to create an iOS wait time app for an amusement park that utilizes parse.com to sync data across all instances of the app. What I want to happen is when an IBAction is run, an object will update with the new wait time. Here is the code I have in that IB Action.
PFQuery *query = [PFQuery queryWithClassName:#"GrandCarousel"];
[query getObjectInBackgroundWithId:#"9Fo5FBABpL" block:^(PFObject *updatedWaitTime, NSError *error) {
updatedWaitTime[#"waitTime"] = localGrandCarouselWaitTime;
[updatedWaitTime saveInBackground];
}];}
Thats the only code in that method. Whenever I run the method, the app crashes why?! Here is a picture of that class in the parse data browser: http://s8.postimg.org/y9ti6dzxx/Screen_Shot_2014_07_12_at_6_20_22_PM.png
(It won't let me add a picture directly yet) As you can see, I am very new to parse and pretty new to objective C. Any help would be awesome!

Can parse cache replace core data

I'm building a simple contact directory with CRUD functions, and I want to have editing functionality offline. My database for the contacts will be stored on a Parse.com backend. Would this be possible using the Parse cache functionality, or would it be better for me to resort to using Core Data?
No, it can't. Parse simply caches the results of your queries. This is extremely limiting and not the same as caching all of your PFObjects. For example, say you ran a query for all of your "contacts" PFObjects. The only thing you would be able to do with the cache is run the exact same query again later (and get the exact same result). You couldn't even query for a subset of that cached data.
Another issue is the cached objects will not be updated with any changes your user is making to your PFObjects. For example, say a user is editing contacts offline and your code is calling saveEventually to save those changes to Parse when possible. When you get a cached query result the user's changes will not be reflected in your PFObjects. The same goes for deleted and added PFObjects I believe. This would make offline use terrible. Here is a thread from the Parse forum that touches on this subject:
https://www.parse.com/questions/does-saving-an-object-supposed-to-update-the-local-cache-of-a-query-on-these-objects
I have seen Parse developers mention improved caching of Parse objects coming at some point in the future. However, this feature does not exist yet and there is no knowing when it will come. In the mean time, if you want to support offline use you have to use Core Data or some other local store. FTASync (which I have never used) is designed to sync Parse objects with Core Data:
https://github.com/itsniper/FTASync
You could also write your own code to sync the two sets of data.
The Parse iOS/OSX SDK provides a local datastore which can be used to store and retrieve PFObjects, even when the network is unavailable. To enable this functionality, add libsqlite3.dylib and call [Parse enableLocalDatastore] before your call to setApplicationId:clientKey:.
As stated in the Parse documentation:
You can store a PFObject in the local datastore by pinning it. Pinning
a PFObject is recursive, just like saving, so any objects that are
pointed to by the one you are pinning will also be pinned. When an
object is pinned, every time you update it by fetching or saving new
data, the copy in the local datastore will be updated automatically.
You don't need to worry about it at all.
PFObject *gameScore = [PFObject objectWithClassName:#"GameScore"];
gameScore[#"score"] = 1337;
gameScore[#"playerName"] = #"Sean Plott";
gameScore[#"cheatMode"] = #NO;
[gameScore pinInBackground];
If you have multiple objects, you can pin them all at once with the
pinAllInBackground convenience method.
[PFObject pinAllInBackground:listOfObjects];
Retrieving an object from the local datastore works just like
retrieving one over the network. The only difference is calling the
fromLocalDatastore method to tell the PFQuery where to look for its
results.
PFQuery *query = [PFQuery queryWithClassName:#"GameScore"];
[query fromLocalDatastore];
[[query getObjectInBackgroundWithId:#"xWMyZ4YE"] continueWithBlock:^id(BFTask *task) {
if (task.error) {
// Something went wrong.
return task;
}
// task.result will be your game score
return task;
}];
Any PFQuery can be used with the local datastore just as with the
network. The results will include any object you have pinned that
matches the query. Any unsaved changes you have made to the object
will be considered when evaluating the query. So you can find a local
object that matches, even if it was never returned from the server for
this particular query.
PFQuery *query = [PFQuery queryWithClassName:#"GameScore"];
[query fromLocalDatastore];
[query whereKey:#"playerName" equalTo:#"Joe Bob"];
[[query findObjectsInBackground] continueWithBlock:^id(BFTask *task) {
if (task.error) {
NSLog(#"Error: %#", task.error);
return task;
}
NSLog(#"Retrieved %d", task.result.count);
return task;
}];
When you are done with an object and no longer need it to be in the
local datastore, you can simply unpin it.
[gameScore unpinInBackground];
There's also a method to unpin several objects at once.
[PFObject unpinAllInBackground:listOfObjects];
For more information on using Parse's local datastore check the Local Datastore documentation provided for iOS/OSX on parse's website.
No it can not. It's no where near the same, I suggest https://github.com/itsniper/FTASync

Resources