How to reload a relationship on an existing parse object - ios

I have a bunch of objects from a PFFetch in an array. Those objects have links (pointer array <-> pointer) to other object. To limit the amount of data sent for my initial fetch I don't want to download all of the linked objects using includeKey on the fetch.
Sometime later, I have a subset of these objects and I do want to fetch the related objects and relations of those objects. If I fetch them again, I duplicate the objects in my app (and presumably needlessly send objects over the wire that I already have in my app)
I.e. I would like some of my objects in my original array to appear as if the original fetch had:
[query includeKey:#"relationship"];
[query includeKey#"relationship.secondRelationship"];
set on the original key. What is the best way to do this? I had imagined some API on PFObject like:
+(void) fetchIfNeededInBackground:(NSArray*)objects includeKey:(NSString*)key block:...
or
- (void)includeKeyAtNextFetch:(NSString*)key
But I can't find anything like this.

it's POSSIBLE you're after the famous containedIn query..
here's an example of using containedIn to address the famous problem "match friends from FB"....
+(void)findFBFriendsWhoAreOnSkywall
{
// issue a fb graph api request to get the fbFriend list...
[APP huddie];
[FBRequestConnection
startForMyFriendsWithCompletionHandler:^(
FBRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
// here, the result will contain an array of the user's
// FB friends, with, the facebook-id in the "data" key
NSArray *fbfriendObjects = [result objectForKey:#"data"];
int kountFBFriends = fbfriendObjects.count;
NSLog(#"myfriends result; count: %d", kountFBFriends);
// NOW MAKE A SIMPLE ARRAY of the fbId of the friends
// NOW MAKE A SIMPLE ARRAY of the fbId of the friends
// NOW MAKE A SIMPLE ARRAY of the fbId of the friends
NSMutableArray *fbfriendIds =
[NSMutableArray arrayWithCapacity:kountFBFriends];
for (NSDictionary *onFBFriendObject in fbfriendObjects)
[fbfriendIds addObject:[onFBFriendObject objectForKey:#"id"]];
for (NSString *onef in fbfriendIds)
NSLog(#"and here they are .. %#", onef);
// query to find friends whose facebook ids are in that list:
// USE THAT SIMPLE ARRAY WITH THE MAGIC 'containedIn' call...
// amazingly easy using the ever-loved containedIn:
// amazingly easy using the ever-loved containedIn:
// amazingly easy using the ever-loved containedIn:
PFQuery *SWUSERSTOADDASFRIENDS = [PFUser query];
[SWUSERSTOADDASFRIENDS whereKey:#"fbId" containedIn:fbfriendIds];
[SWUSERSTOADDASFRIENDS findObjectsInBackgroundWithBlock:^
(NSArray *objects, NSError *error)
{
if (error) // problem getting the matching-friends list
{
[PFAnalytics .. it al went to hell.];
NSLog(#"disaster at the last step! but that's ok");
[APP.hud hide:YES];
[backTo literallyGoToMainPage];
}
else
{
// so all the people in OBJECTS, now make them in to SW friends.
[PFAnalytics trackEvent:#"FBMatchDone" ...];
NSLog(#"found this many fb matches ... %d", objects.count);
[FBMatch actuallyMakeThemFriends:objects];
[APP.hud hide:YES];
[FBMatch message .. objects.count showTheseNames:objects];
}
}];
}
else // problem getting the friend list....
{
[PFAnalytics .. problem];
[APP.hud hide:YES];
[backTo literallyGoToMainPage];
}
}];
}
I hope it helps!

Related

How to update data in Parse IOS

I am facing an issue when trying to update data in Parse.
Here is my code that i am using.
PFQuery *query = [PFQuery queryWithClassName:#"GameScore"];
[query whereKey:#"name" equalTo:#"Username"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *item, NSError *error)
{
if (!error)
{
[item setObject:#500 forKey:"Score"];
}
[item saveInBackgroundWithBlock:^(BOOL succeeded, NSError *itemError)
{
if (!error)
{
NSLog(#"Score is updated");
}
else
{
NSLog(#"Error in updating: %#",error.description);
}
}];
}
else
{
NSLog(#"Error in getting Score: %#",error.description);
}
}];
This code works only when i create a new PFObject and then try to update it.
But,when i exit my app and then try to update the score again,i am unable to update the data.It throws this error..
Error in getting Score: Error Domain=Parse Code=101 "No results matched the query." UserInfo={error=No results matched the query., NSLocalizedDescription=No results matched the query., code=101}
It works again,if i create a new PFObject.
Please help,i am new to Parse and am still try to understand it.
Thank you.
You need to use findObjectsInBackgroundWithBlock instead of getFirstObjectInBackgroundWithBlock as the latter can only be used if there's at least 1 object.
Reference - Parse Questions
You could also use the PFQuery's count method. If the count is >= 1, use getFirstObjectInBackgroundWithBlock, otherwise display a message / handle that case however you'd like.
Other options include storing the objectId of the GameScore object associated with a player on their user object, creating an object without data using the objectId, then fetching it. Or simply use a pointer, but that can do weird things when saving / querying / fetching.

How can I send a list of array to parse and retrieve it and show it in a UItableView?

I am developing a chat app in iOS .There I need to add friends of the currentUser into an array and then send it to parse and save it there as an array.
As the user should add the their friend only one time and after that I need to do some functionalities there.
So How can I check that the user is already added as friend as well as how can I retrieve the array of friends?
Please if anyone can suggest me something then it will be a great help for me as I am new to iOS and Parse.
You can create a join table on Parse which stores who is friends with who.
With a PFQuery you can then filter it to get only the friends related to a certain person. You could call this table "Friendship", which stores 2 User objects, meaning, they are friends.
Update
You would have a PFObject subclass called Friendship. the Friendship object would hold 2 things, one is the User and the other is the Friend. Both should be PFUser type.
You don't need to go into parse for creating the join table, if you create a new object in code and save it, parse will create a table for it. So if you create the first Friendship object and save it, parse will keep it in it's own table of Friendships.
Saving a Friendship would be just like this:
- (void)addFriend:(PFUser *)friend {
Friendship *friendship = [[Friendship alloc] init];
friendship.user = self.user;
friendship.friend = friend;
[friendship saveInBackground];
}
This is just a simple example.
To fetch a list of friends from the user:
- (void)fetchFriendsList {
//create a query for friendships
PFQuery *friendsQuery = [[PFQuery alloc] initWithClassName:[Friendship parseClassName]];
//filter it to find only friends for the user
[friendsQuery whereKey#"user" equalTo:self.user];
[friendsQuery findObjectsInBackgroundWithBlock:^(NSArray *friendships, NSError *error) {
if (!error) {
//do something with the list of friendships
self.friends = [friendships valueForKey:#"friend"];
//the above line create an array with only the friend value of each Friendship object inside the array
} else {
NSLog(#"error fetching friends: %#", [error description]);
}
}];
}
To find out if the friend is already added/exists:
- (void)friendExists:(PFUser *)friend withCompletionBlock:(void (^)(BOOL))completionBlock {
//create a query for friendships
PFQuery *friendsQuery = [[PFQuery alloc] initWithClassName:[Friendship parseClassName]];
//filter it to find only friends for the user
[friendsQuery whereKey#"user" equalTo:self.user];
[friendsQuery whereKey#"friend" equalTo:friend];
[friendsQuery findObjectsInBackgroundWithBlock:^(NSArray *friendships, NSError *error) {
if (!error) {
//if we get one result here, it means it already exists
BOOL alreadyExists = friendship.count > 0;
if (completionBlock) { //making sure there is a block as parameter
completionBlock(alreadyExists);
}
} else {
NSLog(#"error fetching friends: %#", [error description]);
}
}];
}
You need to have a block in there to treat the results, since making a PFQuery request is not instant.
Also , when adding more whereKey:, they are treated as "AND", which means, it will only return objects that comply with all the whereKeys.
That's basically it. I assume you know how to subclass the PFObject, if not, you can take a look at the documentation.

Separate API-class for Parse backend (IOS)

I am new with Parse and Ios-development.
I develop a ios-app that use Parse as backend.
I have got the main-function to work now, but i have a BIG problem.
I want to create a separate class for my API-handling to Parse. As i set it up now i have my parse-code directly in my view-controllers and as far as i know that not that nice coding.
But, the issue is to handle the background-jobs. Let say if i want to do a GET from the server, this can be done in a background-thread, just using "findObjectsInBackgroundWithBlock"
The problem is when i move this method to a separate API-class. Then my ViewController ask my API-class to get all the objects an the API-class will return it as soon its done. It will nor run in the background, i cant return a NSMutableArray with objects to the viewController until the fetch is done.
I have thinking that i maybe can get the data from parse synchronously in my API-class by using [query findObjects:&error] , as long as i figure out how to create my get-method in the API-class to run asynchronously.
I have try to create my API-method as a asynchronously method using blocks but will not run in background on a separate thread. (I am new to blocks an dont evan no if thats the correct way to crate a method that will run in a separate thread when using it)
Here is my API-method (Class: APIClient)
+ (void) GETAllShoppingGroups:(void (^) (NSMutableArray*))completionBlock{
//Create a mutable array (nil)
NSMutableArray *shoppingGroupsArray = nil;
//Create query for class ShoppingGroupe
PFQuery *query = [ShoppingGroupe query];
//Filter - find only the groups the current user is related to
[query whereKey:#"members" equalTo:[PFUser currentUser]];
//Sort Decending
[query orderByDescending:#"createdAt"];
//Tell Parse to also send the real member-objects and not only id
[query includeKey:#"members"];
//Send request of query to Parse with a "error-pointer"and fetch in a temp-array
NSError *error = nil;
NSArray *tempArray = [NSArray arrayWithArray:[query findObjects:&error]];
//Check for success
if (!tempArray) {
NSLog(#"%#", error);
NSLog(#"ERROR: %#", [error userInfo][#"error"]);
return completionBlock(shoppingGroupsArray);
} else {
//Seccess
shoppingGroupsArray = tempArray.mutableCopy;
completionBlock(shoppingGroupsArray);
}
}
Here is my ViewController Class (Class: ShoppingGruopViewController)
- (void) getAllObjects{
//Init array if nil
if (!self.shoppingGroupeArray) {
self.shoppingGroupeArray = [[NSMutableArray alloc]init];
}
//Remove old objects
[self.shoppingGroupeArray removeAllObjects];
//Get objects
[APIClient GETAllShoppingGroups:^(NSMutableArray* completionBlock){
if (completionBlock) {
[self.shoppingGroupeArray addObjectsFromArray:completionBlock]; }
[self.tableView reloadData];
}];
}

iOS push data to Parse in the 2nd row, not at the top

When I upload any data onto the Parse cloud, it stores the row at the top of the table. So every time a new row is added it gets stored at the top.
However, when I retrieve all the rows, the data is retrieved bottom up approach.
So let's say initially cloud is empty.
Push a
Cloud Looks like : a
Push b
Cloud Looks like : b a
Push c
Cloud Looks like : c b a
And now when I retrieve the data, i get it like: a b c
Now what I want is when data is inserted it is put at the 2nd location and not the first location.
Example:
Initial Cloud : "X"
Push a: "X" a
Push b: "X" b a
Push c: "X" c b a
Is there any way I can push data in Parse like this?
I'm doing because when I retrieve data, I wish to execute a method after all the data is retrieved in the background thread. So this way when I reach at X, I can call my method.
Found a solution to the problem....different approach though:
I count the number of objects for that query
Keep a counter increasing every time a record is fetched
when counter reached = total number of objects , then execute method.
NSInteger count= [query countObjects];
for (PFObject *obj in objects) {
[Names addObject:LastName];
if ([Names count] == count) {
[self getResults];
} }
^^^ solution is wrong
This way apparently does block the main thread, so there's a possibility of the app being killed.
Does anyone have any other solutions?
The data stored in the Parse Cloud is in an arbitrary order. Due to the way they currently store the data you may see new data at the end but this behaviour should not be relied upon.
If you want to retrieve data in a specific order then you should add a sorting operation to your PFQuery rather than trying to store the data in a specific order.
I am not sure what you are trying to achieve with your second code block. What does [self getResults] do?
If you want to execute some code after the results have been retrieved, why not just use findObjectsInBackgroundWithBlock?
This allows you to specify code to be executed once the data is received -
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
names = [NSMutableArray arrayWithArray:objects];
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"Object Name: %#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
If you need to update any UI from the block (or a method called within the block) then you should perform that on the main thread -
dispatch_async(dispatch_get_main_queue(), ^{
[self updateMyUIWithResult:objects];
});
So, putting it together you get -
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateMyUIWithResult:objects];
});
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];

How can I display the contents of a Parse cloud code array?

I have an array in the Data Browser that is supposed to have a list of users who've received an item from a user. The actual content of the tile is
[{"__type":"Pointer","className":"_User","objectId":"3zQoMVRJOx"}]
I can't figure out how to actually call,use, and display this data from Xcode.
My end goal is to be able to find the total number of users who've been sent an item so this is why I need the content from the array. Any help would be great. Im sure it is probably a simple line of code that I'm not seeing.
Your question actually qualifies for a "too broad" flag, as it seems you haven't tried this yourself and are experiencing problems, but are asking us to supply the code for you. It is not just a simple line of code.
However, I will supply you with code snipped from the ios guide over at Parse, slightly altered to get the array you're after:
PFQuery *query = [PFQuery queryWithClassName:#"GameScore"];
[query whereKey:#"playerName" equalTo:#"Dan Stemkoski"];
[query includeKey:#"receivers"]; // Force parse to include the user objects of receivers
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
// Write to log the email of every receiver
for (PFUser *receiver in object[#"receivers"]) {
[receiver fetchIfNeeded]; // fetches the object if it is still just a pointer (just a safety; it should be already included by the includeKey call earlier in the code
NSLog(#"Receiver: %#", receiver[#"email"]);
}
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
Good luck :-)

Resources