From 535e4add7a0d794b288639cbed5fcf637bd4b5d7 Mon Sep 17 00:00:00 2001 From: Juan Felipe Alvarez Saldarriaga Date: Wed, 22 Apr 2015 19:27:19 -0500 Subject: [PATCH 1/5] adds podspec file based on https://gist.github.com/heardrwt/1ed602417c7d3b9daa96. --- RHAddressBook.podspec | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 RHAddressBook.podspec diff --git a/RHAddressBook.podspec b/RHAddressBook.podspec new file mode 100644 index 0000000..08003d6 --- /dev/null +++ b/RHAddressBook.podspec @@ -0,0 +1,40 @@ +Pod::Spec.new do |s| + s.name = 'RHAddressBook' + s.version = '1.2.0' + s.homepage = 'https://github.com/heardrwt/RHAddressBook' + s.summary = 'A Cocoa / Objective-C library for interfacing with the iOS AddressBook. Also adds geocoding support.' + s.author = 'Richard Heard' + s.source = { :git => 'https://github.com/heardrwt/RHAddressBook.git', :tag => s.version.to_s} + s.source_files = 'RHAddressBook/*.{h,m}' + s.prefix_header_file = 'RHAddressBook/RHAddressBook-Prefix.pch' + s.frameworks = 'AddressBook', 'CoreLocation', 'AddressBookUI' + s.platform = :ios + s.license = { + :type => 'Modified BSD', + :text => <<-LICENSE + Copyright (c) 2011-2012 Richard Heard. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + LICENSE + } +end \ No newline at end of file From 98e512063d343c1153c723f1f35b8963ae15c267 Mon Sep 17 00:00:00 2001 From: Juan Felipe Alvarez Saldarriaga Date: Wed, 22 Apr 2015 20:06:00 -0500 Subject: [PATCH 2/5] implements unified people, see #33. unify duplicates records if the contact exists in multiple sources, see http://stackoverflow.com/a/11480352/255463 and #33. --- RHAddressBook/RHAddressBook.h | 3 ++ RHAddressBook/RHAddressBook.m | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/RHAddressBook/RHAddressBook.h b/RHAddressBook/RHAddressBook.h index c5e9d59..98ffa46 100644 --- a/RHAddressBook/RHAddressBook.h +++ b/RHAddressBook/RHAddressBook.h @@ -105,6 +105,9 @@ typedef NS_ENUM(NSUInteger, RHAuthorizationStatus) { @property (nonatomic, readonly, copy) NSArray *peopleOrderedByFirstName; @property (nonatomic, readonly, copy) NSArray *peopleOrderedByLastName; +- (NSArray *)peopleUnifiedUsingDefaultSource; +- (NSArray *)peopleUnifiedUsingSource:(RHSource *)source; + -(NSArray*)peopleWithName:(NSString*)name; -(NSArray*)peopleWithEmail:(NSString*)email; -(RHPerson*)personForABRecordRef:(ABRecordRef)personRef; //returns nil if ref not found in the current ab, eg unsaved record from another ab. if the passed recordRef does not belong to the current addressbook, the returned person objects underlying personRef will differ from the passed in value. This is required in-order to maintain thread safety for the underlying AddressBook instance. diff --git a/RHAddressBook/RHAddressBook.m b/RHAddressBook/RHAddressBook.m index 95aaa03..3bd7949 100644 --- a/RHAddressBook/RHAddressBook.m +++ b/RHAddressBook/RHAddressBook.m @@ -645,6 +645,45 @@ -(NSArray*)peopleOrderedByLastName{ return [self peopleOrderedBySortOrdering:kABPersonSortByLastName]; } +/** + * + * + * @return NSArray + */ +- (NSArray *)peopleUnifiedUsingDefaultSource +{ + __block NSArray *result = nil; + + rh_dispatch_sync_for_addressbook(self, ^{ + result = arc_retain([self peopleUnifiedUsingSource:[self defaultSource]]); + }); + + return arc_autorelease(result); +} + +/** + * + * + * @param source + * + * @return NSArray + */ +- (NSArray *)peopleUnifiedUsingSource:(RHSource *)source +{ + __block NSArray *result = nil; + + rh_dispatch_sync_for_addressbook(self, ^{ + CFArrayRef peopleRefs = ABAddressBookCopyArrayOfAllPeopleInSource(_addressBookRef, source.recordRef); + + if (peopleRefs) { + result = arc_retain([self peopleUnifiedForABRecordRefs:peopleRefs]); + CFRelease(peopleRefs); + } + }); + + return arc_autorelease(result); +} + -(NSArray*)peopleWithName:(NSString*)name{ __block NSArray *result = nil; rh_dispatch_sync_for_addressbook(self, ^{ @@ -778,6 +817,39 @@ -(NSArray*)peopleForABRecordRefs:(CFArrayRef)peopleRefs{ return [NSArray arrayWithArray:people]; } +/** + * + * + * @param peopleRefs + * @see http://stackoverflow.com/a/11480352/255463 + * + * @return NSArray + */ +- (NSArray *)peopleUnifiedForABRecordRefs:(CFArrayRef)peopleRefs +{ + if (!peopleRefs) return nil; + + NSMutableSet *people = [NSMutableSet set]; + + rh_dispatch_sync_for_addressbook(self, ^{ + for (CFIndex i = 0; i < CFArrayGetCount(peopleRefs); i++) { + NSMutableSet *contactSet = [NSMutableSet set]; + ABRecordRef personRef = CFArrayGetValueAtIndex(peopleRefs, i); + [contactSet addObject:(__bridge id) personRef]; + + NSArray *linkedRecordsArray = (__bridge NSArray *) ABPersonCopyArrayOfAllLinkedPeople(personRef); + [contactSet addObjectsFromArray:linkedRecordsArray]; + + RHPerson *person = [self personForABRecordRef:personRef]; + if (person) [people addObject:person]; + + CFRelease(personRef); + } + }); + + return people.allObjects; +} + -(RHPerson*)personForABRecordID:(ABRecordID)personID{ __block ABRecordRef recordRef = NULL; From 172e347f076b857accb59bd28c3085e577ebf2c3 Mon Sep 17 00:00:00 2001 From: Juan Felipe Alvarez Saldarriaga Date: Wed, 22 Apr 2015 21:11:33 -0500 Subject: [PATCH 3/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e70744..7c41d15 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ Presenting / editing an RHPerson instance in a ABPersonViewController. Background geocoding ```objectivec - if ([RHAddressBook isGeocodingSupported){ + if ([RHAddressBook isGeocodingSupported]){ [RHAddressBook setPreemptiveGeocodingEnabled:YES]; //class method } float progress = [_addressBook preemptiveGeocodingProgress]; // 0.0f - 1.0f From 86fcc7b851d7d35a9f90573ca7ef3a9f2dbc8ba9 Mon Sep 17 00:00:00 2001 From: Juan Felipe Alvarez Saldarriaga Date: Thu, 23 Jul 2015 16:56:01 -0500 Subject: [PATCH 4/5] get one from each set of linked records. --- RHAddressBook/RHAddressBook.h | 1 + RHAddressBook/RHAddressBook.m | 39 ++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/RHAddressBook/RHAddressBook.h b/RHAddressBook/RHAddressBook.h index 98ffa46..a640de2 100644 --- a/RHAddressBook/RHAddressBook.h +++ b/RHAddressBook/RHAddressBook.h @@ -107,6 +107,7 @@ typedef NS_ENUM(NSUInteger, RHAuthorizationStatus) { - (NSArray *)peopleUnifiedUsingDefaultSource; - (NSArray *)peopleUnifiedUsingSource:(RHSource *)source; +- (NSArray *)peopleUnified; -(NSArray*)peopleWithName:(NSString*)name; -(NSArray*)peopleWithEmail:(NSString*)email; diff --git a/RHAddressBook/RHAddressBook.m b/RHAddressBook/RHAddressBook.m index 3bd7949..d3eefab 100644 --- a/RHAddressBook/RHAddressBook.m +++ b/RHAddressBook/RHAddressBook.m @@ -684,6 +684,22 @@ - (NSArray *)peopleUnifiedUsingSource:(RHSource *)source return arc_autorelease(result); } +- (NSArray *)peopleUnified +{ + __block NSArray *result = nil; + + rh_dispatch_sync_for_addressbook(self, ^{ + CFArrayRef peopleRefs = ABAddressBookCopyArrayOfAllPeople(_addressBookRef); + + if (peopleRefs) { + result = arc_retain([self peopleUnifiedForABRecordRefs:peopleRefs]); + CFRelease(peopleRefs); + } + }); + + return arc_autorelease(result); +} + -(NSArray*)peopleWithName:(NSString*)name{ __block NSArray *result = nil; rh_dispatch_sync_for_addressbook(self, ^{ @@ -829,25 +845,32 @@ - (NSArray *)peopleUnifiedForABRecordRefs:(CFArrayRef)peopleRefs { if (!peopleRefs) return nil; - NSMutableSet *people = [NSMutableSet set]; + NSMutableSet *unifiedRecordsSet = [NSMutableSet set]; + NSMutableArray *unifiedPersons = [NSMutableArray array]; rh_dispatch_sync_for_addressbook(self, ^{ for (CFIndex i = 0; i < CFArrayGetCount(peopleRefs); i++) { NSMutableSet *contactSet = [NSMutableSet set]; - ABRecordRef personRef = CFArrayGetValueAtIndex(peopleRefs, i); - [contactSet addObject:(__bridge id) personRef]; + ABRecordRef record = CFArrayGetValueAtIndex(peopleRefs, i); + [contactSet addObject:(__bridge id) record]; - NSArray *linkedRecordsArray = (__bridge NSArray *) ABPersonCopyArrayOfAllLinkedPeople(personRef); + NSArray *linkedRecordsArray = (__bridge NSArray *) ABPersonCopyArrayOfAllLinkedPeople(record); [contactSet addObjectsFromArray:linkedRecordsArray]; - RHPerson *person = [self personForABRecordRef:personRef]; - if (person) [people addObject:person]; + NSSet *unifiedRecord = [NSSet setWithSet:contactSet]; + [unifiedRecordsSet addObject:unifiedRecord]; - CFRelease(personRef); + CFRelease(record); + } + + // to flat records, choose one. + for (NSSet *unifiedRecord in unifiedRecordsSet) { + RHPerson *person = [self personForABRecordRef:(__bridge ABRecordRef) [unifiedRecord anyObject]]; + if (person) [unifiedPersons addObject:person]; } }); - return people.allObjects; + return unifiedPersons; } -(RHPerson*)personForABRecordID:(ABRecordID)personID{ From eeb29a19ac878853a94c77eb1eb1b1f08be74306 Mon Sep 17 00:00:00 2001 From: Juan Felipe Alvarez Saldarriaga Date: Thu, 23 Jul 2015 20:50:40 -0500 Subject: [PATCH 5/5] fix unified records logic. --- RHAddressBook/RHAddressBook.m | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/RHAddressBook/RHAddressBook.m b/RHAddressBook/RHAddressBook.m index d3eefab..f0336c7 100644 --- a/RHAddressBook/RHAddressBook.m +++ b/RHAddressBook/RHAddressBook.m @@ -838,6 +838,7 @@ -(NSArray*)peopleForABRecordRefs:(CFArrayRef)peopleRefs{ * * @param peopleRefs * @see http://stackoverflow.com/a/11480352/255463 + * @see https://github.com/RigilCorp/ABManager/blob/4abd69d28aef5fa11c6458b694cdc187f469364d/ABManagerExample/ABManager.m#L62 * * @return NSArray */ @@ -846,31 +847,29 @@ - (NSArray *)peopleUnifiedForABRecordRefs:(CFArrayRef)peopleRefs if (!peopleRefs) return nil; NSMutableSet *unifiedRecordsSet = [NSMutableSet set]; - NSMutableArray *unifiedPersons = [NSMutableArray array]; rh_dispatch_sync_for_addressbook(self, ^{ + NSMutableSet *linkedPersonsToSkip = [NSMutableSet set]; + for (CFIndex i = 0; i < CFArrayGetCount(peopleRefs); i++) { - NSMutableSet *contactSet = [NSMutableSet set]; ABRecordRef record = CFArrayGetValueAtIndex(peopleRefs, i); - [contactSet addObject:(__bridge id) record]; + + if ([linkedPersonsToSkip containsObject:(__bridge id) record]) { + continue; + } NSArray *linkedRecordsArray = (__bridge NSArray *) ABPersonCopyArrayOfAllLinkedPeople(record); - [contactSet addObjectsFromArray:linkedRecordsArray]; - NSSet *unifiedRecord = [NSSet setWithSet:contactSet]; - [unifiedRecordsSet addObject:unifiedRecord]; + if (linkedRecordsArray.count > 1) { + [linkedPersonsToSkip addObjectsFromArray:linkedRecordsArray]; + } - CFRelease(record); - } - - // to flat records, choose one. - for (NSSet *unifiedRecord in unifiedRecordsSet) { - RHPerson *person = [self personForABRecordRef:(__bridge ABRecordRef) [unifiedRecord anyObject]]; - if (person) [unifiedPersons addObject:person]; + RHPerson *person = [self personForABRecordRef:record]; + if (person) [unifiedRecordsSet addObject:person]; } }); - return unifiedPersons; + return unifiedRecordsSet.allObjects; } -(RHPerson*)personForABRecordID:(ABRecordID)personID{