Building a simple tableview using Parse PFQuery - ios

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.

Related

Problems in calling Parse getdataInthebackground in a for loop

Hi I am trying to query some files from my Parse database and I want the files to be sorted according to the updateAt time. I have the following code. The query works and the results are sorted according to my condition, but when I load the files using getDataInBackground and then add to an array. The files are not sorted and they appear to be random in the array.
So My questions are
What can I do to make sure the files in the array are in the same order as the query results?
Any way to check the files/images against the objectID in the completion block of getDataInBackground?
p.s. I don't want to use getData since I don't want it to block the main thread.
Thank you very much in advance
PFQuery *query = [PFQuery queryWithClassName:#"Photo"];
[query orderByDescending:#"updateAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *photoStacks, NSError *error)
{
if (!error) {
// The find succeeded.
for (PFObject *photoImage in photoStacks) {
PFFile *userImageFile = photoImage[#"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData];
// need to check object id before adding into the stack to make sure the order is right
[photoImageStacks addObject:image];
if ([photoImageStacks count] == photoStacksCount)
{
[photoPile setArray:photoImageStacks];
}
}
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
use breakpoint and trace first photoImageStacks and second after response you should call reload method if you are using tableview or some delegate or fire a notification so that you can update ui accordingly after successful response.

Parse- [query findObjectsInBackgroundWithBlock:^.)]; Block Doesn't execute w/ Action Extension

I'm trying to develop and Action Extension for iOS9.1 that is supposed to query Parse for some data.
I've added, enabled and tested Parse in the extension and I'm successful at creating test objects and checking for current user.
I can not get the code inside Parse's query method
[query findObjectsInBackgroundWithBlock:^ to execute. LLDB just keeps skipping it so I'm really at a loss.
This code executes perfectly within the container app so I'm a bit confused.
- (void)viewDidLoad
{
[super viewDidLoad];
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.app.slinky"
containingApplication:#"com.app.Slinky"];
[Parse setApplicationId:#"xxxxx"
clientKey:#"xxxxx"];
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *jsDict, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey];
NSString *pageTitle = jsPreprocessingResults[#"title"];
NSString *pageURL = jsPreprocessingResults[#"URL"];
if ([pageURL length] > 0) {
self.siteURL.text = pageURL;
self.URLstring = pageURL;
}
if ([pageTitle length] > 0) {
self.siteTitle.text = pageTitle;
}
});
}];
break;
}
}
}
[self queryParse];
}
-(void)queryParse{
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
**[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
} else {
self.friends = objects;
[self.tableView reloadData];
}
}];**
}
There are some limit to fetch Objects from Parse. Read Here
One has a limit of obtaining 100 objects, but anything from 1 to 1000 has a valid limit.
You have to set the limit again for the query for next objects and skip the properties of PFQuery.
You will have to query again and again until you reach the total count.
For Example - If you have 3000 objects to be fetched, then you have to query 3 times with a count of say 1000.
OR
Call [self queryParse]; inside dispatch_async(dispatch_get_main_queue(), ^{ }); block.
Hope this might workout.
This was just a result of a 12 hour refractoring marathon :-(
When I moved the queryParse call into its own method. I lost the definition of
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
essentially sending a bad query... I don't know why it isn't return an error but regardless I can check that off now.
Thank you all for you help!

Query PFUser not working

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.

Insert object on parse in my table

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.

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];
}];
}

Resources