Getting Parse PFUser: relational data - ios

I have a Parse class "Posts". "Posts" has a column, "user" which stores the user that made the post. It is of type Pointer<_User>.
I want to be able to retrieve the user, and then get an image stored in the User class.
So far I have:
PFQuery *postQuery = [PFQuery queryWithClassName:#"Post"];
[postQuery includeKey:#"user"];
PFObject *result = [postQuery getFirstObject];
if (result) {
NSLog(#"successful query"); //Successful!
} else {
NSLog(#"unsuccessful query");
}
PFUser *user = result[#"user"];
if (user) {
NSLog(#"successful found user");
} else {
NSLog(#"unsuccessful found user"); //Not successful
}
I successfully get "result", but I no "user". What am I missing?

In this case, the User object pointed to had been deleted. If a pointer column is included and it resolves to a non-existing object, it won't fail, but the object won't be fetched/included either. You can probably detect this scenario after query w/ includeKey by checking for the createdAt field.

Related

Querying for existing nodes in Firebase is returning null

My tree structure looks like in FireBase
BOOKS
USERS
UserA_ID
UserB_ID
UserC_ID
UserName:#""
UserEmail:#""
UserBookReadCount:#""
Before I go and update the 'BookReadCount', I try to query for the existence of this user using the following code. In the
FQuery* query = [[userRef queryOrderedByKey] queryEqualToValue:userIDKey];
[query observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot)
{
NSLog(#"Snapshot value: %#", snapshot.value);
if(snapshot.hasChildren)
{
// UserX_ID exists. Use 'updateChildValues' to update the data
}
else
{
// UserX_ID does NOT exist. Use 'setValue' to create the data
}
}];
If the user does not exist I then use 'setValue' to add a new user to this list. If the user exists then I use 'updateChildValues' to update the users data. All this is working fine.
The problem I'm facing is with the queries. Even if the user exists or doesn't exist, the above query always returns null.
Any thoughts on something obvious that I'm missing ?
A couple of things. To test for null you should really
if (snapshot.value == [NSNull null])
Here's a slightly modified implementation of your code that includes the NSNull check.
Firebase *ref = [self.myRootRef childByAppendingPath:#"users"];
FQuery *query = [[ref queryOrderedByKey] queryEqualToValue:#"UserC_ID"];
[query observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
if (snapshot.value == [NSNull null]) {
NSLog(#"was null");
} else {
NSLog(#"key: %# %#", snapshot.key, snapshot.value);
}
}];
I set up a test Firebase with the same structure you are using and it works correctly; returns null for nonexistent user id's and returns the node UserC_ID when it does.
So maybe your code that sets the userIDKey is misbehaving or perhaps your userRef is wonky.

How to enable user to add friends via friend's username and display user's friend list in tableview with swift

I'm currently developing iOS App with Swift that users can add friends via username in one view and display user's friend list in another tableview,I'm currently using parse and I'm able to get my app to let user sign up and log in.
I want to know the code of adding friends via username and display user's friend list with parse,
I've tried looking for this solution and I got nothing but how to get friend list from Facebook which is not related to my app .
Any help is appreciated and Let me know if you need any additional information! ( sorry for my bad english).
You need to use the FBSDK for iOS in order to make a graph request for the Facebook friends list.
I don't quite understand if you want to add a "friend" that already is registered in your app or that has a Facebook account, but in any case you'll need to store your users in a data store, i believe Parse has as PFObject which you can save like this:
var appUser = PFObject(className:"AppUser")
appUser["userFullName"] = "John Doe"
appUser["userFacebookID"] = 1
appUser["userEmail"] = "j.doe#doe.com"
appUser.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
From there what you need is to associate the users, with a "Friendship" object for example, like this:
var userFriendship = PFObject(className:"Friendship")
appUser["invitedUserEmail"] = "jane.doe#doe.com"
appUser["invitingUserEmail"] = "j.doe#doe.com"
appUser["invitationStatus"] = "pending"
appUser.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}
After that you can update the objet in order to change the invitation status to "accepted" or "denied, "cancel" etc.
To get a the list of user invitations and friends you need to make a ParseQuery with the arguments you're looking for, such as:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"invitingUserEmail = 'j.doe#doe.com' AND invitationStatus = 'accepted'"];
PFQuery *query = [PFQuery queryWithClassName:#"Friendship" predicate:predicate];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d friends.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
You can find a lot more in the Parse Docs here https://parse.com/docs/ios/guide

Check for unique phone number before allowed to sign up with parse ios

I've integrated my app with the parse framework and have added a few extra fields during the sign up process, one of those being phone number. I'd like to check to make sure the phone number entered is unique prior to them being allowed to sign up, like parse does for username and email. However, I can't find documentation anywhere on how to do so.
I've tried running a query on PFUser to check if the entered phone number is in the Users table but the query won't work (results are always nil) unless its included in the signUpInBackgroundWithBlock of code. However, putting the query here still uploads the info to parse no matter the results of the query. Has anyone found a way to do this? Thanks in advance!
Would be helpful if you share your query but something like the following should probably do the trick:
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"phoneNo" equalTo:#"0123456"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
if (objects.count == 0) {
//Allow Number to be saved
} else {
//Give warning that the number is already in use
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
You can add a "beforeSave" trigger that validates the phone number uniqueness. The Parse.com documentation has some good examples.
Parse.com documentaion

parse.com / iOS - Is there a way to download the whole class object in json format?

I am testing parse.com and I am creating an app that needs to work offline.
I would like to download the whole class object in json format in order to save it locally and access the data.
I cannot find any documentation on how to do it using Swift/iOS.
I successfully get the object with a specific ID using:
var query = PFQuery(className:"TestObject")
query.getObjectInBackgroundWithId("DuZIpSgjnt") {
(TestObject: PFObject?, error: NSError?) -> Void in
if error == nil && TestObject != nil {
println(TestObject)
} else {
println(error)
}
}
but not the whole class.
What I would like to retrieve is the same json response that I get using REST Api from curl:
https://api.parse.com/1/classes/GameScore
that returns all my objects in json:
{"results":[{"cheatMode":false,"createdAt":"2015-05-15T15:55:17.782Z","objectId":"IKCmjRVet9","playerName":"Sean Plott","score":1337,"updatedAt":"2015-05-15T15:55:17.782Z"},{"cheatMode":false,"createdAt":"2015-05-15T16:05:51.688Z","objectId":"UOKOZkxaGl","playerName":"Marco xxx","score":9998,"updatedAt":"2015-05-15T16:05:51.688Z"}]}
any help appreciated.
Thank you!
If you want to store data persistently, I suggest that you first enable the local datastore, as described below:
Local datastore for Parse iOS
So the operation is basically a fetch then a pin. A fetch looks like:
PFQuery *query = [PFQuery queryWithClassName:#"YourCustomClassName"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *err) {
if (!err) {
// Do something with your objects, e.g. pin them for later access.
}
}];
Pinning just means parse is now storing the pinned data for you locally. you can fetch them later locally by marking you query as such:
[query fromLocalDatastore];

iOS Parse validating user

I want to write code in AppDelegate.m that checks if PFUser.currentUser credentials are still valid. The reason I am doing this is for this scenario. Assume user logged in successfully and now currentUser has the basic information of that user. If the user changes the password at some time, when application launch, it should check if those credentials are up to date or not. If email & password doesn't match the one in table, it should log the user out.
I tried to do this but apparently PFUser.currentUser.password is always set to null while PFUser.currentUser.email has the actual value of email used to log in. How can I achieve this validation without the password being accessible?
Here is the code I have for guidance:
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"objectId" equalTo: PFUser.currentUser.objectId];
[query whereKey:#"password" equalTo: PFUser.currentUser.password];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!object) {
/*Credentials changed! Logout user and request login again*/
NSLog(#"Credentials Changed!");
[PFUser logOut];
[[[navigationController viewControllers] objectAtIndex:0] performSegueWithIdentifier:#"loginSegue" sender:self];
}
else {
/*Credentials are still valid..proceed!*/
NSLog(#"Credentials Correct!");
[[[navigationController viewControllers] objectAtIndex:0] performSegueWithIdentifier:#"skipLoginSegue" sender:self];
}
}];
When this failed I tried to NSLog the password and got null so I understood that this was the problem.
I would write a cloud code function using the master key: Parse.Cloud.useMasterKey();
When you include this in your function you will be able to access/check the user's password and send back a "verified" variable or something of that sort.
Here is my cloud code function to modify a user, but you can easily modify it to verify a user's info. There are also many answers on the parse forums on this as well as lots of info in the docs.
Parse.Cloud.define("editUser", function(request, response) {
//var GameScore = Parse.Object.extend("SchoolHappening");
// Create a new instance of that class.
//var gameScore = new GameScore();
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.User);
query.get(request.params.myUser, {
success: function(myUser) {
// The object was retrieved successfully.
myUser.set("cabinetPosition", request.params.myPosition);
// Save the user.
myUser.save(null, {
success: function(myUser) {
// The user was saved successfully.
response.success("Successfully updated user.");
},
error: function(gameScore, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
response.error("Could not save changes to user.");
}
});
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
});
If you want to verify your current password, you can store your password locally, then use loginWithUsernameInBackground: to verify. Something like,
[PFUser logInWithUsernameInBackground:[PFUser currentUser].username
password:password
block:^(PFUser *user, NSError *error) {
}];

Resources