Forráskód Böngészése

1.封装iOS联系人管理。

Pen Li 7 éve
szülő
commit
556c88f257

+ 15 - 0
common/Contact/CNContact+RAContact.h

@@ -0,0 +1,15 @@
+//
+//  CNContact+RAContact.h
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/25.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import <Contacts/Contacts.h>
+
+@interface CNContact (RAContact)
+
+- (BOOL)ra_containsKeyWord:(NSString *)keyword;
+
+@end

+ 99 - 0
common/Contact/CNContact+RAContact.m

@@ -0,0 +1,99 @@
+//
+//  CNContact+RAContact.m
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/25.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import "CNContact+RAContact.h"
+
+@implementation CNContact (RAContact)
+
+- (BOOL)ra_containsKeyWord:(NSString *)keyword {
+    
+    if (keyword && keyword.length > 0) {
+
+        // givenName middleName familyName
+        NSString *givenName = self.givenName;
+        NSString *middleName = self.middleName;
+        NSString *familyName = self.familyName;
+
+        if ([givenName containsString:keyword] || [middleName containsString:keyword] || [familyName containsString:keyword]) {
+            return YES;
+        }
+        
+        // organizationName departmentName jobTitle
+        NSString *organizationName = self.organizationName;
+        NSString *departmentName = self.departmentName;
+        NSString *jobTitle = self.jobTitle;
+
+        if ([organizationName containsString:keyword] || [departmentName containsString:keyword] || [jobTitle containsString:keyword]) {
+            return YES;
+        }
+        
+        // phoneNumbers
+        NSArray<CNLabeledValue<CNPhoneNumber*>*> *phoneNumbers = self.phoneNumbers;
+        for (CNLabeledValue<CNPhoneNumber*> *labeledValue in phoneNumbers) {
+            
+            CNPhoneNumber *phoneNumber = labeledValue.value;
+//            NSString *label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
+
+            if ([phoneNumber.stringValue containsString:keyword]) {
+                return YES;
+            }
+        }
+
+        // emailAddresses
+        NSArray<CNLabeledValue<NSString*>*> *emailAddresses = self.emailAddresses;
+        for (CNLabeledValue<NSString*> *labeledValue in emailAddresses) {
+            
+            NSString *email = labeledValue.value;
+//            NSString *label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
+
+            if ([email containsString:keyword]) {
+                return YES;
+            }
+        }
+
+        // postalAddresses
+        NSArray<CNLabeledValue<CNPostalAddress*>*> *postalAddresses = self.postalAddresses;
+        for (CNLabeledValue<CNPostalAddress*> *labeledValue in postalAddresses) {
+            
+//            NSString *label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
+            CNPostalAddress *postalAddress = labeledValue.value;
+            NSString *postalCode = postalAddress.postalCode;
+            NSString *country = postalAddress.country;
+            NSString *state = postalAddress.state;
+            NSString *city = postalAddress.city;
+            NSString *street = postalAddress.street;
+            
+            if ([postalCode containsString:keyword] || [country containsString:keyword] || [state containsString:keyword] || [city containsString:keyword] || [street containsString:keyword]) {
+                return YES;
+            }
+        }
+
+
+        // urlAddresses
+        NSArray<CNLabeledValue<NSString*>*> *urlAddresses = self.urlAddresses;
+        for (CNLabeledValue<NSString*> *labeledValue in urlAddresses) {
+            
+            NSString *url = labeledValue.value;
+//            NSString *label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
+
+            if ([url containsString:keyword]) {
+                return YES;
+            }
+        }
+        
+        // note
+        if ([self.note containsString: keyword]) {
+            return YES;
+        }
+        
+    }
+    
+    return NO;
+}
+
+@end

+ 44 - 0
common/Contact/CNMutableContact+RAContact.h

@@ -0,0 +1,44 @@
+//
+//  CNMutableContact+RAContact.h
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/25.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import <Contacts/Contacts.h>
+
+@interface CNMutableContact (RAContact)
+
+#pragma mark - Phone
+
+- (void)ra_addPhoneNumber:(NSString *)phoneNumber withLabel:(NSString *)label;
+
+- (void)ra_deletePhoneNumber:(CNLabeledValue<CNPhoneNumber *> *)phoneNumber;
+
+#pragma mark - Email
+
+- (void)ra_addEmail:(NSString *)email withLabel:(NSString *)label;
+
+- (void)ra_deleteEmail:(CNLabeledValue<NSString *> *)email;
+
+#pragma mark - Postal Address
+
+- (void)ra_addPostalAdress:(CNPostalAddress *)postalAddress withLabel:(NSString *)label;
+
+- (void)ra_deletePostalAddress:(CNLabeledValue<CNPostalAddress *> *)address;
+
+#pragma mark - Social Profile
+
+- (void)ra_addSocialProfile:(CNSocialProfile *)socialProfile withLabel:(NSString *)label;
+
+- (void)ra_deleteSocialProfiles:(CNLabeledValue<CNSocialProfile *> *)socialProfile;
+
+#pragma mark - URL
+
+- (void)ra_addURL:(NSString *)url withLabel:(NSString *)label;
+
+- (void)ra_deleteURL:(CNLabeledValue<NSString *> *)url;
+
+@end
+

