heardrwt / rhaddressbook Goto Github PK
View Code? Open in Web Editor NEWA Cocoa / Objective-C library for interfacing with the iOS AddressBook.
License: Other
A Cocoa / Objective-C library for interfacing with the iOS AddressBook.
License: Other
I'm trying to block program execution until the user grants access (or denies) access to the AddressBook, using a semaphore, however this seems to block the entire UI thread.
Using the code on http://stackoverflow.com/questions/12648244/programmatically-request-access-to-contacts-in-ios-6
If the semaphore is omitted, the execution continues (effectively loading no data), until the App is closed and reopened.
I have the following function:
- (RHAddressBook *) addressBook {
@synchronized (self) {
if (_ab == nil) {
_ab = [[RHAddressBook alloc] init];
if ([RHAddressBook authorizationStatus] == RHAuthorizationStatusNotDetermined) {
//request authorization
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[_ab requestAuthorizationWithCompletion:^(bool granted, NSError *error) {
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
}
return _ab;
}
}
Hit it during an in-development app, found a small amount of code that replicates the crash consistently on my iPhone 4S (iOS 6.1.2) and the simulator. Create an app with ARC, link against the necessary libs (AddressBook and CoreLocation).
ViewController.m:
#import "ViewController.h"
#import "RHAddressBook.h"
@interface ViewController ()
@property (nonatomic, strong) RHAddressBook *rhAddressBook;
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.queue = [[NSOperationQueue alloc] init];
self.rhAddressBook = [[RHAddressBook alloc] init];
if ([RHAddressBook authorizationStatus] == RHAuthorizationStatusNotDetermined){
//request authorization
[self.rhAddressBook requestAuthorizationWithCompletion:^(bool granted, NSError *error) {
}];
}
}
- (IBAction)crashPressed:(id)sender {
for(int i = 0; i < 100; i++) {
[self.queue addOperationWithBlock:^{
NSArray *people = self.rhAddressBook.people;
}];
}
}
@end
crashPressed is an outlet I connected to a button inside my ViewController nib. A single tap crashes, and from the location of the crash it seems like it's trying to send a message to a freed object. To verify I enabled zombie objects and then the program started crashing with the following log message:
*** -[RHPerson release]: message sent to deallocated instance 0x1d0c2510
I'll gladly send my zipped project if you wish. I didn't verify this using the provided test-case, but I would get the same crashes with and without ARC in RHAddressBook (my code always had ARC), and with and without USE_REF_MAP and USE_PERSON_ID_MAP. I didn't exhaustively try all the combinations of those parameters, but I tried more than half.
While playing around with RHAddressBook for an app i'm working on, I noticed that the people
method in RHAddressBook.m will return duplicates if the contact exists in multiple sources. This seems to be a common issue, related to the underlying API.
Googling around, I came across this stackoverflow thread which seems to offer up a decent algorithm for a solution.
I think this would make a good addition to RHAddressBook. Something like this, but probably with better method names..
-(NSArray*)peopleWithoutLinkedDuplicates; // This would return an array of all users with 'linked duplicates' removed, returning the person from the default source for any duplicates (convenience method for below)
-(NSArray*)peopleWithoutLinkedDuplicatesShowingSource:(RHSource *)source; // This would return an array of all users with 'linked duplicates' removed, returning the person from the supplied source for any duplicates
I'd be happy to look at implementing this and sending a pull request if you'd like. Just let me know how you'd want the methods named, etc.
Hi,
I found out that the cache building operation takes a very long time. I have 800+ contacts on iCloud and this step takes more than 5sec at app launch. It is not that bad, but still can be an issue.
I ran instruments and discovered that 70%+ of this time is spent on -[RHAddressBookGeoResult initWithPersonID:addressID]
-> -[RHAddressBookGeoResult associatedAddressDictionary]
-> ABAddressBookCreateWithOptions()
or ABAddressBookCreate()
.
By skipping the if(addesses)
part in -[RHAddressBookSharedServices rebuildCache]
I was able to speed things up to a quick 300ms startup.
If I understand well the goal of this if
it is to get coordinate of every addresses, so I definitely need it for my app, but can't we prevent this time consuming operation by sharing an addressBookRef instance ?
Stan
Multiple error occurred when compiling with Xcode 4.5 GM when ARC is enabled.
Here are some errors:
/Volumes/Data HD/Users/Kai/Downloads/heardrwt-RHAddressBook-62338d9/RHAddressBook/RHAddressBook.m:187:5: Cast of block pointer type 'void (^)(bool, NSError *__strong)' to C pointer type 'const void *' requires a bridged cast
/Volumes/Data HD/Users/Kai/Downloads/heardrwt-RHAddressBook-62338d9/RHAddressBook/RHAddressBook.m:187:5: Cast of C pointer type 'void *' to block pointer type 'typeof (completion)' (aka 'void (^__strong)(bool, NSError *__strong)') requires a bridged cast
/Volumes/Data HD/Users/Kai/Downloads/heardrwt-RHAddressBook-62338d9/RHAddressBook/RHAddressBook.m:196:37: Cast of C pointer type 'CFErrorRef' (aka 'struct __CFError *') to Objective-C pointer type 'NSError *' requires a bridged cast
/Volumes/Data HD/Users/Kai/Downloads/heardrwt-RHAddressBook-62338d9/RHAddressBook/RHAddressBook.m:198:17: Cast of block pointer type 'void (^)(bool, NSError *__strong)' to C pointer type 'const void *' requires a bridged cast
/Volumes/Data HD/Users/Kai/Downloads/heardrwt-RHAddressBook-62338d9/RHAddressBook/RHAddressBook.m:211:9: Cast of block pointer type 'void (^)(bool, NSError *__strong)' to C pointer type 'const void *' requires a bridged cast
I am working on a App which Syncs new/existing contacts with the default Address book. To avoid duplicates I am searching for person in the AddressBook and if I get any results I simply update the Person. Furthermore, to avoid duplication of phone numbers and email addresses within the Person I do the following,
//Set phone number if exists
if(user.phone){
RHMultiStringValue *phoneMultiValue = [person phoneNumbers];
RHMutableMultiStringValue *mutablePhoneMultiValue = [phoneMultiValue mutableCopy];
if (! mutablePhoneMultiValue) mutablePhoneMultiValue = [[RHMutableMultiStringValue alloc] initWithType:kABMultiStringPropertyType];
//Avoid Duplicates
NSInteger phoneIndex = [mutablePhoneMultiValue firstIndexOfValue:user.phone];
if(phoneIndex == -1){
[mutablePhoneMultiValue addValue:user.phone withLabel:RHPersonPhoneIPhoneLabel];
person.phoneNumbers = mutablePhoneMultiValue;
}
}
//Set email if exists
if( user.email){
RHMultiStringValue *emailMultiValue = [person emails];
RHMutableMultiStringValue *mutableEmailMultiValue = [emailMultiValue mutableCopy];
if (! mutableEmailMultiValue) mutableEmailMultiValue = [[RHMutableMultiStringValue alloc] initWithType:kABMultiStringPropertyType];
//Avoid duplicates
NSInteger emailIndex = [mutableEmailMultiValue firstIndexOfValue:user.email];
if(emailIndex == -1){
[mutableEmailMultiValue addValue:user.email withLabel:RHWorkLabel];
person.emails = mutableEmailMultiValue;
}
}
This works great for users that are already in the AddressBook but when I created a new user with,
[RHPerson newPersonInSource:[self.addressBook defaultSource]];
Things get funky!!
[person emails] and [person phoneNumbers] returns an array with pre-populated values from random contacts in the AddressBook even though a new RHPerson was created.
I did manage to workaround it forcing initialization on setting emails and phoneNumbers when a new Person is created as follows,
person.emails = [[RHMultiStringValue alloc] init];
person.phoneNumbers = [[RHMultiStringValue alloc] init];
This fixed my issue but would like to dig deep as to why this is happening.
It will be great to add Cocoapods support !
RHAddressBookSharedServices is still using ABAddressBookCreate() which is deprecated. _addressBook is NULL and rebuildCache will fail with EXEC_BAD_ACCESS.
Hi, I want to display the phone number on UILabel. But it is RHMultiValue
.
I am getting this error [RHPerson phoneNumbers]: message sent to deallocated instance intermittently.
It all happens in a single function similar to this:
NSArray *people = [addressBook people];
for (RHPerson *person in people)
{
RHMultiValue *phoneNumbers = [person phoneNumbers];
}
It seems to me that the records are not retained properly.
I did spend time trying to identify the problem, but no luck.
BTW, I am using ARC and there is no internal/external change to the address book in my test.
Hi
I cannot find a way to have the name of a source, as it would appear in Contacts app, after calling group button. I expected something like "iCloud" or "Exchange (work)" etc but I cannot find a way to get these names.
So how can I get those names ?
And what is the "name" property of RHSource used for ?
Thanks
Hello,
I tried to use RHAddressBook, but I'm going in circles with an issue that's already been reported.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSThread rh_performBlock:]: unrecognized selector sent to instance 0x124991f0'
*** First throw call stack:
(
0 CoreFoundation 0x03a4c1e4 exceptionPreprocess + 180
1 libobjc.A.dylib 0x031fa8e5 objc_exception_throw + 44
2 CoreFoundation 0x03ae9243 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x03a3c50b __forwarding + 1019
4 CoreFoundation 0x03a3c0ee _CF_forwarding_prep_0 + 14
5 yy 0x000e6b19 -[RHAddressBookSharedServices init] + 537
6 yy 0x000e6833 +[RHAddressBookSharedServices sharedInstance] + 195
The library was created using -Objc -all_load, as suggested. But so far, no luck.
Any idea what I'm doing wrong ?
thanks in advance,
-M
There's a compiler warning in RHAddressBook.m:
'dispatch_get_current_queue()' is deprecated: first deprecated in iOS 6.0
in the method rh_dispatch_is_current_queue_for_addressbook()
I'm seeing my app hang frequently on this code ever since upgrading to XCode 5.1 and iOS 7.1. It happens when the app is installed on a device (more consistently for iPad Mini than iPhone 5s) but not in the simulator.
-(void)rh_performBlock:(VoidBlock)block waitUntilDone:(BOOL)wait{
//if current thread and wait (run directly)
if ([[NSThread currentThread] isEqual:self] && wait){
block(); return;
}
[self performSelector:@selector(_rh_runBlock:) onThread:self withObject:arc_autorelease([block copy]) waitUntilDone:wait];
}
Hi Richard,
it would be great to add a convenience method "name" to RHPerson.
It is great for debugging.
-(NSString *)name {
NSMutableString *string = [NSMutableString string];
if (self.firstName || self.lastName)
{
if (self.prefix) [string appendFormat:@"%@ ", self.prefix];
if (self.firstName) [string appendFormat:@"%@ ", self.firstName];
if (self.nickname) [string appendFormat:@"\"%@\" ", self.nickname];
if (self.lastName) [string appendFormat:@"%@", self.lastName];
if (self.suffix && string.length)
[string appendFormat:@", %@ ", self.suffix];
else
[string appendFormat:@" "];
}
if (self.organization) [string appendString:self.organization];
return [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
This was taken from Erica Sadun' s ABContact.m file.
Downloaded and crashed using RHAddressBookTester target on a REAL device.
Steps to reproduce:
Nothing in logs show any trace of what happened.
Hello,
I'm experiencing some unusual and inconsistent crashes when trying to access the thumbnail of an RHPerson object. I have about 6 crash reports, all with similar stack-traces.
I'm building against iOS SDK 6.0, the app is running on iOS 6.01 and is built with ARC, using the latest RHAddressBook code.
The stacktraces all look similar:
Exception Type: SIGTRAP
Exception Codes: #0 at 0x39a5d68a
Crashed Thread: 4
Thread 0:
...
5 Fliple 0x00063911 -[NSThread(RHBlockAdditions) rh_performBlock:waitUntilDone:] + 157
6 Fliple 0x0005cc31 -[RHAddressBook performAddressBookAction:waitUntilDone:] + 121
7 Fliple 0x00063b4b -[RHRecord performRecordAction:waitUntilDone:] + 127
8 Fliple 0x00066e41 -[RHPerson imageWithFormat:] + 177
9 Fliple 0x00066d77 -[RHPerson thumbnail] + 23
10 Fliple 0x00022ac1 -[ctzDictionary tableView:cellForRowAtIndexPath:] (ctzDictionary.m:568)
...
Thread 4 Crashed:
...
0 CoreFoundation 0x39a5d68a _CFRelease + 18
1 Fliple 0x00063b8d __46-[RHRecord performRecordAction:waitUntilDone:]_block_invoke_0 + 37
2 Fliple 0x0005cc51 __56-[RHAddressBook performAddressBookAction:waitUntilDone:]_block_invoke_0 + 29
3 Fliple 0x00063a49 -[NSThread(RHBlockAdditions) _rh_runBlock:] + 13
...
Most of the time the code runst just fine, except for some rare cases (that I couldn't reproduce so far), when it crashes.
My only suspicion is that the user has added/removed a contact source (such as Exchange/Facebook) and iOS is still adding/removing contacts in the background.
If this is the case, how could I protect my code against this kind of behavior?
Hey there,
I've been using RHAddressBook for one of my projects and it's been working great! Would you mind submitting it to Cocoapods, or if you want, I'll go ahead and submit it for you.
Thanks
EXC_BAD_ACCESS
Happen when I do
[person setImage:image];
and then
[addressBook save];
crash is on
-(BOOL)hasUnsavedChanges{
in RHAddressBook.m
(line 1055), v1.1.1
any ideas?
With a large number of contacts, calling
[[RHAddressBook alloc] init];
can take a few minutes and block the main thread. It seems to be blocking on the rebuildCache method. Wrapping in a GCD async dispatch fixes the problem, but it still takes a few minutes before the address book is accessible. Is there anything I can do to speed up the cache rebuild?
Hi Richard,
I am reopening my previous issue slightly different, since by using Cocoapods you cannot exclude geolocation by having a define in the projects .pch but a change in the RHAddressBook.h file is required.
So if the project is updated one needs to change it again.
It would be more flexible to add geolocation at runtime instead.
I just added RHAddressBook to my iOS project using CocoaPods. I imported the appropriate header file at the top of one of my ViewControllers:
#import <RHAddressBook/AddressBook.h>
Then, in viewDidLoad
, I allocated and initialized an RHAddressBook
instance:
RHAddressBook *ab = [[RHAddressBook alloc] init];
The app builds fine, but crashes on that line due to an uncaught exception. Relevant console output:
-[RHAddressBookThreadMain threadMain:]:39 spawned thread: <NSThread: 0xa624c50>{name = RHAddressBookInstanceThread for instance 0xa6265e0, num = 5}
-[NSThread rh_performBlock:]: unrecognized selector sent to instance 0xa61aef0
-[RHAddressBookThreadMain threadMain:]:39 spawned thread: <NSThread: 0xa61aef0>{name = RHAddressBookSharedServicesThread for 0xa624dd0, num = 6}
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSThread rh_performBlock:]: unrecognized selector sent to instance 0xa61aef0'
My project targets iOS 6 and is built with ARC enabled (though the RHAddressBook code itself is not built with ARC).
Stepping through the code, this exception is thrown the first time rh_performBlock
is sent to an instance of NSThread
. It seems like the RHBlockAdditions category is not being properly added to the NSThread class... Any ideas as to why this might be happening?
Hi Richard,
if your amazing library is linked via cocoaPods we have to change every time the flag that controls the inclusion/exclusion of geolocation in the library. This could easily lead to mistakes (longer init times). I think it's an easy fix.
Merry Christmas,
Nikos
In some cases I find that contacts are not being saved when using code like this:
RHPerson *person = [self getPersonWithNumber:number];
RHMutableMultiStringValue* mutablePhoneMultiValue = [[RHMutableMultiStringValue alloc] initWithType:kABMultiStringPropertyType];
[mutablePhoneMultiValue addValue:number withLabel:details];
person.phoneNumbers = mutablePhoneMultiValue;
person.firstName = @"John Smith";
[person save];
The code is hit but it is not reflected in the address book. Any suggestions? Could it be a threading issue?
this doesn't work, unfortunately:
[socialProfiles addValue:@"myKey" withLabel:RHPersonSocialProfileServiceKey];
[socialProfiles addValue:@"myValue" withLabel:RHPersonSocialProfileUsernameKey]
I'm looking for a replacement for
ABMultiValueRef social = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
{
CFStringRef valueCFString = (__bridge CFStringRef) value;
CFStringRef keys[] = {
kABPersonSocialProfileServiceKey,
kABPersonSocialProfileUsernameKey
}, values[] = {
CFSTR("myKey"),
CFSTR("myValue")
};
ABMultiValueAddValueAndLabel(social, CFDictionaryCreate(kCFAllocatorDefault, (void *) keys, (void *) values, 3, NULL, NULL), NULL, NULL);
}
CFErrorRef error;
ABRecordSetValue(rhPerson.recordRef, kABPersonSocialProfileProperty, social, &error);
CFRelease(social);
thanks a LOT!
to serialize all RHPerson data fields into one data object
I'll preface this ticket with the fact that I'm a new iOS dev (coming from python/frontend tools-land).
I've built the static library, ensuring to set the relevant flags:
Copied over the output and added it to my project:
I have no trouble importing the .h file:
#import "RHAddressBook/AddressBook.h"
However as soon as I try to instantiate an RHAddressBook instance, my app crashes at runtime:
RHAddressBook *ab = [[RHAddressBook alloc] init];
causes:
2013-05-16 17:30:52.755 Braid[6970:4003] -[RHAddressBookThreadMain threadMain:]:39 spawned thread: <NSThread: 0x777ad00>{name = RHAddressBookSharedServicesThread for 0x777abb0, num = 4}
2013-05-16 17:30:52.755 Braid[6970:370f] -[RHAddressBookThreadMain threadMain:]:39 spawned thread: <NSThread: 0x777a990>{name = RHAddressBookInstanceThread for instance 0x777a870, num = 3}
2013-05-16 17:30:52.755 Braid[6970:c07] -[NSThread rh_performBlock:]: unrecognized selector sent to instance 0x777ad00
2013-05-16 17:30:52.768 Braid[6970:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSThread rh_performBlock:]: unrecognized selector sent to instance 0x777ad00'
*** First throw call stack:
(0x1bfc012 0x1518e7e 0x1c874bd 0x1bebbbc 0x1beb94e 0x40fc1 0x40c54 0x34c8c 0x33bd 0x53e1c7 0x53e232 0x53e4da 0x5558e5 0x5559cb 0x555c76 0x555d71 0x55689b 0x5569b9 0x556a45 0x65c20b 0x4ad2dd 0x152c6b0 0x247fc0 0x23c33c 0x247eaf 0x54c2bd 0x494b56 0x49366f 0x493589 0x4927e4 0x49261e 0x4933d9 0x4962d2 0x54099c 0x48d574 0x48d76f 0x48d905 0x496917 0x2c9f 0x45a157 0x45a747 0x45b94b 0x46ccb5 0x46dbeb 0x45f698 0x1b57df9 0x1b57ad0 0x1b71bf5 0x1b71962 0x1ba2bb6 0x1ba1f44 0x1ba1e1b 0x45b17a 0x45cffc 0x299d 0x28c5)
libc++abi.dylib: terminate called throwing an exception
Any pointers to help me figure out what I'm doing incorrectly? Google and I have been best buddies for a couple of hours now, to no avail.
After receiving address book authorization a call to
[RHPerson vCardRepresentationForPeople:[addressBook people]]
results in a crash. Digging into the source it appears that while vCardRepresentationForPeople
expects an array of RHPerson
objects, ABPersonCreateVCardRepresentationWithPeople
expects a CFArray
of ABRecordRef
objects. Perhaps the ABRecordRef
objects can be extracted from each RHPerson
prior to the call to ABPersonCreateVCardRepresentationWithPeople
.
I have found that on very large datasets the cache takes a really long time to build.
This is obvious as the addressbook tries to geocode every address, this might be ok if the plan is to use the data later.
But in case this data is not relevant I have not found a way to disable this functionality.
The rest of the framework works really well and its a pleasure to use, thank you for building it!
I will try to get around sending a pull request once I get the basic architecture of the framework down.
RHAddressBook uses these two deprecated methods. They need to be replaced.
ABAddressBookCreate() and ABPersonGetCompositeNameFormat()
Solution here: https://devforums.apple.com/message/708024#708024
My App is crashing when the addressbook is changed. How do I debug this?
AddressBook
ABAddressBookHasUnsavedChanges + 79
1
AddressBook
ABLogAPIUsage + 26
2
RHAddressBook.m line 1055
34-[RHAddressBook hasUnsavedChanges]_block_invoke
3
libdispatch.dylib
_dispatch_client_callout + 22
4
libdispatch.dylib
_dispatch_barrier_sync_f_slow + 230
5
RHAddressBook.m line 1054
-[RHAddressBook hasUnsavedChanges]
6
RHAddressBook.m line 1069
-[RHAddressBook addressBookExternallyChanged:]
7
CoreFoundation
__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER + 12
8
CoreFoundation
_CFXNotificationPost + 1720
9
Foundation
-[NSNotificationCenter postNotificationName:object:userInfo:] + 72
10
Foundation
-[NSNotificationCenter postNotificationName:object:] + 30
11 libdispatch.dylib
_dispatch_call_block_and_release + 10
19 UIKit
UIApplicationMain + 1136
20
main.m line 16
main
I'm seeing when modifying contacts in a gmail account, that sometimes the contacts revert to the state they were in before the change (or in other cases a duplicate is added in the previous state). I assume this is not an issue with RHAddressbook, but I was wondering if you had seen this issue before.
Thanks,
John
Hello,
I encountered a strange crash and I have no clue where to start investigating. I thought maybe you could offer some insight or maybe you encountered similar situations.
The code looks something like this:
NSError *err;
RHGroup *grp = [_addressBook newGroupInDefaultSource];
grp.name = groupName;
if (![_addressBook save: &err]) {
errMsg = [NSString stringWithFormat:NSLocalizedString(@"Error adding group. Error: %@", nil), err.description];
return nil;
}
return grp;
Sometimes this piece of code generates the following crash (relevant stack-traces below)
Thread 6 Crashed:
0 libsystem_kernel.dylib 0x320d6350 __pthread_kill + 8
1 libsystem_c.dylib 0x354a1973 abort + 95
2 MyApp 0x0012dd8b uncaught_exception_handler + 27
3 CoreFoundation 0x3a3e057f __handleUncaughtException + 615
4 libobjc.A.dylib 0x342cfa65 _objc_terminate() + 129
5 libc++abi.dylib 0x385bf07b safe_handler_caller(void (*)()) + 79
6 libc++abi.dylib 0x385bf114 std::terminate() + 20
7 libc++abi.dylib 0x385c0599 __cxa_current_exception_type + 1
8 libobjc.A.dylib 0x342cf9d1 objc_exception_rethrow + 13
9 CoreFoundation 0x3a326f21 CFRunLoopRunSpecific + 457
10 CoreFoundation 0x3a326d49 CFRunLoopRunInMode + 105
11 Foundation 0x3513778f -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 255
12 MyApp 0x000ff91b -[RHAddressBookThreadMain threadMain:] + 283
13 Foundation 0x351e467d __NSThread__main__ + 973
14 libsystem_c.dylib 0x35447311 _pthread_start + 309
Thread 0:
0 libsystem_kernel.dylib 0x320d608c __psynch_cvwait + 24
1 libsystem_c.dylib 0x35449f19 pthread_cond_wait + 41
2 Foundation 0x35160ccf -[NSCondition wait] + 195
3 Foundation 0x35137d6f -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 655
4 Foundation 0x35161865 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] + 109
5 MyApp 0x000fa8e9 -[NSThread(RHBlockAdditions) rh_performBlock:waitUntilDone:] + 157
6 MyApp 0x000fa84b -[NSThread(RHBlockAdditions) rh_performBlock:] + 23
7 MyApp 0x000f7b1b -[RHAddressBook save:] + 215
8 MyApp 0x000dfba7 +[Global addGroupNamed:withError:] (Global.m:332)
9 MyApp 0x000e4fdf -[GroupListController addGroupWithName:] (GroupListController.m:301)
10 MyApp 0x000e4daf __36-[GroupListController onAddClicked:]_block_invoke_0 (GroupListController.m:267)
11 MyApp 0x000e61ef -[ctzAlertView dismissWithClickedButtonIndex:animated:] (ctzAlertView.m:189)
12 UIKit 0x335fd0a5 -[UIApplication sendAction:to:from:forEvent:] + 73
13 UIKit 0x335fd057 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 31
14 UIKit 0x335fd035 -[UIControl sendAction:to:forEvent:] + 45
15 UIKit 0x335fc8eb -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 503
16 UIKit 0x335fcde1 -[UIControl touchesEnded:withEvent:] + 489
17 UIKit 0x335255f1 -[UIWindow _sendTouchesForEvent:] + 525
18 UIKit 0x33512801 -[UIApplication sendEvent:] + 381
19 UIKit 0x3351211b _UIApplicationHandleEvent + 6155
20 GraphicsServices 0x386f55a3 _PurpleEventCallback + 591
21 GraphicsServices 0x386f51d3 PurpleEventCallback + 35
22 CoreFoundation 0x3a3b5173 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 35
23 CoreFoundation 0x3a3b5117 __CFRunLoopDoSource1 + 139
24 CoreFoundation 0x3a3b3f99 __CFRunLoopRun + 1385
25 CoreFoundation 0x3a326ebd CFRunLoopRunSpecific + 357
26 CoreFoundation 0x3a326d49 CFRunLoopRunInMode + 105
27 GraphicsServices 0x386f42eb GSEventRunModal + 75
28 UIKit 0x335662f9 UIApplicationMain + 1121
29 MyApp 0x000b9315 main (main.m:19)
Thread 11 name: Exception Backtrace
Thread 11:
0 CoreFoundation 0x3a3e02a3 __exceptionPreprocess + 163
1 libobjc.A.dylib 0x342cf97f objc_exception_throw + 31
2 CoreFoundation 0x3a3e01c5 -[NSException initWithCoder:] + 1
3 CoreFoundation 0x3a3579e5 -[__NSCFSet addObject:] + 157
4 Fliple 0x000f7117 __26-[RHAddressBook addGroup:]_block_invoke_0 + 219
5 Fliple 0x000faa25 -[NSThread(RHBlockAdditions) _rh_runBlock:] + 17
6 Foundation 0x351e48ed __NSThreadPerformPerform + 461
7 CoreFoundation 0x3a3b5683 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
8 CoreFoundation 0x3a3b4ee9 __CFRunLoopDoSources0 + 213
9 CoreFoundation 0x3a3b3cb7 __CFRunLoopRun + 647
10 CoreFoundation 0x3a326ebd CFRunLoopRunSpecific + 357
11 CoreFoundation 0x3a326d49 CFRunLoopRunInMode + 105
12 Foundation 0x3513778f -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 255
13 Fliple 0x000ff91b -[RHAddressBookThreadMain threadMain:] + 283
14 Foundation 0x351e467d __NSThread__main__ + 973
15 libsystem_c.dylib 0x35447311 _pthread_start + 309
16 libsystem_c.dylib 0x354471d8 thread_start + 8
Sir, could you advise please? I'm trying to create instance of RHMultiStringValue but it doesn't work. I did everything as procedure. In the same controller RHAddressBook is working person retrieved but... What can be the cause?
RHAddressBook
API is great, but performance lags far behind direct ABAddressBook
access. Simple example running on iPhone 5s
NSDate *date;
date = [NSDate date];
NSTimeInterval taken;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, NULL);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, NULL, ABPersonGetSortOrdering());
taken = [[NSDate date] timeIntervalSinceDate:date];
for (id person in (__bridge NSArray *)allPeople) {
// Get the name of the contact
NSString *firstName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonLastNameProperty);
id email = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonEmailProperty);
id phone = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonPhoneProperty);
}
NSLog(@"abaddressbook took %f seconds %ld", taken, CFArrayGetCount(allPeople));
date = [NSDate date];
RHAddressBook *book = [[RHAddressBook alloc] init];
NSArray *people = [book peopleOrderedByUsersPreference];
for (RHPerson *person in people) {
person.firstName;
person.lastName;
person.phoneNumbers;
person.emails;
}
taken = [[NSDate date] timeIntervalSinceDate:date];
NSLog(@"rhaddressbook took %f seconds %ld", taken, (long)people.count);
And logs
2013-10-20 12:25:46.806 [813:60b] abaddressbook took 0.057833 seconds 779
2013-10-20 12:25:47.260 [813:60b] rhaddressbook took 0.452621 seconds 779
An order of magnitude slower :( There ought to be a way to access the RHAddressBook in a high performance manner without all the overhead of waiting for secondary thread synchronously
I've noticed that the external changes notification is fired multiple times even when my app is the one manipulating the contacts. This sort of creates an infinite loop situation as my app modifies the contacts when they are changed (which then triggers a notification). I've also noticed that it often gets called multiple times when I do manipulate the contacts manually outside the app. I only have one RHAddressbook instance that I create. Is this a known issue?
Thanks a lot,
John
I was impressed by the feature set provided by the library.
My old library (ABContactHelper by Erica Sadun) was replaced in half an hour.
But to my surprise, it was more than 2-3 times slower.
Can you take look at this?
It took a while of searching around to figure out that inSource
was the way to get the source. I think it would be more obvious if it was just source
.
For backwards compatibility a convenience alias could be made of source
calling back to inSource
?
Thank you for the wonderful library!
My issue:
I have a contacts application I'm trying to write, and I need to extract emails and phone numbers out of the device contacts. One consideration I've made is, if there are more than one HOME email or phone number (or any other type), I'll just grab the first one and not all of them.
Here is my code and I just can't seem to get the correct email / phone number. Am I doing this right?
NSArray *allContacts = [ab peopleOrderedByFirstName];
for(RHPerson *p in allContacts){
Person *person = [[Person alloc] init];
NSString *firstName = [p firstName];
NSString *lastName = [p lastName];
NSString *fullName = [p name];
NSString *companyName = [p organization];
if (firstName) {
person.firstName = firstName;
}
if (lastName) {
person.lastName = lastName;
}
if (fullName) {
person.fullName = fullName;
}
if (companyName) {
person.companyName = companyName;
}
//email
RHMultiValue *emailsMultiValue = [p emails];
if(emailsMultiValue != nil){
// NSString *homeEmailLabel = [RHPerson localizedLabel:@"Home"]; //eg Home
NSString *homeEmail = [emailsMultiValue valueAtIndex:[emailsMultiValue indexForIdentifier:0]];
NSString *test = [emailsMultiValue multiValueRef];
if (homeEmail) {
person.homeEmail = homeEmail;
}
// NSString *homeEmailLabel = [RHPerson localizedLabel:[addressesMultiValue 1]]; //eg Work
NSString *workEmail = [emailsMultiValue valueAtIndex:[emailsMultiValue indexForIdentifier:1]];
if (workEmail) {
person.workEmail = workEmail;
}
}
//Phone
RHMultiStringValue *phonesMultiValue = [p phoneNumbers];
if (phonesMultiValue != nil) {
// NSString *homePhoneLabel = [RHPerson localizedLabel:[phonesMultiValue 0]]; //eg Home
NSString *homePhone = [phonesMultiValue valueAtIndex:0];
if(homePhone){
person.homePhone = homePhone;
}
NSString *workPhone = [phonesMultiValue valueAtIndex:1];
if(workPhone){
person.workPhone = workPhone;
}
NSString *cellPhone = [phonesMultiValue valueAtIndex:2];
if(cellPhone){
person.cellPhone = cellPhone;
}
}
Note:
This is working perfectly if the user has exactly one of each of the fields that I am trying to pull out for email and phone number...not if they don't have them or have too many...
After updating to 1.1.0 problems with arc appeared. I do use a folder instead of packaging library to be able to make changes in source code. I have set the -all-load and -ObjC flags to "Other linker flags" and -fno-objc-arc to the files. Then tried different flags combinations but nothing worked for me.
Even tried moving from arc_autorelease to cfrelease and other similar solutions. Do you have any ideas on what should I do to use your lib with ARC?
According to the CocoaPods documentation:
If you do not explicitly set the list of public header files, all headers of source_files will be made public.
s.public_header_files = 'Classes/*/.h'
In your XCode project, you specify 6 private headers:
RHAddressBookSharedServices.h
RHAddressBookGeoResult.h
NSThread+RHBlockAdditions.h
RHAddressBookThreadMain.h
RHAddressBook_Private.h
RHAddressBook/RHRecord_Private.h
Unless I'm missing something, it seems like the CocoaPods podspec should specify your public headers so that the above 6 headers are treated as private when CocoaPods builds RHAddressBook into its static library?
I think this is a question more than an issue. Any one know about this?
In my app, I try to monitor the RHAddressBookExternalChangeNotification, and reload my contacts data feeds when received the notification.
But when I change the contact profile internally, and invoke the RHAddressBook save method, then I got 1~3 RHAddressBookExternalChangeNotification, I don't understand this.
Does RHAddressBookExternalChangeNotification means the address book is changed by external applications? Not the current app......
Thanks.
If the access to contact is not activated in settings:
we have this error:
[RHAddressBookSharedServices init]:125 Error: Failed to create RHAddressBookSharedServices instance. Underlying ABAddressBookCreateWithOptions() failed with error: Error Domain=ABAddressBookErrorDomain Code=1 "The operation couldn’t be completed. (ABAddressBookErrorDomain error 1.)"
and then, the application is blocked in that code (see the '---->' mark):
-(void)deregisterForAddressBookChanges{
if (![[NSThread currentThread] isEqual:_addressBookThread]){
----> [self performSelector:_cmd onThread:_addressBookThread withObject:nil waitUntilDone:YES];
return;
}
....
This because _addressBookThread is Nil.
to solve this problem, I have test if we are not in that situation, then the code will be
-(void)deregisterForAddressBookChanges{
if(_addressBookThread != nil) {
if (![[NSThread currentThread] isEqual:_addressBookThread]){
[self performSelector:_cmd onThread:_addressBookThread withObject:nil waitUntilDone:YES];
return;
}
} else {
return;
}
If you have other solution, I am taker...
Thanks!
adding those methods will allow to add the addressbook.people NSArray as NSSets so they can be compared/intersected/minus'd etc. Useful.
i think arc is very popular, can you support it?
Hello,
Is there a way to disable geocoding support without needing to edit RHAddressBook.h?
I tried wrapping #define RH_AB_INCLUDE_GEOCODING 0
in an #ifndef
and defining it in my prefix header, but the RHAddressBook library is built before my pch file is run, so I can't override it that way.
The reason I don't want to edit RHAddressBook.h is that I can't persist that change to the repo (and thus my other developers) unless I fork RHAddressBook.
Thanks,
Tommy
Here's the crash:
2013-01-08 01:17:51.775 XXXX[2851:50d7] -[__NSCFString count]: unrecognized selector sent to instance 0x1fb025c0
2013-01-08 01:17:51.776 XXXX[2851:50d7] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString count]: unrecognized selector sent to instance 0x1fb025c0'
Here's the associated code:
RHMultiStringValue *imMultiValue = [person instantMessageServices];
RHMutableMultiStringValue *mutableIMMultiValue = [imMultiValue mutableCopy];
if (! mutableIMMultiValue) mutableIMMultiValue = [[RHMutableMultiStringValue alloc] initWithType:kABMultiStringPropertyType];
[mutableIMMultiValue addValue: stringVar withLabel: RHPersonInstantMessageUsernameKey];
person.instantMessageServices = mutableIMMultiValue;
[person save];
I noticed that it behaves better by not crashing if I pass in a string literal as the value (the string I am using comes from a string decoded from JSON). However, the value is never added, only the label.
I believe this is not a bug in RHAddressBook, but I thought I may ask here. I'm registering to listen for RHAddressBookExternalChangeNotification. I modify a contact in the native Contacts app, go back to my app and receive the notification as expected.
But a few seconds later, without taking any action in my app, I get 3 or 4 more callbacks.
It's actually RHAddressBookExternalChangeCallback that gets called by the iOS SDK, so this callbacks come from iOS itself.
I'm checking what has changed by iterating over the contacts and comparing the "modified" property.
So I go back to the app, get a notification and this is logged:
2014-05-22 13:26:15.937 yyy[2584:3107] CONTACT XXXXXX modification date: 2014-05-22 12:26:11 +0000
A few seconds later, I get another notification and this is logged:
2014-05-22 13:26:23.763 yyy[2584:3107] CONTACT XXXXXX modification date: 2014-05-22 12:26:23 +0000
The same contact seems to have been modified between the notifications, but my app is not doing that at all. In fact, it never writes to the address book.
Any ideas?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.