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];
}];
}
Related
Hi am new to using Parse and am trying to load a simple Table View Controller with data from an array retrieved using Parse PFQuery. Though I can nslog the "categories" array in view did load, by the time the code reaches numberOfRowsInSection the array seems to have been reset to nil.
Any help with this would be greatly appreciated.
Btw I did try this loading the code into an array with literals and no problem the table was displayed fine.
Heres the code:
#implementation DisplayCategoriesTVC
NSArray *categories;
- (void)viewDidLoad {
[super viewDidLoad];
// CODE TO RETRIEVE CONTENTS OF THE PARSE CATEGORIES CLASS
PFQuery *query = [PFQuery queryWithClassName:#"Categories"];
// [query whereKey:#"Sequence" > #1];
[query findObjectsInBackgroundWithBlock:^(NSArray *categories, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %lu categories.", (unsigned long)categories.count);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [categories count];
}
The specfic question I have is, why at the numberOfRowsInSection is the categories array showing nil value?
The specific question I have is why does the categories array now show nil and what can I do to keep the values that were loaded by the PFQuery and use them in my other methods?
You're performing something on the background thread:
findObjectsInBackground:
What does this mean since you're new?
What's the difference between synchronous and asynchronous calls in Objective-C, versus multi-threading?
So how do you reload the tableView when your data finally does aggregate from the background task?
You simply, reload the tableView, but we need to do it on the main thread because UI updates happen there:
[self.tableView reloadData];
For more info see :
iPhone - Grand Central Dispatch main thread
So completely:
PFQuery *query = [PFQuery queryWithClassName:#"Categories"];
// [query whereKey:#"Sequence" > #1];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %lu categories.", (unsigned long)categories.count);
self.categories = objects;
//Since this is a UI update we need to perform this on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
Your query has completed its task before your UI is updated because it's happening on the background thread, so you need to tell your UI components when it is done.
I am using this query to find users, it works but it just shows me the first user. I want it to show me the user with the text of an UITextField.
How can I do that ?
(I have a textfield and there I type in a name and then it should show the parsed users with the name)
PFQuery *query = [PFUser query];
NSArray *users = [query findObjects];
userQuerys.text = users[0][#"username"];
Thanks very much
This code will fetch you all the PFUsers in which username is equal to the name parameter:
- (void)searchUsersNamed:(NSString *)name withCompletion:(void (^)(NSArray *users))completionBlock {
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:name];
[query findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// we found users with that username
// run the completion block with the users.
// making sure the completion block exists
if (completionBlock) {
completionBlock(users);
}
} else {
// log details of the failure
NSLog(#"Error: %# %#", error, [error description]);
}
}];
}
An example, if you need to update the UI with the result, for example, a table:
- (void)someMethod {
// we will grab a weak reference of self to perform
// work inside the completion block
__weak ThisViewController *weakSelf = self;
//replace ThisViewController with the correct self class
[self searchUsersNamed:#"Phillipp" withCompletion:^(NSArray *users) {
//perform non-UI related logic here.
//set the found users inside the array used by the
//tableView datasource. again, just an example.
weakSelf.users = users;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//pefrorm any UI updates only
//for example, update a table
[weakSelf.tableView reloadData];
}];
}];
}
A small note: the completionBlock here wont run if there is an error, but it will run even if no users were found, so you gotta treat that (if needed. in this example, it was not needed).
Avoid running non-UI related logic on that mainQueue method, you might lock the Main thread, and that`s bad user experience.
I have a view controller with inside table and I want to fill her with an array saved on Parse. To download the data I use this code:
PFQuery *query = [PFQuery queryWithClassName:#"myClass"];
[query whereKey:#"X" equalTo:#"Y"];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(error==nil){
myArray=[object objectForKey:#"Z"];
NSLog(#"%#",myArray);
}
}];
}
Now I display it inside myarray the data on parse. But if I use arrays to populate the table it is always me empty. I used NSLog and I saw that outside of the method [query getFirstObjectInBackgroundWithBlock: ^ (PFObject * object, NSError * error) my array is always empty.
How can help me?
Fetching data from a remote database takes a little time. The parse functions that take block params run asynchronously. See the comments within your slightly modified code...
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(error==nil){
// this appears first in the file, but runs later
// after the request is finished
myArray=[object objectForKey:#"Z"];
NSLog(#"%#",myArray);
// tell our view that data is ready
[self.tableView reloadData];
}
}];
// this appears second in the file, but runs right away, right
// when the request is started
// while execution is here, the request isn't done yet
// we would expect myArray to be uninitialized
Be sure, in your datasource methods e.g. numberOfRows to answer myArray.count. And use the data in the array myArray[indexPath.row] when building the table view cell.
I dont know what the deal with parse is but for some reason it wont allow me to save the retrieved array into a mutable array I created. It works inside the parse code block but once outside, it displays null. Help please?
PFQuery *query = [PFQuery queryWithClassName:#"comments"];
[query whereKey:#"flirtID" equalTo:recipe.flirtID];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
comments = [[NSMutableArray alloc]initWithArray:objects];
// Do something with the found objects
for (PFObject *object in objects) {
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
NSLog(#"%#",[comments objectAtIndex:0]);
It's actually working as it should.
You should read up on how blocks work.
Edit: Try reading Apple's Documentation
You're NSLogging 'comments' before comments actually gets set. How does that work?
You see, query is running in the background, and it will actually take a bit of time. It's running asynchronously. But the code outside the block will run immediately.
While the code comes before, because it's an asynchronous block, it can and will be run whenever.
Try this:
comments = [[NSMutableArray alloc]initWithArray:objects];
NSLog(#"%#",[comments objectAtIndex:0]);
The important question is, what do you want to do after the query? Looks like you want to save comments, but then what? That will determine what you do next.
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!