+ 199 - 0
common/Contact/CNMutableContact+RAContact.m

@@ -0,0 +1,199 @@
+//
+//  CNMutableContact+RAContact.m
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/25.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import "CNMutableContact+RAContact.h"
+
+@implementation CNMutableContact (RAContact)
+
+#pragma mark - Phone
+
+- (void)ra_addPhoneNumber:(NSString *)phoneNumber withLabel:(NSString *)label {
+    
+    if (phoneNumber) {
+        
+        if (!label) {
+            label = CNLabelHome;
+        }
+        
+        CNPhoneNumber *phone = [CNPhoneNumber phoneNumberWithStringValue:phoneNumber];
+        CNLabeledValue *labeled_phone = [[CNLabeledValue alloc] initWithLabel:label value:phone];
+        
+        NSMutableArray<CNLabeledValue<CNPhoneNumber *> *> *phoneNumbers = [self.phoneNumbers mutableCopy];
+        if (phoneNumbers == nil) {
+            phoneNumbers = [NSMutableArray array];
+        }
+        [phoneNumbers addObject:labeled_phone];
+        
+        self.phoneNumbers = [phoneNumbers copy];
+    }
+}
+
+- (void)ra_deletePhoneNumber:(CNLabeledValue<CNPhoneNumber *> *)phoneNumber {
+    
+    if (phoneNumber) {
+        
+        NSMutableArray<CNLabeledValue<CNPhoneNumber *> *> *phoneNumbers = [self.phoneNumbers mutableCopy];
+        if (phoneNumbers && phoneNumbers.count > 0 && [phoneNumbers containsObject:phoneNumber]) {
+            
+            [phoneNumbers removeObject:phoneNumber];
+            
+            self.phoneNumbers = [phoneNumbers copy];
+        }
+        
+    }
+}
+
+#pragma mark - Email
+
+- (void)ra_addEmail:(NSString *)email withLabel:(NSString *)label {
+    
+    if (email) {
+        
+        if (!label) {
+            label = CNLabelHome;
+        }
+        
+        CNLabeledValue<NSString *> *labeledEmail = [[CNLabeledValue alloc] initWithLabel:label value:email];
+        
+        NSMutableArray<CNLabeledValue<NSString *> *> *emails = [self.emailAddresses mutableCopy];
+        if (emails == nil) {
+            emails = [NSMutableArray array];
+        }
+        [emails addObject:labeledEmail];
+        
+        self.emailAddresses = [emails copy];
+    }
+}
+
+- (void)ra_deleteEmail:(CNLabeledValue<NSString *> *)email {
+    
+    if (email) {
+        
+        NSMutableArray<CNLabeledValue<NSString *> *> *emails = [self.emailAddresses mutableCopy];
+        if (emails && emails.count > 0 && [emails containsObject:email]) {
+            
+            [emails removeObject:email];
+            
+            self.emailAddresses = [emails copy];
+        }
+        
+    }
+}
+
+#pragma mark - Postal Address
+
+- (void)ra_addPostalAdress:(CNPostalAddress *)postalAddress withLabel:(NSString *)label {
+    
+    if (postalAddress) {
+        
+        if (!label) {
+            label = CNLabelHome;
+        }
+        
+        CNLabeledValue<CNPostalAddress *> *labeledAddr = [[CNLabeledValue alloc] initWithLabel:label value:postalAddress];
+        
+        NSMutableArray<CNLabeledValue<CNPostalAddress *> *> *addresses = [self.postalAddresses mutableCopy];
+        if (addresses == nil) {
+            addresses = [NSMutableArray array];
+        }
+        [addresses addObject:labeledAddr];
+        
+        self.postalAddresses = [addresses copy];
+    }
+}
+
+- (void)ra_deletePostalAddress:(CNLabeledValue<CNPostalAddress *> *)address {
+    
+    if (address) {
+        
+        NSMutableArray<CNLabeledValue<CNPostalAddress *> *> *addresses = [self.emailAddresses mutableCopy];
+        if (addresses && addresses.count > 0 && [addresses containsObject:address]) {
+            
+            [addresses removeObject:address];
+            
+            self.postalAddresses = [addresses copy];
+        }
+        
+    }
+}
+
+#pragma mark - Social Profile
+
+- (void)ra_addSocialProfile:(CNSocialProfile *)socialProfile withLabel:(NSString *)label {
+    
+    if (socialProfile) {
+        
+        if (!label) {
+            label = CNLabelHome;
+        }
+        
+        CNLabeledValue<CNSocialProfile *> *labeledSocialProfile = [[CNLabeledValue alloc] initWithLabel:label value:socialProfile];
+        
+        NSMutableArray<CNLabeledValue<CNSocialProfile *> *> *socialProfiles = [self.socialProfiles mutableCopy];
+        if (socialProfiles == nil) {
+            socialProfiles = [NSMutableArray array];
+        }
+        [socialProfiles addObject:labeledSocialProfile];
+        
+        self.socialProfiles = [socialProfiles copy];
+    }
+}
+
+- (void)ra_deleteSocialProfiles:(CNLabeledValue<CNSocialProfile *> *)socialProfile {
+    
+    if (socialProfile) {
+        
+        NSMutableArray<CNLabeledValue<CNSocialProfile *> *> *socialProfiles = [self.socialProfiles mutableCopy];
+        if (socialProfiles && socialProfiles.count > 0 && [socialProfiles containsObject:socialProfile]) {
+            
+            [socialProfiles removeObject:socialProfile];
+            
+            self.socialProfiles = [socialProfiles copy];
+        }
+        
+    }
+}
+
+#pragma mark - URL
+
+- (void)ra_addURL:(NSString *)url withLabel:(NSString *)label {
+    
+    if (url) {
+        
+        if (!label) {
+            label = CNLabelHome;
+        }
+        
+        CNLabeledValue<NSString *> *labeledURL = [[CNLabeledValue alloc] initWithLabel:label value:url];
+        
+        NSMutableArray<CNLabeledValue<NSString *> *> *urlAddresses = [self.urlAddresses mutableCopy];
+        if (urlAddresses == nil) {
+            urlAddresses = [NSMutableArray array];
+        }
+        [urlAddresses addObject:labeledURL];
+        
+        self.urlAddresses = [urlAddresses copy];
+    }
+}
+
+- (void)ra_deleteURL:(CNLabeledValue<NSString *> *)url {
+    
+    if (url) {
+        
+        NSMutableArray<CNLabeledValue<NSString *> *> *urlAddresses = [self.urlAddresses mutableCopy];
+        if (urlAddresses && urlAddresses.count > 0 && [urlAddresses containsObject:url]) {
+            
+            [urlAddresses removeObject:url];
+            
+            self.urlAddresses = [urlAddresses copy];
+        }
+        
+    }
+}
+
+@end

