Why PFRelation does not support fromLocalDataStore queries? - ios

I have a PFUser object with several PFRelation pointing to other objects. When I run the following code:
PFRelation *relation = [[PFUser currentUser] relationForKey:#"Relation"];
PFQuery *query = [relation query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
NSLog(#"%#", objects);
}];
it works fine. However, when I want to do the same from the local data store:
PFRelation *relation = [[PFUser currentUser] relationForKey:#"Relation"];
PFQuery *query = [[relation query] fromLocalDataStore]; // !!!
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
NSLog(#"%#", objects);
}];
it returns nothing, despite the fact I've pinned both my user and related objects with [myObject pinInBackground].
Why PFRelation queries does not support fromLocalDataStore? What I'm doing wrong?

I've created a bug report for this issue which is
escalated to the engineering team to investigate further
So it looks like it's a bug
In the meanwhile, I've converted all my PFRelations to arrays of pointers to related PFObjects. Not as fancy as PFRelation, but works fine with local data storage.
Update: parse solved the issue in Parse SDK v.1.7.3

It's a Parse bug. It can be also reproduced with Android SDK. I'm not sure on iOS, but on Android I have found a way how to make it work.
You need to have a Pointer in "related" classes to the ParseObject which has Relations.
In your case, you have a PFUser which has several PFRelations. If you add a PFPointer to your PFUser in "related" classes, query from the local datastore would work just fine.
Or for the case from my link above - If Comment class would have a Pointer to a Post class everything would work fine.

Have the same problem in parse 1.7.5 (release notes mentions that they fixed this bug, but still not working for me)
You can make it working with executing separate query for relation you have for each pinning object (maybe there is a way to do it with all objects at once?). And then pin results of relation query.
After that querying from local datastore starts working fine.

Related

How to find all classes on parse core database?

I have multiple classes on my app project on parse and I was wondering if there is a way where I can get ALL the class names with a query on objective-c?
Below is the code I have.
PFQuery *query = [PFQuery queryWithClassName:#"ClassNameID"];
[query getObjectInBackgroundWithId:#"key" block:^(PFObject *key, NSError *error) {
//code goes here
}];
ClassNameID is the classname on my parse database, but I want the user to select its own database that I have on parse, how do I get all the classnames?
You have to use REST Schema API to get all of your Class details in your app instead of a query.
From Parse blog: http://blog.parse.com/announcements/releasing-the-schema-api/
Documentation: https://www.parse.com/docs/rest/guide#schemas

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.

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

Ordering a query using SUPQuery on iOS

I know there is a way to define the order we want things in a query, but although I know it, I am not able to use it. In my example I have the following:
SUPQuery *query=[SUPQuery getInstance];
[query select:#"s.fname, s.lname"];
[query from:#"Sales_order" :#"s"];
[query orderBy:#"s.lname" : 0];
The input paramenters for the orderBy are:
[query orderBy:(SUPString*) :(SUPInt)];
I don't know what else to try for those two fields, does anyone has some clue? Thanks.
P.S: Do you know where I could find a resource center/blog, etc where I could see some examples implemented.
I was able to figure it out:
[query orderBy:#"s.lname" : [SUPSortOrder DESCENDING]];

Resources