Crash report when user accesses the address book
Asked Answered
A

3

8

In my App, Crashlytics is used to gather crash reports from users. Here is one crash report from a user. It is possibly depending on the contacts informations of the user. I can not recreate the crash, as I do not know what's in his/her contacts. Does any one has an idea about this situation?

com.apple.root.default-priority Crashed
0    CoreFoundation  CFStringCreateCopy + 13
1    AppSupport  CPSqliteDatabaseCreateWithPath + 36
2    AppSupport  CPSqliteDatabaseCreateWithPath + 36
3    AppSupport  CPRecordStoreGetDatabase + 16
4    AppSupport  _getReaderConnection + 10
5    AppSupport  CPRecordStoreProcessQueryWithBindBlock + 22
6    AppSupport  CPRecordStoreCopyAllInstancesOfClassWhereWithBindBlock + 98
7    AddressBook     ABCCopyArrayOfAllPeopleInSourceWithSortOrdering + 244
8    SeeYouKee  PhoneNumberInputViewController.m line 538-[PhoneNumberInputViewController dofetchContacts:]
9    AddressBook     __37-[ABTCC accessRequestWithCompletion:]_block_invoke_0 + 26
10   TCC     __TCCAccessRequest_block_invoke_038 + 316
11 ...   libxpc.dylib    _xpc_connection_call_reply + 26
12   libdispatch.dylib   _dispatch_root_queue_drain + 278
13   libdispatch.dylib   _dispatch_worker_thread2 + 92
14   libsystem_c.dylib   _pthread_wqthread + 360

The code for 8 SeeYouKee PhoneNumberInputViewController.m line 538-[PhoneNumberInputViewController dofetchContacts:] is:

NSArray *contactsInAddressBook = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, kABPersonSortByLastName));

EDIT 1

-(void)dofetchContacts:(ABAddressBookRef)addressBook{
NSMutableArray *contactMutArr = [NSMutableArray array];
NSMutableString *mStrOfContacts = [NSMutableString string];

NSArray *contactsInAddressBook = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, kABPersonSortByLastName));

