6

I am trying to create a Application which lists all contacts from the iPhone address book with the following code (coffeescript)

listContacts: ->
    options = new ContactFindOptions()
    options.filter = '';
    options.multiple = true
    fields = ["id", "photos", "name", "phoneNumbers"]
    navigator.contacts.find(fields, @onSuccess, @onError, options)

onSuccess: (contacts) ->
    console.log contacts.length

onError: (error) ->
    console.log error

this seems to work nice for a bunch of contacts. but with 3000 the contacts will never return. the funny thing though this works perfectly on the iOsSimulator.

are there any limitations to the number of contacts which can be retrieved?

aschmid00
  • 7,038
  • 2
  • 47
  • 66
reco
  • 459
  • 1
  • 6
  • 13
  • Nobody did this before. Really? – reco Nov 04 '12 at 17:23
  • I think it is not a good idea to load 3000 contacts. Have you tried some kind of paging? – Christian Kuetbach Nov 06 '12 at 19:21
  • we thought about that too. does phone gap have an option for that? i cannot find a page option on the api docs. would be cool to have something like .find({limit=100}) – reco Nov 06 '12 at 23:28
  • just fund this question: http://stackoverflow.com/questions/12335003/limit-results-in-contacts-find-of-phonegap-api with no answer ;( – reco Nov 06 '12 at 23:28
  • 2
    I did not find any solution about that. You should open an issue in the issuetracker. Until this is fixed the only way arround this issue may be a filtering by letters of names name="A*", but this is ugly. – Christian Kuetbach Nov 07 '12 at 10:32
  • Does it work faster when you remove photos from the fields ? And are you using cordova 2.2 ? – Emmanuel Tabard Dec 12 '12 at 10:38

2 Answers2

2

I had the same problem with 300 contacts, it took around 5 minutes. After I patched it only takes 10 seconds.

Here is my pull request : https://github.com/phonegap/phonegap/pull/19

They have to generate a temp file for each picture and they are using a crazy loop to find a free file path. Something like :

do {        
  filePath = [NSString stringWithFormat:@"%@/photo_%03d.jpg", docsPath, i++];       
} while ([fileMgr fileExistsAtPath:filePath]);

Now I use mktemp and everything is faster.

If you don't need full res pictures, you can also replace :

CFDataRef photoData = ABPersonCopyImageData(self.record);

by :

CFDataRef photoData = ABPersonCopyImageDataWithFormat(self.record, kABPersonImageFormatThumbnail);

I hope that'll help you !

Edit :

IOS'll flush the temp directory each time you start the application:

You are responsible for deleting any temporary files that you created. The system will clean them up at startup, but that could be a very long time away.

From: http://cocoadev.com/wiki/NSTemporaryDirectory

If you don't want to slow down the bootstrap of your application, you should use always the same filepath based on the contact id. You'll save cleanup and write time if the file already exists :

- (NSObject*)extractPhotos
{
    NSMutableArray* photos = nil;

    if (ABPersonHasImageData(self.record)) {

        //CFDataRef photoData = ABPersonCopyImageDataWithFormat(self.record, kABPersonImageFormatThumbnail);
        CFDataRef photoData = ABPersonCopyImageData(self.record);
        NSData* data = (__bridge NSData*)photoData;

        // write to temp directory and store URI in photos array
        // get the temp directory path
        NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath];
        NSError* err = nil;
        int recordId = ABRecordGetRecordID(self.record);

        NSFileManager* fileMgr = [[NSFileManager alloc] init];
        NSString* filePath = [NSString stringWithFormat:@"%@/photo_%03d.jpg", docsPath, recordId];
        BOOL hasImage = NO;

        if ([fileMgr fileExistsAtPath:filePath]) {
            hasImage = YES;
        } else if ([data writeToFile:filePath options:NSAtomicWrite error:&err]) {
            hasImage = YES;
        }

        if (hasImage) {
            photos = [NSMutableArray arrayWithCapacity:1];
            NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:2];
            [newDict setObject:filePath forKey:kW3ContactFieldValue];
            [newDict setObject:@"url" forKey:kW3ContactFieldType];
            [newDict setObject:@"false" forKey:kW3ContactFieldPrimary];
            [photos addObject:newDict];
        }

        CFRelease(photoData);
    }
    return photos;
}

Edit (08/01/2013): FYI : merged in cordova : http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/c6a1dbe3

Emmanuel Tabard
  • 696
  • 5
  • 14
1

First you have to add plugin from terminal command line

$ cordova plugin add org.apache.cordova.contacts

onDeviceReady you can call a method to open contact list

function chooseContact() {
    var options = new ContactFindOptions();
    options.fields = ["displayName", "name", "emails", "phoneNumbers"];
    navigator.contacts.chooseContact(onSuccess, options);
}

function onSuccess(id, contact) {
    console.log(JSON.stringify(contact));
}
Muhammad Aamir Ali
  • 20,419
  • 10
  • 66
  • 57