PFQuery in UITableviewCell subclass and calling it in cellForRowAtIndex: - ios

I have this query in my view controller's cellForRowAtIndex::
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:object[#"creator"]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (object) {
PFFile *file = [object objectForKey:#"profileImg"];
cell.avatar.file = file;
[cell.avatar loadInBackground];
}
}];
I would like to move this method into my custom cell's class, is it possible somehow? I've tried to move it (with some modifications) to my cell's .m file, but I can't create a class method from this.
-(void) setupAvatar:(PFUser *)object {
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:object[#"creator"]];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (object) {
PFFile *file = [object objectForKey:#"profileImg"];
self.avatar.file = file;
[self.avatar loadInBackground];
}
}];
}
How could I solve this issue? Is it totally wrong to make queries in the cell's class or it's ok, but I'm doing it wrong? I would really appreciate if somebody could show me the right way.

If you can help it, I would try to avoid doing that work in the cell subclass since it breaks the MVC pattern. There's no good reason for a view to know about Parse, that's a model's job. If at all possible I would try to flesh out a data source class for your table view controller and ask that for your image instead. I would also be super apprehensive about making a call like that in cellForRowAtIndexPath especially because it isn't synchronous. I bet you are going to run into some really strange cell reuse bugs since that method will be running often and there's not a guaranteed way to know when it will return - your cell images might start changing on their own and exhibiting a lot of other strange behavior.

Related

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.

load objects from parse and display on map

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.

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.

Parse.com with iOS App and Objective-C code

until recently, I've always used Parse.com for data management of my app I was now working on a new project and I have some problems ....
P.S. I'm using Xcode 6
First there PFImageView, you can confirm this? at this point without consulting a PFImageView how can I draw a picture in my app? I do not understand why you can not use in my app PFImageView
Also when I do a query, previously (using the enter button on the keyboard) appeared the auto-build block of the query but now I find myself this
PFQuery *query = [PFUser query];
[query findObjectsInBackgroundWithBlock: (nullable PFArrayResultBlock (nullable) block]
What is happening to parse.com? where am I doing wrong? someone can 'give me help on this?
On the PFImageView:
Try calling – loadInBackground on in. Source
On autocompletion:
This seems to be a bug in Xcode actually. After they have implemented the "nullable" keyword, Xcode seems to be having a hard time auto generating code for the blocks you pass as arguments. That has nothing to do with Parse though.
Why don't use PFFile instead?
For loading an image from data you can do like this:
PFFile *fileImage = your_file;
[fileImage getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
UIImage *image = [UIImage imageWithData:imageData];
yourImageView.image = image;
}];
And for the PFQuery replace that method with this block:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { }}];
or check the class reference here

iOS multithreading with Parse and dispatch_async

So I'm trying to load a list of usernames from Parse using a PFRelation query. The idea is for this list to be loaded and then used in the view controller that calls it. (through self.yourFriendArray). The original problem is that when I call this function in the viewDidLoad method of the view controller, the self.yourFriendArray array is nil because the query runs synchronously and does not update it.
So my plan was to use the main thread within this findObjectsInBackgroundWithBlock call, but I'm not getting the desired output yet.
Am I multi-threading incorrectly?
Also, I'm just getting used to asking questions on stack overflow -- if there's something wrong with how I'm asking this one please suggest some updates in the comments! Thank you guys!
[[friendsRelation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (PFUser *user in objects) {
NSString *username = user.username;
[tempArray addObject:username];
dispatch_async(dispatch_get_main_queue(), ^{
[self.yourFriendArray addObject:username];
});
}
}];
I don't know if I'm understanding well what you are trying to do... Is it that you have a tableView that loads the list of userNames from the yourFriendArray? Then, the only thing you need to do is to reload the tableView when the updated array is ready.
[[friendsRelation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (PFUser *user in objects) {
NSString *username = user.username;
[self.yourFriendArray addObject:username];
}
[self.tableView reloadData];
}];
Sorry if this is not what you are asking...
Try this:
[[friendsRelation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
for (PFUser *user in objects) {
NSString *username = user.username;
[self.yourFriendArray addObject: username];
}
[self.tableview reloadData];
});
}];

Resources