if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatLastNameFirst) {

    for (id aPerson in contactsInAddressBook) {

        ABRecordRef person = (__bridge ABRecordRef)(aPerson);

        ABMultiValueRef phoneMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty);
        ABMultiValueRef emailMultiValue = ABRecordCopyValue(person, kABPersonEmailProperty);
        int countPhone = 0;
        int countEmail = 0;
        NSMutableArray *phoneStrArr;
        NSMutableArray *emailStrArr;

        if (phoneMultiValue != NULL) {
            countPhone = ABMultiValueGetCount(phoneMultiValue);
        }

        if (emailMultiValue != NULL) {
            countEmail = ABMultiValueGetCount(emailMultiValue);
        }

        if (countEmail>0) {
            emailStrArr = [NSMutableArray array];
            for (int i = 0; i < countEmail; i++) {
                CFStringRef anEmailCF = ABMultiValueCopyValueAtIndex(emailMultiValue, i);
                NSString *anEmail = (__bridge NSString *)anEmailCF;
                [emailStrArr addObject:anEmail];
                if (anEmailCF != NULL)CFRelease(anEmailCF);
            }
        }

        if (countPhone > 0) {

            phoneStrArr = [NSMutableArray array];
            for (int i = 0; i < countPhone; i++) {
                CFStringRef anPhoneCF = ABMultiValueCopyValueAtIndex(phoneMultiValue, i);
                NSString *anPhone = (__bridge NSString *)anPhoneCF;
                NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
                NSString *anPhonePureNumber = [[anPhone componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
                [phoneStrArr addObject:anPhonePureNumber];
                if (anPhoneCF != NULL)CFRelease(anPhoneCF);
            }

        }
        //                if (arrRefOfEmails != NULL)CFRelease(arrRefOfEmails);


        CFStringRef lastNameMultiValueCF = ABRecordCopyValue(person, kABPersonLastNameProperty);
        CFStringRef firstNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonFirstNameProperty);
        CFStringRef middleNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonMiddleNameProperty);
        NSString *lastNameMultiValue = (__bridge NSString *)lastNameMultiValueCF;
        NSString *firstNmaeMultiValue = (__bridge NSString *)firstNmaeMultiValueCF;
        NSString *middleNmaeMultiValue = (__bridge NSString *)middleNmaeMultiValueCF;

        NSString *name = [NSString stringWithFormat:@"%@%@%@",(![lastNameMultiValue length])?@"":lastNameMultiValue, (![middleNmaeMultiValue length])?@"":middleNmaeMultiValue, (![firstNmaeMultiValue length])?@"":firstNmaeMultiValue];

        if (lastNameMultiValueCF != NULL)CFRelease(lastNameMultiValueCF);
        if (firstNmaeMultiValueCF != NULL)CFRelease(firstNmaeMultiValueCF);
        if (middleNmaeMultiValueCF != NULL)CFRelease(middleNmaeMultiValueCF);
        CFDataRef anAvatarCF = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);

        NSData *anAvatarData = (__bridge NSData *)anAvatarCF;
        UIImage *anAvatar = [UIImage imageWithData:anAvatarData];

        if (anAvatarCF != NULL)CFRelease(anAvatarCF);

        NSDictionary *aPersonDict = [NSDictionary dictionaryWithObjectsAndKeys:name, @"name", [phoneStrArr componentsJoinedByString:@"; "], @"phoneNumber", [emailStrArr componentsJoinedByString:@"; "], @"email", anAvatar, @"avatar", nil];
        [contactMutArr addObject:aPersonDict];

        NSLog(@"------phoneStrArr :%@",phoneStrArr);
        NSString *enPhoneNumber = @"";
        if (phoneStrArr) {
            enPhoneNumber = [EncryptWithMD5 encryptWithMD5: [phoneStrArr componentsJoinedByString:@"; "]];
        }
        [mStrOfContacts appendString:enPhoneNumber];
        [mStrOfContacts appendString:@", "];
        if (phoneMultiValue != NULL)CFRelease(phoneMultiValue);
        if (emailMultiValue != NULL)CFRelease(emailMultiValue);

    }

}else{

    for (id aPerson in contactsInAddressBook) {
        ABRecordRef person = (__bridge ABRecordRef)(aPerson);
        ABMultiValueRef phoneMultiValue = ABRecordCopyValue(person, kABPersonPhoneProperty);
        ABMultiValueRef emailMultiValue = ABRecordCopyValue(person, kABPersonEmailProperty);
        int countEmail = 0;
        NSMutableArray *emailStrArr;
        NSMutableArray *phoneStrArr;

        if (emailMultiValue != NULL) {
            countEmail = ABMultiValueGetCount(emailMultiValue);
        }

        if (countEmail>0) {
            emailStrArr = [NSMutableArray array];
            for (int i = 0; i < countEmail; i++) {
                CFStringRef anEmailCF = ABMultiValueCopyValueAtIndex(emailMultiValue, i);
                NSString *anEmail = (__bridge NSString *)anEmailCF;
                [emailStrArr addObject:anEmail];
                if (anEmailCF != NULL)CFRelease(anEmailCF);
            }
        }

        int count = ABMultiValueGetCount(phoneMultiValue);

        if (count > 0) {
            phoneStrArr = [NSMutableArray array];
            for (int i = 0; i < count; i++) {
                CFStringRef anPhoneCF = ABMultiValueCopyValueAtIndex(phoneMultiValue, i);
                NSString *anPhone = (__bridge NSString *)anPhoneCF;
                NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
                NSString *anPhonePureNumber = [[anPhone componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
                [phoneStrArr addObject:anPhonePureNumber];
                if (anPhoneCF != NULL)CFRelease(anPhoneCF);
            }
        }

        CFStringRef lastNameMultiValueCF = ABRecordCopyValue(person, kABPersonLastNameProperty);
        CFStringRef firstNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonFirstNameProperty);
        CFStringRef middleNmaeMultiValueCF = ABRecordCopyValue(person, kABPersonMiddleNameProperty);
        NSString *lastNameMultiValue = (__bridge NSString *)lastNameMultiValueCF;
        NSString *firstNmaeMultiValue = (__bridge NSString *)firstNmaeMultiValueCF;
        NSString *middleNmaeMultiValue = (__bridge NSString *)middleNmaeMultiValueCF;

        NSString *name = [NSString stringWithFormat:@"%@%@%@", (![firstNmaeMultiValue length])?@"":firstNmaeMultiValue, (![middleNmaeMultiValue length])?@"":middleNmaeMultiValue,(![lastNameMultiValue length])?@"":lastNameMultiValue];

        if (lastNameMultiValueCF != NULL)CFRelease(lastNameMultiValueCF);
        if (firstNmaeMultiValueCF != NULL)CFRelease(firstNmaeMultiValueCF);
        if (middleNmaeMultiValueCF != NULL)CFRelease(middleNmaeMultiValueCF);

        CFDataRef anAvatarCF = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);

        NSData *anAvatarData = (__bridge NSData *)anAvatarCF;
        UIImage *anAvatar = [UIImage imageWithData:anAvatarData];

        if (anAvatarCF != NULL)CFRelease(anAvatarCF);

        NSDictionary *aPersonDict = [NSDictionary dictionaryWithObjectsAndKeys:name, @"name", [phoneStrArr componentsJoinedByString:@"; "], @"phoneNumber",  [emailStrArr componentsJoinedByString:@"; "], @"email", anAvatar, @"avatar", nil];
        [contactMutArr addObject:aPersonDict];

        NSString *enPhoneNumber = [EncryptWithMD5 encryptWithMD5: [phoneStrArr componentsJoinedByString:@"; "]];
        [mStrOfContacts appendString:enPhoneNumber];
        [mStrOfContacts appendString:@", "];

        if (phoneMultiValue != NULL)CFRelease(phoneMultiValue);
        if (emailMultiValue != NULL)CFRelease(emailMultiValue);

    }


}
self.contactArr = [[NSArray alloc] initWithArray: contactMutArr];
strOfContacts = [NSString stringWithString:mStrOfContacts];
}

