Parse: Error 102 when querying an array of pointers - ios

I've just started using Parse, its been fairly easy to understand until I got to the more advanced aspects such as saving an array of pointers.
I read on a Parse forum that the best way to do this was to save a JSON object to the server that defined an array of types, for example I have an array of users:
[
{
"__type":"Pointer",
"className":"_User",
"objectId":"YkIlOohXZV"
},
{
"__type":"Pointer",
"className":"_User",
"objectId":"MBe4g6t8av"
}
]
This information gets saved to Parse just fine, however when I come to query the data I never get a Parse error 102.
I have tried:
let myRequestsQuery = PFQuery("Requests")
myRequestsQuery.whereKey("recipients", containsString: PFUser.currentUser().objectId!)
Why am I getting an error 102? Is there a better way to search the array or do I have to query everything back from the server?

The problem is that you are comparing a pointer to a string, instead of to another parse object.
I work with parse on Android so I'm not sure about the syntax, but you should use a whereKey and equalTo the parse user itself (PFUser.currentUser()) and not a string lookup on it's ID since it's not stored as a string. When arrays are concerned, equalTo searches inside the for a match.
I don't know Swift, but I think it should look something like this in Obj-C:
PFQuery *query = [PFQuery queryWithClassName:#"Requests"];
[query whereKey:#"recipients" equalTo:[PFUser currentUser]];
Again... please excuse any syntax errors...
P.S. just to be sure, here's a working Java code snippet I use in my app:
ParseQuery<ParseObject> query = ParseQuery.getQuery("Requests");
query.whereEqualTo("recipients", ParseUser.getCurrentUser());

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

Firebase iOS FQuery by child value is failing intermittently

I am using Firebase iOS framework. Don't see an obvious way to tell the build version I am using. I am testing on an iOS simulator usually.
I implemented a helper function to find certain data by indexed key. I have created indexes on these keys in the security rules, but that should not be necessary. My data and query are very similar to the dinosaur query by height example in the firebase docs. My data is flattened to //{key: value, key: value, ...}. E.g., Player/-JwISoamh_jRhYeKvKLk will contain a dictionary like:
{
"height": "1.89",
"firstName": "LeBron",
"lastName": "James"
}
I use my code to find all players with lastName === "James". So, in the code below the ObjBase would point to /Player and childKey = "lastName" and value="James". I expect querySnaphot to contain 1 child with the node for "JwISoamh_jRhYeKvKLk".
Even though the data is in my test data in my app on firebaseio.com and the values match up, this code returns me a querySnapshot.childrenCount == 0:
FQuery *query = [[objBase queryOrderedByChild:childKey] queryEqualToValue:value];
[query observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *querySnapshot) {
Until I change the FEventTypeValue to FEventTypeChildAdded. At that point I get data, but it is not an array of nodes in the querySnapshot.children, but values. As soon as I change the code back to FEventTypeValue my query, exactly as it was before, will work for some period of time (multiple runs of the app). Possibly it stops working when I nuke the data for a bunch of new changes. Maybe it is being cached locally after that initial load.
Perfectly willing to believe I am doing something wrong, but I cannot see what and the fact that it works after I "seed it" with the ChildAdded leads me to think it is an async issue or some initial setup I need to do.
Thanks!!

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 do I get a server timestamp from Firebase's iOS API?

I have an iOS app that uses Firebase and currently has a few dictionaries with keys that are NSDate objects. The obvious issue with this is that NSDate draws from the device's system time, which is not universal.
With that, what's the best way to get a server timestamp (similar to Firebase.ServerValue.TIMESTAMP for the Web API) using Firebase's iOS API so that I can sort my dictionary keys chronologically?
I'm also aware of the chronological nature of IDs generated by childByAutoID, but I can't figure out the proper way to sort these in code. While they may be returned in chronological order, any time something like allKeys is called on them, the order goes out the window.
Any help with this issue would be greatly appreciated!
Update: In Firebase 3.0 + Swift, you can use
FIRServerValue.timestamp(). In Objective-C this is [FIRServerValue timestamp].
In Swift, you can now use FirebaseServerValue.timestamp() with Firebase 2.0.3+ (before 3.0).
The equivalent for Firebase.ServerValue.TIMESTAMP in iOS is kFirebaseServerValueTimestamp. Right now, this only works for Objective-C and not Swift.
In Swift, you can create your own global timestamp with
let kFirebaseServerValueTimestamp = [".sv":"timestamp"]
and then you'll be able to use kFirebaseServerValueTimestamp in the same way.
But you can only use this as the value or priority of a node. You won't be able to set it as the key name (although, I don't believe you could in the Web API either).
In general, calling allKeys on a dictionary does not guarantee order. But if you're using childByAutoID at a node, you can get back the right order by ordering the NSArray returned by allKeys lexicographically. Something like this would work:
[ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSDictionary *value = snapshot.value;
NSLog(#"Unsorted allKeys: %#", value.allKeys);
NSArray *sortedAllKeys = [value.allKeys sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"Sorted allKeys: %#", sortedArray);
}];
This is similar to sorting an NSArray alphabetically, but when sorting the auto-generated IDs, you do not want localized or case insensitive sort, so you use compare: instead of localizedCaseInsensitiveCompare:
Caveat: Seems like the timestamp is added AFTER your object is persisted in Firebase. This means that if you have a .Value event listener set up on the location your object is persisted to, it will be triggered TWICE. Once for the initial object being stored in the location, and again for the timestamp being added. Struggled with this issue for days :(
Helpful information for anyone else who can't figure out why their event listeners are triggering twice/multiple times!
As of Firebase 4.0 you can use ServerValue.timestamp()
for example:
let ref = Database.database().reference().child("userExample")
let values = ["fullName": "Joe Bloggs", "timestamp": ServerValue.timestamp()] as [String : Any]
ref.updateChildValues(values) { (err, ref) in
if let err = err {
print("failed to upload user data", err)
return
}
}
You can get Time Stamp using FIRServerValue.timestamp().
But, Because of FIRServerValue.timestamp() listener is called two times. Listener will be called two times.

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