0
votes

I followed this tutorial here https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections and was able to create a beautiful table with sections, but it takes forever to load! Well, not forever, 5 minutes to be exact. My table in parse has 587 rows in it and it takes 5 minutes to load all of the objects into sections. The first few minutes shows the "Loading..." on the blank view, then there is an empty tableview, and finally all of the objects load. Is there a reason something like this is taking so long? I can't have my users wait 5 minutes for something to load. This tableview is displayed during the register process. It is a list of schools and the new user must select which school they are from. The sections organize the schools based on location, and there are about 30 sections. Any suggestions for getting this to load faster?

Here is the code for the SchoolFinderViewController.m file

#import "SchoolFinderViewController.h"

@interface SchoolFinderViewController ()

@property (nonatomic, retain) NSMutableDictionary *sections;
@property (nonatomic, retain) NSMutableDictionary *sectionToRegionMap;

@end


@implementation SchoolFinderViewController

@synthesize sections = _sections;
@synthesize sectionToRegionMap = _sectionToRegionMap;

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {

        self.parseClassName = @"School";
        self.textKey = @"Name";
        self.pullToRefreshEnabled = NO;
        self.paginationEnabled = YES;
        self.objectsPerPage = 600;
        self.sections = [NSMutableDictionary dictionary];
        self.sectionToRegionMap = [NSMutableDictionary dictionary];
    }
    return self;
}

-(void)viewDidLoad
{
    [super viewDidLoad];

    self.title = @"Schools";
}


#pragma mark - PFQueryTableViewController

- (void)objectsDidLoad:(NSError *)error {
    [super objectsDidLoad:error];

    // This method is called every time objects are loaded from Parse via the PFQuery
    NSLog(@"Count in objectsDidLoad: %lu", (unsigned long)[self.objects count]);
    [self.sections removeAllObjects];
    [self.sectionToRegionMap removeAllObjects];

    NSInteger section = 0;
    NSInteger rowIndex = 0;
    int i = 0;
    for (PFObject *object in self.objects) {
        PFObject *obj = [object objectForKey:@"region"];
        [obj fetchIfNeeded];
        NSLog(@"School %@", [object objectForKey:@"Name"]);
        NSString *Region = [obj objectForKey:@"name"];
        NSLog(@"Reg: %@", Region);
        NSMutableArray *objectsInSection = [self.sections objectForKey:Region];
        if (!objectsInSection) {
            objectsInSection = [NSMutableArray array];

            NSLog(@"Is this called? %d", i);
            // this is the first time we see this Region - increment the section index
            [self.sectionToRegionMap setObject:Region forKey:[NSNumber numberWithInt:section++]];
        }

        [objectsInSection addObject:[NSNumber numberWithInt:rowIndex++]];
        [self.sections setObject:objectsInSection forKey:Region];
    }

    NSLog(@"Finally done...");
}

- (PFQuery *)queryForTable {
    PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];

    query.cachePolicy = kPFCachePolicyCacheThenNetwork;

    // If no objects are loaded in memory, we look to the cache first to fill the table
    // and then subsequently do a query against the network.
    if (self.objects.count == 0) {
        query.cachePolicy = kPFCachePolicyCacheThenNetwork;
    }

    // Order by name
    [query orderByAscending:@"Name"];
    return query;
}

- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath {
    NSString *Region = [self RegionForSection:indexPath.section];
    NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
    NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row];
    return [self.objects objectAtIndex:[rowIndex intValue]];
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.sections.allKeys.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSString *Region = [self RegionForSection:section];
    NSArray *rowIndecesInSection = [self.sections objectForKey:Region];
    return rowIndecesInSection.count;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    NSString *Region = [self RegionForSection:section];
    return Region;
}


#pragma mark - UITableViewDelegate

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

    NSLog(@"CellFor %ld", (long)indexPath.row);


    cell.textLabel.text = [object objectForKey:@"Name"];

    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [super tableView:tableView didSelectRowAtIndexPath:indexPath];

    PFObject *selectedObject = [self objectAtIndexPath:indexPath];
}


#pragma mark - ()

- (NSString *)RegionForSection:(NSInteger)section {
    return [self.sectionToRegionMap objectForKey:[NSNumber numberWithInt:section]];
}
1
What does your query look like?... Sounds like a bad idea, to force load the entire table in the beginning.. - Fosco
@Fosco I updated the question with my code. - Jacob

1 Answers

1
votes

Yeah, you're not going to be able to make this fast enough as-is... The client should not have to download every object first, and scrolling lists with 500+ items are not a good user experience. Perhaps you should have an initial screen where they pick some subset, and then they can query a smaller set of data on the next screen. What you're currently using as a section might be a good candidate.