+ 45 - 0
common/Contact/RAContactManager.h

@@ -0,0 +1,45 @@
+//
+//  RAContactManager.h
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/24.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CNContact,CNMutableContact;
+NS_CLASS_AVAILABLE(10_11, 9_0)
+@interface RAContactManager : NSObject
+
++ (instancetype)defaultManager;
+
+- (void)requestAuthorization:(void(^)(BOOL granted))completion;
+
+#pragma mark - Search
+
+- (void)fetchAllContactCompletionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion;
+
+- (void)searchContactByName:(NSString *)name completionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion;
+
+- (void)searchContactByKeyword:(NSString *)keyword completionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion;
+
+#pragma mark - Insert
+
+- (void)insertContact:(CNMutableContact *)contact  completionHandler:(void(^)(BOOL result,NSError *error))completion;
+
+#pragma mark - Remove
+
+- (void)removeContact:(CNContact *)contact completionHandler:(void(^)(BOOL result,NSError *error))completion;
+
+#pragma mark - Update
+
+- (void)updateContact:(CNMutableContact *)contact completionHandler:(void(^)(BOOL result,NSError *error))completion;
+
+#pragma mark - Utils
+
+- (void)sortGroupedContactsByFamilyNameFirstLetter:(NSArray<CNContact *> *)contacts
+                                        completion:(void(^)(NSArray<NSString *> *sortedKeys, NSDictionary<NSString *, NSArray<CNContact *> *> *contactGroup))completion;///<使用姓氏首页字母分组
+
+@end
+

+ 293 - 0
common/Contact/RAContactManager.m