Edit 2

-(void)beginFetchContacts{
// Request authorization to Address Book
ABAddressBookRef addressBookRef = NULL;

if (ABAddressBookRequestAccessWithCompletion) {
    CFErrorRef *aError=nil;
    addressBookRef = ABAddressBookCreateWithOptions(NULL, aError);

    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
        ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
            // First time access has been granted, add the contact
            if (granted) {
                [self dofetchContacts:addressBookRef];
            }else{
                //                [self alertActionSwitchOnTheContactsAccess];
                [self buttonCancelPressed:nil];
            }
        });
    }
    else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
        // The user has previously given access, add the contact
        [self dofetchContacts:addressBookRef];
    }
}else{
    addressBookRef = ABAddressBookCreate();
    [self dofetchContacts:addressBookRef];
}

if (addressBookRef != NULL)CFRelease(addressBookRef);
}
Agglutinin answered 13/5, 2013 at 3:4 Comment(8)
If possible could you please provide your source code form where you are fetching contacts from address book? Few days back even i was getting a crash and it turned out to be the wrong release of Core Foundation object.Dominions
@Deepesh I thought I got the reason. It is on the ABPersonGetCompositeNameFormat.Agglutinin
Ok so finally the issue is resolved? May i know what was the issue ?Dominions
@Deepesh Im debugging it, once I fix it, I will post an answer:)Agglutinin
@Deepesh Oh, no, it's another bug:(Agglutinin
May i know what version of xCode, iOS version you are using? Is there any particular device on which this crash is occurring?Dominions
@Deepesh xCode4.6, iOS6.1, iPhone3.1Agglutinin
@Deepesh and: xCode4.6, iOS6.1.3, iPhone4.1 ; xCode4.6, iOS6.1.2, iPhone5.2Agglutinin
W
5

Could it be that you're calling ABAddressBookCreateWithOptions() and/or ABAddressBookRequestAccessWithCompletion() on a different thread from where you're calling ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering()?

Please note the following from Apple's API documentation:

The completion handler is called on an arbitrary queue. If your app uses an address book throughout the app, you are responsible for ensuring that all usage of that address book is dispatched to a single queue to ensure correct thread-safe operation.

Source: http://developer.apple.com/library/ios/#documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html

Alternatively, check to make sure you're not prematurely releasing the ABAddressBookRef you got back from ABAddressBookCreateWithOptions(). Remember that ABAddressBookRequestAccessWithCompletion() is asynchronous.

Webbed answered 28/6, 2013 at 18:22 Comment(1)
I have edit the post, May be the problem is related to if (addressBookRef != NULL)CFRelease(addressBookRef);Agglutinin
Q
6

I see that the crashed thread is "com.apple.root.default-priority"

ABAddressBook is NOT thread safe, so if you call it from two different threads, it throws an exception and crashes the app.

