Display (All) Data from two classes on Parse.com in UITableView - ios

I want to be able to display all of the data from two classes from Parse.com but it is not working. Every time I go to open the tableview the app crashes. Here is my code. I used the guidance from https://www.parse.com/questions/query-with-two-classes to aid me.
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
// This table displays items in the class
self.parseClassName = #"rep";
self.parseClassName = #"rep2";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
self.objectsPerPage = 100;
}
return self;
}
- (PFQuery *)queryForTable {
PFQuery *1query = [PFQuery queryWithClassName:#"rep"];
PFQuery *2query = [PFQuery queryWithClassName:#"rep2"];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[1query,2query]];
[query whereKey:#"user" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
}];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
// Configure the cell to show todo item with a priority at the bottom
cell.textLabel.text = [object objectForKey:#"name"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Username: %#",
[object objectForKey:#"username"]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
PFObject *objectToDel = [self.objects objectAtIndex:indexPath.row];
[objectToDel deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
UIAlertView *Alert = [[UIAlertView alloc] initWithTitle:#"Item Was Deleted Successfully. Pull Down to Refresh Tab" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[Alert show];
}
];
}
#end
> Specwatch[668:192464] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'All sub queries of an
> `or` query should be on the same class.'
> *** First throw call stack:
> (0x182319900 0x181987f80 0x182319848 0x1000bd8b4 0x100025c10 0x100072514 0x100071e00 0x18700c0c0 0x1870cbda8 0x1870cbc80
> 0x1870caec8 0x1870caa6c 0x1870ca694 0x1870ca5fc 0x187007778
> 0x184a16b2c 0x184a11738 0x184a115f8 0x184a10c94 0x184a109dc
> 0x184a0a0cc 0x1822d0588 0x1822ce32c 0x1822ce75c 0x1821fd680
> 0x18370c088 0x187074d90 0x10006d054 0x181d9e8b8)
> libc++abi.dylib: terminating with uncaught exception of type NSException
> (lldb)

I'll try to summarize how to go about emancipating yourself from the PFQueryTableViewController (and UITableViewController for that matter. The world would be a slightly happier place if neither of these classes had been invented (imo)). Create a UIViewController subclass called ViewController.
In IB, add a UIViewController (setting its class to ViewController), give it a UITableView constrained to the edges of the view. Hook up the datasource, delegate and an outlet called tableView. Add a prototype cell. For now, just use a standard subtitle or left detail UITableViewCell. Give the cell an identifier of #"cell".
// viewcontroller.m
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property(weak,nonatomic) IBOutlet UITableView *tableView;
#property(strong,nonatomic) NSMutableArray *objects;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[PFCloud callFunctionInBackground:#"getTwoClasses" withParameters:nil block:^(NSArray *objects, NSError *error) {
self.objects = objects;
[self.tableView reloadData];
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
PFObject *object = self.objects[indexPath.row];
cell.textLabel.text = [object objectId];
cell.detailTextLabel.text = [object parseClassName];
return cell;
}
That very simple implementation should be all that's needed on the client. On the server, we just need a cloud function called "getTwoClasses" to get objects from two classes (rep and rep2).
Deploy this function to do that...
Parse.Cloud.define("getTwoClasses", function(request, response) {
var reps;
var user = request.user;
var repQuery = new Parse.Query("rep");
repQuery.equalTo("user", user);
query.find().then(function(result) {
reps = result;
var rep2Query = new Parse.Query("rep2");
rep2Query.equalTo("user", user);
return rep2Query.find();
}).then(function(result) {
response.success(reps.concat(result));
}, function(error) {
response.error(error);
});
});

Related

Unable to dequeue a cell when searching table view parse.com iOS

I'm trying to do a search function in my table view, to return objects from my Parse.com class.
I'm getting this error when I try to make a search in the UISearchBar:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier FeaturedTableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
Here's how I'm doing it:
#interface DiscoverViewController () <UISearchBarDelegate, UISearchDisplayDelegate>
#property (nonatomic, retain) PFQuery *query;
#property (nonatomic, retain) PFObject *category;
#end
#implementation DailyDiscoverViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
self.searchResults = [NSMutableArray array];
_categories = [[NSMutableArray alloc] initWithCapacity:100];
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
[query whereKeyExists:#"name"];
[query whereKey:#"name" containsString:searchTerm];
NSArray *results = [query findObjects];
[self.searchResults addObjectsFromArray:results];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)refresh {
PFQuery *query = [PFQuery queryWithClassName:#"Categories"];
[query orderByDescending:#"name"];
[query findObjectsInBackgroundWithBlock:^(NSArray *posts, NSError *error) {
if (!error) {
//NSLog(#"%#", posts);
} else {
}
[_categories setArray:posts];
[self.tableView reloadData];
[_loadingView stopAnimating];
[_refreshControl endRefreshing];
}];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
if (section == 0) {
return 1;
} else {
return self.categories.count;
}
} else {
return self.searchResults.count;
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return 320;
} else {
return 52;
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
_featuredObject = [_featured objectAtIndex:indexPath.row];
DiscoverFeaturedTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverFeaturedTableViewCell" forIndexPath:indexPath];
[(PFFile*)_featuredObject[#"picture"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
cell.image1.image = [UIImage imageWithData:data];
}];
return cell;
} else {
_category = [_categories objectAtIndex:indexPath.row];
DiscoverTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"DiscoverTableViewCell" forIndexPath:indexPath];
cell.name.text = _category[#"name"];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]) {
PFUser *obj2 = [self.searchResults objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Projects"];
PFObject *searchedUser = [query getObjectWithId:obj2.objectId];
NSString *first = [searchedUser objectForKey:#"name"];
cell.name.text = [first substringToIndex:1];
cell.name.text = first;
return cell;
}
return cell;
}
}
#end
I think you can look this stack over flow post : POST
K.
Ok,
Try it on viewDidLoad:
YourCustomCell *yourCustomCell = [UINib
nibWithNibName:#"YourCustomCell" bundle:nil]; [self.tableView
registerNib:cellNib forCellReuseIdentifier:#"cell"];
Note: Your custom cell must have a .xib of course.
You could try this, I remember running into this exact issue and this was one of the solutions I tried:
[self.tableView registerClass:[DiscoverTableViewCell class] forCellReuseIdentifier:#"DiscoverTableViewCell"];
However, I distinctly remember that this didn't work. It turned out I hadn't correctly connected the cells in the storyboard. Furthermore, make sure that the class of your storyboard cell has been changed to represent your cell's custom class.

Display user data on UITableView obtained from a column typed array of PFUser on Parse.com

I created UITableView inside UIView (UserListVC) to display user data stored in a column typed array on Parse.com. The column called "followers" contains the array of PFUser objectId who did like the user in a row (in this case is current user).
In userListVC.m:
#implementation UserListViewController
{
NSMutableArray *tableData;
}
- (void)viewDidLoad {
[super viewDidLoad];
/*
tableData = [NSMutableArray arrayWithObjects:#"user1", #"user2", nil];
NSLog(#"tableData --> %#", tableData);
*/
tableData = [[NSMutableArray alloc] init];
PFQuery *userQuery = [PFQuery queryWithClassName:kPAWParseUserClassKey];
[userQuery whereKey:kPAWParseUsernameKey equalTo:[PFUser currentUser].username];
userQuery.cachePolicy = kPFCachePolicyNetworkElseCache;
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error)
{
if( !error )
{
NSArray *array = [users valueForKey:#"followings"];
for (int i = 0; i <= array.count; i++)
PFQuery *followingsQuery = [PFUser query];
[followingsQuery whereKey:#"objectId" equalTo:[[[users valueForKey:#"followings"] objectAtIndex:0] objectAtIndex:i]];
followingsQuery.cachePolicy = kPFCachePolicyNetworkElseCache;
[followingsQuery findObjectsInBackgroundWithBlock:^(NSArray *followings, NSError *error) {
if (!error) {
NSLog(#"following names --> %#", [followings valueForKey:kPAWParseUsernameKey]);
[tableData addObject:[followings valueForKey:kPAWParseUsernameKey]]; //??
[self.tableView reloadData];
NSLog(#"table data --> %#", tableData);
}
}];
}
}];
}
I am now able to extract usernames from the user class by using data array that I get from "followers" column and I also have [self.tableView reloadData]; in async query block but there is still a problem with tableview not showing usernames obtained from tableData. If I use sample data ("user1","user2") just for testing, there is no problem.
Here below I show my log from the code:
2014-12-31 11:05:00.626 Test[12354:60b] following names --> (
user1name
)
2014-12-31 11:05:00.628 Test[12354:60b] table data --> (
(
user1name
)
)
2014-12-31 11:05:00.631 Test[12354:60b] following names --> (
user2name
)
2014-12-31 11:05:00.633 Test[12354:60b] table data --> (
(
user1name
),
(
user2name
)
)
I think it is better to also provide code for the method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath and here it is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tableIdentifier = #"TableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableIdentifier];
}
NSLog(#"table data xxxxx --> %#", tableData);
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
The above method NEVER runs but if I test with my sample data, it does run. To be more specific, my questions would be:
This is UIView with UITableView inside it. I am not sure how to do [self.tableView reloadData]; properly. I mean how to declare tableView. In this case I do this:
#interface UserListViewController ()
#property (weak, nonatomic) UITableView *tableView;//????
#end
Why the method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath never get run?
Just to answer these 2 specific questions:
Connect tableView to IBOutlet in .h file and synthesise it in .m file, then:
[self.tableView reloadData];
do the above inside async block.
The method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath will get run if do as the above suggests.
However there remains other issues but they are out of the scope of this question I believe.

UITableViewCell doesn't display

I have a problem with my TableViewController. There is a custom cell, with a class, and various infos dynamically loaded. My TableViewController appears, but my cell doesn't display, but i can touch this, and my transition with infos are good.
Thanks for your answers.
TableViewController.m
#interface Chat() {
NSMutableArray *messages;
UIRefreshControl *refreshControl;
}
#property (strong, nonatomic) IBOutlet UITableView *tableMessages;
#end
#implementation Chat
NSString *cellIdentifier = #"ChatCell";
- (void)viewDidLoad {
[super viewDidLoad];
[_tableMessages registerClass:[ChatCell class] forCellReuseIdentifier:cellIdentifier];
refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(loadMessages) forControlEvents:UIControlEventValueChanged];
[_tableMessages addSubview:refreshControl];
messages = [[NSMutableArray alloc] init];
[self loadMessages];
}
- (void)loadMessages {
if ([PFUser currentUser] != nil)
{
PFQuery *query = [PFQuery queryWithClassName:PF_MESSAGES_CLASS_NAME];
[query whereKey:PF_MESSAGES_USER equalTo:[PFUser currentUser]];
[query includeKey:PF_MESSAGES_LASTUSER];
[query orderByDescending:PF_MESSAGES_UPDATEDACTION];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error == nil) {
[messages removeAllObjects];
[messages addObjectsFromArray:objects];
[_tableMessages reloadData];
} else [ProgressHUD showError:#"Network error."];
[refreshControl endRefreshing];
}];
}
}
- (void)actionCleanup {
[messages removeAllObjects];
[_tableMessages reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [messages count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell bindData:messages[indexPath.row]];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
DeleteMessageItem(messages[indexPath.row]);
[messages removeObjectAtIndex:indexPath.row];
[_tableMessages deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
PFObject *message = messages[indexPath.row];
ChatView *chatView = [[ChatView alloc] initWith:message[PF_MESSAGES_ROOMID]];
[self.navigationController pushViewController:chatView animated:YES];
}
#end
TableViewCell.m
#interface ChatCell() {
PFObject *message;
}
#end
#implementation ChatCell
- (void)bindData:(PFObject *)message_ {
message = message_;
_chatImg.layer.cornerRadius = _chatImg.frame.size.width/2;
_chatImg.layer.masksToBounds = YES;
PFUser *lastUser = message[PF_MESSAGES_LASTUSER];
[_chatImg setFile:lastUser[PF_USER_PICTURE]];
[_chatImg loadInBackground];
_chatUsername.text = message[PF_MESSAGES_DESCRIPTION];
_chatMessage.text = message[PF_MESSAGES_LASTMESSAGE];
NSTimeInterval seconds = [[NSDate date] timeIntervalSinceDate:message.updatedAt];
_chatDate.text = TimeElapsed(seconds);
}
#end
It's because you register the cell using - registerClass:forCellReuseIdentifier:.
If you register it this way you have to construct the view programmatically or load the nib file in ChatCell code.
To solve the problem, do either of these:
Create a nib file containing the view for your table view cell and set the class to ChatCell. Then use - registerNib:forCellReuseIdentifier: to register the nib.
Construct the view programmatically eg. create a UILabel and add it as a subview of ChatCell.
Make the prototype cell in the storyboard and set the cell identifier to ChatCell. Then remove the - registerClass:forCellReuseIdentifier:
Check You are given correct cell Identifier in storyboard. (case sensitive) " ChatCell"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ChatCell";
ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell bindData:messages[indexPath.row]];
return cell;
}
You are updating the UI on background thread. Try this, in your "loadMessages" method.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
if (error == nil) {
[messages removeAllObjects];
[messages addObjectsFromArray:objects];
[_tableMessages reloadData];
} else [ProgressHUD showError:#"Network error."];
[refreshControl endRefreshing];
});
}];

Parse iOS SDK: Query using UISearchDisplayController

Scenario = I have an app that allows users to search for other users that use the service. In the search page there is a UISearchDisplayController that when a user begins typing in the search bar, a tableView will programmatically appear (just like any other UISearchDisplayController) and filter all of the users in the database depending on whats being typed ('begins-with'). So the user will begin typing, "B... r...." and users will begin to populate the tableView from "Brad" to "Brandon" and so on based on the text being inputted.
Question = How would one go about designing the parse query to achieve this effect?
Specific Questions =
1) When and Where to begin the initial query?...
PFQuery *searchQuery = [PFUser query];
[searchQuery whereKey:#"username" containsString:controller.searchBar.text];
[searchQuery orderByDescending:#"updatedAt"];
[searchQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"%#", objects);
searchArray = objects;
}];
in "searchDisplayControllerDidBeginSearch"?
2) When and Where do I put the logic to fill in the tableView?
PFObject *searchObject = [searchArray objectAtIndexPath:indexPath.row];
cell.nameLabel.text = searchObject[#"name"];
in "cellForRowAtIndexPath"?
If there is anyone out there that knows this and can help me out Id appreciate it.
Here is a simple example:
#import <Parse/Parse.h>
#interface MySearchController : PFQueryTableViewController
#end
And implementation
#import "MySearchController.h"
#interface MySearchController() <UISearchBarDelegate, UISearchDisplayDelegate>
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation MySearchController
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// get users
self.parseClassName = [PFUser parseClassName];
self.pullToRefreshEnabled = YES;
self.paginationEnabled = YES;
self.objectsPerPage = 10;
self.searchResults = [NSMutableArray new];
}
return self;
}
- (void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
for (PFUser *user in self.objects)
{
NSString *username = user.username;
if ([[username lowercaseString] hasPrefix:[searchTerm lowercaseString]])
{
[self.searchResults addObject:user];
}
}
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString: (NSString *)searchString {
[self filterResults:searchString];
return YES;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return (tableView == self.tableView) ? self.objects.count : self.searchResults.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PFUser *user = (tableView == self.tableView) ? self.objects[indexPath.row] : self.searchResults[indexPath.row];
static NSString *identifier = #"reuseIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier];
}
cell.textLabel.text = user.username;
return cell;
}
#end
The main thing to note about this that threw me off is that you have two table views, so you have to be careful.
One of the table views is from the original query, it will give you all users, that one is self.tableView. The other is from the search results, self.searchDisplayController.searchResultsTableView. The latter is active while searching. Therefore, you must return different values for each regular tableviewcontroller method. The number of rows is either self.objects.count or self.searchResults.count. The correct user is either self.objects[indexPath.row] or self.searchResults[indexPath.row]. It is easy to check which table view you're dealing with in a given protocol method, just use this condition:
(tableView == self.tableView)

