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.
Related
I am testing out Parse localDatastore and am struggling with refreshing the local datastore after a new server PFQuery.
The PFQuery works fine and seems to pin the array to the local datastore just fine. When I change the contents of the array on the server, the server PFQuery pulls down the updated array, but the local datastore doesn't seem to update:
- (void)viewDidLoad {
[super viewDidLoad];
// Query Parse
PFQuery *query = [PFQuery queryWithClassName:#"contacts"];
NSArray *objects = [query findObjects];
[PFObject pinAllInBackground:objects block:^(BOOL succeeded, NSError *error) {
if(succeeded) {
NSLog(#"Successfully retrieved %lu records from Parse.", (unsigned long)objects.count);
} else if (error) {
NSLog(#"Error");
}
}];
}
and then a UIButton is used to log the contents of the local datastore to the console:
-(IBAction)showDatastore {
// Query the Local Datastore
PFQuery *query = [PFQuery queryWithClassName:#"contacts"];
[query fromLocalDatastore];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %lu contacts from Datastore.", (unsigned long)objects.count);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
In my sample the original array has 15 objects. Both the count's from each array initially are 15. I then remove an object from the server array and the initial PFQuery count is 14, but the local datastore count remains 15.
Parse's documentation states:
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.
But that doesn't seem to be the case... at least not with this recommended code. Is there something i'm missing?
It depends on how you are deleting the object. If you're using deleteEventually, then the deletion will propagate to the LDS
You can query from the local datastore using exactly the same kinds of queries you use over the network. The results will include every object that matches the query that's been pinned to your device. The query even takes into account any changes you've made to the object that haven't yet been saved to the cloud. For example, if you call deleteEventually, on an object, it will no longer be returned from these queries.
But any other method requires explicit unpinning if you want it to work.
deleteEventually is the prefered method I believe.
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.
Working on an App that allow user to see what local people have posted (businesses, schools, or just people). I am able to post the information and save it to parse. I am also able to download user data near current location, but it issue is that my helper method does not do it right away when the app first launches.
I have tried to call the helper method below in the viewDidLoad,viewWillAppear and viewDidAppear and no luck. The postArray is always null when the user initially opens the application and goes to the map. If I move to another screen and come back to the mapVC the data is there...(I NSlog the postArray) and all the posts near the current location print out. I would like to have this data initially right when the map presents the user's location.
Questions.
Where should I call my helper method? When the app launches I want to
have the data so i can display it on the map.
Is there another method that I need to write?
Is there something wrong with the current method.
- (void)loadLocalPosts {
NSLog(#"Querying for Local Posts");
[activityIndicator startAnimating];
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError
*error) {
geopoint = geoPoint;
[query whereKey:#"location" nearGeoPoint:geoPoint];
[query setLimit:50];
[query addDescendingOrder:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
postArray = objects;
}];
}];
}
The problem here is that postArray is populated in a background thread and your application initializes faster than it can populate the array. This is common in asynchronous programming. The way to fix this, is by asking the map to refresh in the main thread.
- (void)loadLocalPosts {
NSLog(#"Querying for Local Posts");
[activityIndicator startAnimating];
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError
*error) {
geopoint = geoPoint;
[query whereKey:#"location" nearGeoPoint:geoPoint];
[query setLimit:50];
[query addDescendingOrder:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
postArray = objects;
dispatch_async(dispatch_get_main_queue(),^
{
// update view properties, refresh etc.
});
}];
}];
}
Note: All view related modifications should always happen in the main thread. Hence the dispatch_get_main_queue(). Now the main loop could however, be doing view related operations, hence a synchronous call will crash the application. Hence dispatch_async is used. This will add the block to the next run loop to be executed after the current one.
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 am running the query detailed below. It does not log any error and proceeds to log the objects array. When it logs the objects array it displays every object within it up until the first object that was added to the class today. No objects other than the first one added today are displayed. If I delete the first object from today it will display every object up until the new first object for today.
PFQuery *query = [PFQuery queryWithClassName:#"className"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"%#", objects);
}
} else {
NSLog(#"Error: %#", error);
}
}];
I wasn't able to figure out why this was occurring so I simply created a new class and added all of the values from my old class to it and it began to work.