even if you always dispatch your calls to DISPATCH_QUEUE_PRIORITY_DEFAULT, it may run on two different threads, because DISPATCH_QUEUE_PRIORITY_DEFAULT is not a serial queue.

Use this to create your serial queue that dispatches to DISPATCH_QUEUE_PRIORITY_DEFAULT:

dispatch_queue_t abQueue = dispatch_queue_create("myabqueue", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(abQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));

Remember to dispatch (sync or async) all your calls to address book to this queue.

EDIT:

It looks like you are calling dofetchContacts in a completion handler block passed into the function ABAddressBookRequestAccessWithCompletion. Be sure to dispatch this call on the main thread!

The documentation says:

The completion handler is called on an arbitrary queue. If your app uses an address book throughout the app, you are responsible for ensuring that all usage of that address book is dispatched to a single queue to ensure correct thread-safe operation.

Quevedo answered 23/5, 2013 at 13:35 Comment(2)
Actually "dofetchContacts:" runs in main thread. "com.apple.root.default-priority" is the a note from CrashLytics.Agglutinin
No, actually this code runs not on the main thread, because the completion handler is called from a background queue.Taintless
W
5

Could it be that you're calling ABAddressBookCreateWithOptions() and/or ABAddressBookRequestAccessWithCompletion() on a different thread from where you're calling ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering()?

Please note the following from Apple's API documentation:

The completion handler is called on an arbitrary queue. If your app uses an address book throughout the app, you are responsible for ensuring that all usage of that address book is dispatched to a single queue to ensure correct thread-safe operation.

Source: http://developer.apple.com/library/ios/#documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html

Alternatively, check to make sure you're not prematurely releasing the ABAddressBookRef you got back from ABAddressBookCreateWithOptions(). Remember that ABAddressBookRequestAccessWithCompletion() is asynchronous.

Webbed answered 28/6, 2013 at 18:22 Comment(1)
I have edit the post, May be the problem is related to if (addressBookRef != NULL)CFRelease(addressBookRef);Agglutinin
T
0
- (IBAction)btn_addprofile:(id)sender 
{
    // creating the picker
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
    // place the delegate of the picker to the controll
    picker.peoplePickerDelegate = self;

    // showing the picker

    app.appstart=0;
    [self presentModalViewController:picker animated:YES];
    // releasing
    [picker release];
}

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker 
{
    // assigning control back to the main controller
    [self dismissModalViewControllerAnimated:YES];
}

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {

    add_profile_screen *viewcontroller=[[add_profile_screen alloc]initWithNibName:@"add_profile_screen" bundle:nil];



    // setting the first name

    NSString *str_f  =(NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    NSString *str_l=(NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);

    NSLog(@"%@",str_f);
    NSLog(@"%@",str_l);

    if([str_f isEqualToString:@""] || [str_l isEqualToString:@""] || !str_f || !str_l)
    {
        if([str_f isEqualToString:@""] || !str_f )
        {
            viewcontroller.strfirstname=[NSString stringWithFormat:@"%@",str_l];
        }
        else
        {
            viewcontroller.strfirstname=[NSString stringWithFormat:@"%@ ",str_f];
        }
    }
    else
    {
        viewcontroller.strfirstname=[NSString stringWithFormat:@"%@ %@",str_f,str_l];
    }

    // viewcontroller.strname=[NSString stringWithFormat:@"%@",(NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty)];



    ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);
    if (ABMultiValueGetCount(multi) > 0) 
    {
        // collect all emails in array
        // for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++)
        for (CFIndex i = 0; i <1; i++)
        {
            CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
            viewcontroller.strlastname= (NSString *)emailRef;
            CFRelease(emailRef);
        }
    }

    // setting the number
    ABMultiValueRef multi1 = ABRecordCopyValue(person, kABPersonPhoneProperty);
    viewcontroller.strnumber=[NSString stringWithFormat:@"%@",(NSString*)ABMultiValueCopyValueAtIndex(multi1, 0)];
    NSLog(@"%@",viewcontroller.strnumber);


    [self.navigationController pushViewController:viewcontroller animated:YES];
    [viewcontroller release];


        // remove the controller
    [self dismissModalViewControllerAnimated:YES];

    return NO;
}

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    return NO;
}
Theomorphic answered 28/6, 2013 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.