0

I'm displaying an array of contacts ( [[ContactStore sharedStore]allContacts] ) in a tableview and have divided the list into alphabetic sections. I have used the following code to return an array of the first letters of the contacts, and a dictionary of the number of entries per letter.

    //create an array of the first letters of the names in the sharedStore
nameIndex = [[NSMutableArray alloc] init];

//create a dictionary to save the number of names for each first letter
nameIndexCount = [[NSMutableDictionary alloc]init];

for (int i=0; i<[[[ContactStore sharedStore]allContacts]count]; i++){

//Get the first letter and the name of each person
    Contact *p = [[[ContactStore sharedStore]allContacts]objectAtIndex:i];
    NSString *lastName = [p lastName];
    NSString *alphabet = [lastName substringToIndex:1];


    //If that letter is absent from the dictionary then add it and set its value as 1 
    if ([nameIndexCount objectForKey:alphabet] == nil) {
        [nameIndex addObject:alphabet];
        [nameIndexCount setValue:@"1" forKey:alphabet];
    //If its already present add one to its value
    } else {

        NSString *newValue = [NSString stringWithFormat:@"%d", ([[nameIndexCount valueForKey:alphabet] intValue] + 1)];

        [nameIndexCount setValue:newValue forKey:alphabet];
    }
} 

This works, however it is very slow when the array is large, I'm sure there's a better way to do this but I'm quite new to this so am not sure how. Are there any suggestions for a better way to do this?

peten
  • 113
  • 8

2 Answers2

2

Although Bio Cho has a good point, you might see an increase in performance by calling

[[ContactStore sharedStore]allContacts]

only once. For example:

nameIndex = [[NSMutableArray alloc] init];
nameIndexCount = [[NSMutableDictionary alloc] init];

/*
 Create our own copy of the contacts only once and reuse it
 */
NSArray* allContacts = [[ContactStore sharedStore] allContacts];

for (int i=0; i<[allContacts count]; i++){
    //Get the first letter and the name of each person
    Contact *p = allContacts[i];
    NSString *lastName = [p lastName];
    NSString *alphabet = [lastName substringToIndex:1];

    //If that letter is absent from the dictionary then add it and set its value as 1 
    if ([nameIndexCount objectForKey:alphabet] == nil) {
        [nameIndex addObject:alphabet];
        [nameIndexCount setValue:@"1" forKey:alphabet];
    //If its already present add one to its value
    } else {
        NSString *newValue = [NSString stringWithFormat:@"%d", ([[nameIndexCount 
            valueForKey:alphabet] intValue] + 1)];

        [nameIndexCount setValue:newValue forKey:alphabet];
    }
} 

Though I can't say for sure, I'd guess that repeatedly accessing your shared store is what's killing you. Maybe only accessing it once will give you what you need.

eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
  • Thanks Rickay for your answer it pointed me to the problem, I investigated further to realise the lag wasn't the enumeration but accessing the shared store for the first time. I'm now getting the app to access the shared store earlier on and it loads super quickly. Thanks! – peten Jan 07 '13 at 03:28
  • No problem. You might want to call -sharedStore in applicationDidFinishLaunching in your app's delegate so the contacts are loaded from the very get-go. You need to consider, though, what might happen if the app is tested with a very large data set. You can't be too careful. Good luck! – eric.mitchell Jan 07 '13 at 03:43
0

Consider storing your contacts in Core Data and using an NSFetchedResultsController.

The NSFetchedResultsController will only load a subset of the rows which are visible on the table view, thus preventing your user from having to wait for all the contacts to be sorted.

NSFetchedResultsController will also sort your contacts by an attribute (ie. first or last name), and you can set your section titles to be the first letter of the field you're sorting by.

Take a look at this question and this tutorial.

Community
  • 1
  • 1
Bio
  • 505
  • 4
  • 10