I'm currently working on an app that downloads a substantial number of contacts through a web service and inserts the parsed data into the iOS address book. Because it's not unheard of for the users of the app to have 10-15 thousand contacts, my initial approach was to break things up into discrete operations (one operation being a download/parse/insert of 1500 contacts) and have them run individually in the background. For example, if I was to download 20000+ contacts the code would look something like the following.
for(int i = 0; i < 20047; i+=1500)
{
NSString* url = [NSString stringWithFormat:@"http://XXXXXX.XXXX/XXXXX"];
NSString* urlEncodedString = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:urlEncodedString];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^()
{
[self parseJSON:responseObject];
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
[self.downloadQueue addOperation:op];
}
For reference, the "parseJSON" function looks roughly like the following:
CFErrorRef error = NULL;
ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, &error);
for(NSDictionary* _contactInfo in _contacts)
{
ABRecordRef aRecord = ABPersonCreate();
.............
//Strip the record info and insert it into the ABRecordRef
.............
//Add the record to the previously created group (ID equal to 1 in this case.)
if(ABAddressBookAddRecord(ab, aRecord, nil))
{
ABRecordRef ieGroup = ABAddressBookGetGroupWithRecordID(ab,1);
ABGroupAddMember(ieGroup, aRecord, nil);
}
CFRelease(aRecord);
}
ABAddressBookSave(ab, nil);
CFRelease(ab);
This approach seems to work well when the number of contacts is less than 10000, but starts to break down in the 15000 range. I've been able to download 16000+ contacts, but it tends to be flaky and has a habit of crashing around the 12000 mark at the ABAddressBookSave function.
While doing some simple timing tests it seems like the biggest bottleneck is the ABAddressBookSave. Every subsequent save operation takes longer and longer (on the order of minutes, not seconds) which isn't terribly surprising since I'm guessing each operation has to wait longer on the lock for the previous one to finish.
My question is whether anyone has dealt with this type of situation and whether or not there is a better solution? Would downloading/parsing/inserting everything sequentially a better idea and less prone to problems?