Warning: A long-running Parse operation is being executed on the main thread

my app uses a SearchGraphical to search for registered users in the app ... My problem is that when I go to do research shows me the duplicate results as:
Name Search: Francesca
Result: Francesca (1 cell) Francesca (2 Cell)
**09/14/2013 22:18:17.817 Unipot v.02 [63396: a0b] Successfully retrieved 1 scores.
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: Antonella Folino
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: francesca Folino
09/14/2013 22:18:17.818 Unipot v.02 [63396: a0b] Content: francesca Folino
09/14/2013 22:18:17.819 Unipot v.02 [63396: a0b] Content: francesca Folino**
I do not understand why. This problem arises when you type in the search bar is done quickly.
In addition, the app crashes after a short time by returning to this notice:
**Terminating apt two to uncaught exception 'NSRangeException', reason: '*** - [__NSArrayM objectAtIndex:]: index 10 beyond bounds [0 .. 9] '**
This is my file. M Can you help me??
#import "Ricerca.h"
#import "Custom.h"
#interface Ricerca () <UISearchDisplayDelegate, UISearchBarDelegate>
#property (nonatomic, strong) UISearchDisplayController *searchController;
#property (nonatomic, strong) NSMutableArray *searchResults;
#end
#implementation Ricerca
- (void)viewDidLoad {
[super viewDidLoad];
[self loadObjects];
self.searchResults = [NSMutableArray array];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.parseClassName = #"_User";
self.paginationEnabled = YES;
self.loadingViewEnabled = NO;
self.objectsPerPage = 10;
}
return self;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait || UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
// Configure the cell
UIColor *color = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:0.0];
cell.detailTextLabel.backgroundColor = color;
cell.textLabel.backgroundColor = color;
if (tableView == self.tableView) {
cell.textLabel.text = [object objectForKey:#"username"];
}
else {
PFObject *searchedUser = [self.searchResults objectAtIndex:indexPath.row];
NSString *content = [searchedUser objectForKey:#"username"];
cell.textLabel.text = content;
NSLog(#"Content: %#", content);
}
return cell;
}
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"username" containsString:searchTerm];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
[self.searchResults addObjectsFromArray:objects];
[self.searchDisplayController.searchResultsTableView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
return self.objects.count;
} else {
return self.searchResults.count;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.tableView) {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
} else {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
}
In your filterResults: method you are calling [self.searchResults addObjectsFromArray:objects];, this will add those results to what is already there. In the case where this method is getting hit multiple times before the first query finishes you could end up with the following scenario:
filter 1: "Fran"
clear results
start query 1
filter 2: "Franc"
clear results
start query 2
filter 2 finishes in background: add single result to array (contents 1 item)
filter 1 finishes in background: add single result to array (contents 2 items)
As you can see, there's no way to be sure when a query will finish, they might come back in a different order, and might not come back before you call the method again.
In this case it is hard to know what to do, you could empty self.searchResults in the success block, but in a case like above the final contents will be for the first query instead of the second query.
You could implement some kind of cancel/ignore-results option, or add a delay to the start of the query and hope for the best. Either way I would suggest reading up on threading issues and async operations.

Resources