@@ -0,0 +1,293 @@
+//
+//  RAContactManager.m
+//  ContactDemo
+//
+//  Created by Jack on 2018/12/24.
+//  Copyright © 2018 USAI. All rights reserved.
+//
+
+#import "RAContactManager.h"
+#import <Contacts/Contacts.h>
+#import "CNContact+RAContact.h"
+
+@implementation RAContactManager
+
++ (instancetype)defaultManager {
+    static RAContactManager *manager;
+    static dispatch_once_t token;
+    dispatch_once(&token, ^{
+        manager = [[RAContactManager alloc] init];
+    });
+    return manager;
+}
+
+#pragma mark - Authorization
+
+- (void)requestAuthorization:(void(^)(BOOL granted))completion {
+    
+    if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized) {
+        
+        if (completion) {
+            completion(YES);
+        }
+        
+    } else {
+        
+        CNContactStore *store = [[CNContactStore alloc] init];
+        [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
+            
+            if (completion) {
+                completion(granted);
+            }
+            
+        }];
+    }
+}
+
+#pragma mark - Get
+
+- (void)searchContactWithPredicate:(NSPredicate *)predicate completionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion {
+    
+    // 创建联系人仓库
+    CNContactStore *store = [[CNContactStore alloc] init];
+    
+    // 创建联系人的请求对象
+    // keys决定能获取联系人哪些信息,例:姓名,电话,头像等
+    NSArray *fetchKeys = @[
+                           CNContactIdentifierKey,
+                           CNContactGivenNameKey,
+                           CNContactFamilyNameKey,
+                           CNContactMiddleNameKey,
+                           CNContactImageDataKey,
+                           CNContactOrganizationNameKey,
+                           CNContactDepartmentNameKey,
+                           CNContactJobTitleKey,
+                           CNContactPhoneNumbersKey,
+                           CNContactEmailAddressesKey,
+                           CNContactPostalAddressesKey,
+                           CNContactSocialProfilesKey,
+                           CNContactUrlAddressesKey,
+                           CNContactNoteKey
+                           ];
+    CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:fetchKeys];
+    request.sortOrder = CNContactSortOrderFamilyName;
+    request.predicate = predicate;
+    
+    // 请求联系人
+    NSError *error = nil;
+    NSMutableArray<CNContact *> *contacts = [NSMutableArray array];
+    [store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
+        
+        [contacts addObject:contact];
+    }];
+    
+    if (error) {
+        contacts = nil;
+    }
+    if (completion) {
+        completion([contacts copy], error);
+    }
+}
+
+- (void)fetchAllContactCompletionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion {
+    [self searchContactWithPredicate:nil completionHandler:completion];
+}
+
+- (void)searchContactByKeyword:(NSString *)keyword completionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion {
+    if (keyword) {
+        
+        [self searchContactWithPredicate:nil completionHandler:^(NSArray<CNContact *> *contacts, NSError *error) {
+            
+            if (error) {
+                
+                if (completion) {
+                    completion(nil, error);
+                }
+                
+            } else {
+                
+                NSMutableArray<CNContact *> *keywordContacts = [NSMutableArray array];
+                for (CNContact *c in contacts) {
+                    if ([c ra_containsKeyWord:keyword]) {
+                        [keywordContacts addObject:c];
+                    }
+                }
+                
+                if (completion) {
+                    completion(keywordContacts, nil);
+                }
+            }
+            
+        }];
+        
+    } else {
+        
+        [self searchContactWithPredicate:nil completionHandler:completion];
+    }
+}
+
+- (void)searchContactByName:(NSString *)name completionHandler:(void(^)(NSArray<CNContact *> *contacts, NSError *error))completion {
+    [self searchContactWithPredicate:[CNContact predicateForContactsMatchingName:name] completionHandler:completion];
+}
+
+#pragma mark - Add
+
+- (void)insertContact:(CNMutableContact *)contact  completionHandler:(void(^)(BOOL result,NSError *error))completion {
+    
+    if (contact) {
+        CNContactStore *store = [[CNContactStore alloc] init];
+        CNSaveRequest *request = [[CNSaveRequest alloc] init];
+        
+        [request addContact:contact toContainerWithIdentifier:nil];
+        
+        NSError *err;
+        [store executeSaveRequest:request error:&err];
+        if (completion) {
+            completion(err == nil, err);
+        }
+    } else {
+        
+        if (completion) {
+            completion(NO, [NSError errorWithDomain:NSCocoaErrorDomain code:404 userInfo:@{@"msg":@"contact is nil"}]);
+        }
+    }
+}
+
+#pragma mark - Remove
+
+- (void)removeContact:(CNContact *)contact completionHandler:(void(^)(BOOL result,NSError *error))completion {
+    
+    if (contact) {
+        
+        if (contact.identifier != nil) {
+            
+            CNContactStore *store = [[CNContactStore alloc] init];
+            CNSaveRequest *request = [[CNSaveRequest alloc] init];
+            
+            [request deleteContact:contact.mutableCopy];
+            NSError *err;
+            [store executeSaveRequest:request error:&err];
+            if (completion) {
+                completion(err == nil, err);
+            }
+            
+        } else {
+            
+            if (completion) {
+                completion(NO, [NSError errorWithDomain:NSCocoaErrorDomain code:404 userInfo:@{@"msg":@"contact's identifier is nil"}]);
+            }
+        }
+        
+    } else {
+        
+        if (completion) {
+            completion(NO, [NSError errorWithDomain:NSCocoaErrorDomain code:404 userInfo:@{@"msg":@"contact is nil"}]);
+        }
+    }
+}
+
+#pragma mark - Modify
+
+- (void)updateContact:(CNMutableContact *)contact completionHandler:(void(^)(BOOL result,NSError *error))completion {
+    
+    if (contact) {
+        
+        if (contact.identifier != nil) {
+            
+            CNContactStore *store = [[CNContactStore alloc] init];
+            CNSaveRequest *request = [[CNSaveRequest alloc] init];
+            
+            [request updateContact:contact];
+            NSError *err;
+            [store executeSaveRequest:request error:&err];
+            if (completion) {
+                completion(err == nil, err);
+            }
+            
+        } else {
+            
+            if (completion) {
+                completion(NO, [NSError errorWithDomain:NSCocoaErrorDomain code:404 userInfo:@{@"msg":@"contact's identifier is nil"}]);
+            }
+        }
+        
+    } else {
+        
+        if (completion) {
+            completion(NO, [NSError errorWithDomain:NSCocoaErrorDomain code:404 userInfo:@{@"msg":@"contact is nil"}]);
+        }
+    }
+    
+}
+
+#pragma mark - Utils
+
+- (NSString *)getFirstLetterFromString:(NSString *)aString {
+    
+    if (aString) {
+        NSMutableString *str = [NSMutableString stringWithString:aString];
+        //带声调的拼音
+        CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformMandarinLatin,NO);
+        NSLog(@"%@",str);
+        //不带声调的拼音
+        CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformStripDiacritics,NO);
+        //转化为大写拼音
+        NSString *strPinYin = [str capitalizedString];
+        
+        NSString *firstString = [strPinYin substringToIndex:1];
+        //判断姓名首位是否为大写字母
+        NSString * regexA = @"^[A-Z]$";
+        NSPredicate *predA = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexA];
+        //获取并返回首字母
+        return [predA evaluateWithObject:firstString] ? firstString : @"#";
+    } else{
+        return @"#";
+    }
+}
+
+- (void)addContact:(CNContact *)contact toGroupDictionary:(NSMutableDictionary<NSString *, NSMutableArray<CNContact *> *> *)dic {
+    
+    if (contact && dic) {
+    
+        NSString *firstLetter = [self getFirstLetterFromString:contact.familyName];
+        
+        if (!dic[firstLetter]) {
+            dic[firstLetter] = [NSMutableArray array];
+        }
+        [dic[firstLetter] addObject:contact];
+    }
+}
+
+- (void)sortGroupedContactsByFamilyNameFirstLetter:(NSArray<CNContact *> *)contacts completion:(void(^)(NSArray<NSString *> *sortedKeys, NSDictionary<NSString *, NSArray<CNContact *> *> *contactGroup))completion {
+    
+    if (contacts && contacts.count > 0) {
+        
+        NSMutableDictionary<NSString *, NSMutableArray<CNContact *> *> *dic = [NSMutableDictionary dictionary];
+        for (CNContact *c in contacts) {
+            [self addContact:c toGroupDictionary:dic];
+        }
+        
+        // 对Group排序
+        [dic enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableArray<CNContact *> * _Nonnull obj, BOOL * _Nonnull stop) {
+           
+            [obj sortUsingComparator:^NSComparisonResult(CNContact *  _Nonnull obj1, CNContact *  _Nonnull obj2) {
+                return [obj1.familyName localizedCompare:obj2.familyName];
+            }];
+        }];
+        
+        NSArray *sortedKeys = [dic.allKeys sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
+            return [obj1 localizedCompare:obj2];
+        }];
+        
+        if (completion) {
+            completion(sortedKeys, dic);
+        }
+        
+    } else {
+        if (completion) {
+            completion(nil, nil);
+        }
+    }
+}
+
+@end