【问题标题】:iOS - add contact into Contacts?iOS - 将联系人添加到联系人中?
【发布时间】:2026-01-31 10:20:05
【问题描述】:

喂! 当用户点击一个按钮时,有没有办法将联系人添加或更新到实际的 Apple Contacts Book 中?一些节日的电子邮件回复包括“名片”,收件人可以下载并在他们的通讯录中找到。

【问题讨论】:

    标签: iphone ios email uibutton contacts


    【解决方案1】:

    如果在 iOS 9 或更高版本中执行此操作,则应使用 Contacts 框架:

    @import Contacts;
    

    您还需要更新您的Info.plist,添加NSContactsUsageDescription 以解释您的应用为什么需要访问联系人。

    然后,当您想以编程方式添加联系人时,您可以执行以下操作:

    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted) {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Access to contacts." message:@"This app requires access to contacts because ..." preferredStyle:UIAlertControllerStyleActionSheet];
        [alert addAction:[UIAlertAction actionWithTitle:@"Go to Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
        }]];
        [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
        [self presentViewController:alert animated:TRUE completion:nil];
        return;
    }
    
    CNContactStore *store = [[CNContactStore alloc] init];
    
    [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (!granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                // user didn't grant access;
                // so, again, tell user here why app needs permissions in order  to do it's job;
                // this is dispatched to the main queue because this request could be running on background thread
            });
            return;
        }
    
        // create contact
    
        CNMutableContact *contact = [[CNMutableContact alloc] init];
        contact.familyName = @"Doe";
        contact.givenName = @"John";
    
        CNLabeledValue *homePhone = [CNLabeledValue labeledValueWithLabel:CNLabelHome value:[CNPhoneNumber phoneNumberWithStringValue:@"312-555-1212"]];
        contact.phoneNumbers = @[homePhone];
    
        CNSaveRequest *request = [[CNSaveRequest alloc] init];
        [request addContact:contact toContainerWithIdentifier:nil];
    
        // save it
    
        NSError *saveError;
        if (![store executeSaveRequest:request error:&saveError]) {
            NSLog(@"error = %@", saveError);
        }
    }];
    

    或者,更好的是,如果您想使用 ContactUI 框架添加联系人(为用户提供联系人的视觉确认,并让他们根据需要进行定制),您可以导入这两个框架:

    @import Contacts;
    @import ContactsUI;
    

    然后:

    CNContactStore *store = [[CNContactStore alloc] init];
    
    // create contact
    
    CNMutableContact *contact = [[CNMutableContact alloc] init];
    contact.familyName = @"Smith";
    contact.givenName = @"Jane";
    
    CNLabeledValue *homePhone = [CNLabeledValue labeledValueWithLabel:CNLabelHome value:[CNPhoneNumber phoneNumberWithStringValue:@"301-555-1212"]];
    contact.phoneNumbers = @[homePhone];
    
    CNContactViewController *controller = [CNContactViewController viewControllerForUnknownContact:contact];
    controller.contactStore = store;
    controller.delegate = self;
    
    [self.navigationController pushViewController:controller animated:TRUE];
    

    下面是我的原始答案,在 9 之前的 iOS 版本中使用 AddressBookAddressBookUI 框架。但如果只支持 iOS 9 及更高版本,请使用上述ContactsContactsUI 框架。

    --

    如果您想将联系人添加到用户的通讯录,请使用AddressBook.Framework 创建联系人,然后使用AddressBookUI.Framework 呈现用户界面以允许用户将其添加到他们的个人地址使用ABUnknownPersonViewController 预订。因此,您可以:

    1. AddressBook.FrameworkAddressBookUI.Framework 添加到Link Binary With Libraries 下的列表中;

    2. 导入 .h 文件:

      #import <AddressBook/AddressBook.h>
      #import <AddressBookUI/AddressBookUI.h>
      
    3. 编写创建联系人的代码,例如:

      // create person record
      
      ABRecordRef person = ABPersonCreate();
      
      // set name and other string values
      
      ABRecordSetValue(person, kABPersonOrganizationProperty, (__bridge CFStringRef) venueName, NULL);
      
      if (venueUrl) {
          ABMutableMultiValueRef urlMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
          ABMultiValueAddValueAndLabel(urlMultiValue, (__bridge CFStringRef) venueUrl, kABPersonHomePageLabel, NULL);
          ABRecordSetValue(person, kABPersonURLProperty, urlMultiValue, nil);
          CFRelease(urlMultiValue);
      }
      
      if (venueEmail) {
          ABMutableMultiValueRef emailMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
          ABMultiValueAddValueAndLabel(emailMultiValue, (__bridge CFStringRef) venueEmail, kABWorkLabel, NULL);
          ABRecordSetValue(person, kABPersonEmailProperty, emailMultiValue, nil);
          CFRelease(emailMultiValue);
      }
      
      if (venuePhone) {
          ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
          NSArray *venuePhoneNumbers = [venuePhone componentsSeparatedByString:@" or "];
          for (NSString *venuePhoneNumberString in venuePhoneNumbers)
              ABMultiValueAddValueAndLabel(phoneNumberMultiValue, (__bridge CFStringRef) venuePhoneNumberString, kABPersonPhoneMainLabel, NULL);
          ABRecordSetValue(person, kABPersonPhoneProperty, phoneNumberMultiValue, nil);
          CFRelease(phoneNumberMultiValue);
      }
      
      // add address
      
      ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
      NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
      
      if (venueAddress1) {
          if (venueAddress2)
              addressDictionary[(NSString *) kABPersonAddressStreetKey] = [NSString stringWithFormat:@"%@\n%@", venueAddress1, venueAddress2];
          else
              addressDictionary[(NSString *) kABPersonAddressStreetKey] = venueAddress1;
      }
      if (venueCity)
          addressDictionary[(NSString *)kABPersonAddressCityKey] = venueCity;
      if (venueState)
          addressDictionary[(NSString *)kABPersonAddressStateKey] = venueState;
      if (venueZip)
          addressDictionary[(NSString *)kABPersonAddressZIPKey] = venueZip;
      if (venueCountry)
          addressDictionary[(NSString *)kABPersonAddressCountryKey] = venueCountry;
      
      ABMultiValueAddValueAndLabel(multiAddress, (__bridge CFDictionaryRef) addressDictionary, kABWorkLabel, NULL);
      ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, NULL);
      CFRelease(multiAddress);
      
      // let's show view controller
      
      ABUnknownPersonViewController *controller = [[ABUnknownPersonViewController alloc] init];
      
      controller.displayedPerson = person;
      controller.allowsAddingToAddressBook = YES;
      
      // current view must have a navigation controller
      
      [self.navigationController pushViewController:controller animated:YES];
      
      CFRelease(person);
      

    请参阅通讯簿编程指南的ABUnknownPersonViewController Class ReferencePrompting the User to Create a New Person Record from Existing Data 部分。

    【讨论】:

    • 你为什么放“status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusDenied”?
    • 错字。应该拒绝||受限制的。查看修改后的答案。
    • 你能帮我了解为什么在创建如下标记值时我无法使用自定义标识符:let app = CNLabeledValue&lt;NSString&gt;(label: "App", value: appURL); contact.urlAddresses.append(app) 请注意,如果 iPhone 仅使用 iCloud 进行保存,我可以使用它联系人。但如果我使用的是 Google 通讯录,它就不起作用了。
    • 获取日志:_BSMachError: port e317; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND" 2017-03-29 06:05:56.068572 doko[26442:3530002] [Common] _BSMachError: port e317; (os/kern) invalid name (0xf) "Unable to deallocate send right"
    • 我不能在这里评论谷歌的限制,我不使用谷歌联系人,所以我不能轻易验证。听起来这值得作为自己的问题发布在 Stack Overflow 上(假设您已经搜索了该特定错误消息但一无所获)。
    【解决方案2】:

    呈现默认联系人控制器

    第 1 步: 将 ContactUi.framework 添加到项目中并导入

        #import <Contacts/Contacts.h>
        #import <ContactsUI/ContactsUI.h>
    

    第二步: 添加此代码

     -(void)showAddContactController{
            //Pass nil to show default contact adding screen
            CNContactViewController *addContactVC = [CNContactViewController viewControllerForNewContact:nil];
            addContactVC.delegate=self;
            UINavigationController *navController   = [[UINavigationController alloc] initWithRootViewController:addContactVC];
            [viewController presentViewController:navController animated:NO completion:nil];
        }
    

    第三步:

    要在按 DONE 或 CANCEL 时获得回调,请添加 &lt;CNContactViewControllerDelegate&gt; 并实现委托方法。

    - (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(nullable CNContact *)contact{
        //You will get the callback here
    }
    

    【讨论】:

      【解决方案3】:
      @import Contacts;
      -(void)addToContactList
       {
      CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
      
      if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted) {
      
          UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"This app previously was refused permissions to contacts; Please go to settings and grant permission to this app so it can add the desired contact" preferredStyle:UIAlertControllerStyleAlert];
      
          [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
      
          [self presentViewController:alert animated:TRUE completion:nil];
      
          return;
      
      }
      
      CNContactStore *store = [[CNContactStore alloc] init];
      
      [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
      
          if (!granted) {
      
              dispatch_async(dispatch_get_main_queue(), ^{
      
                  // user didn't grant access;
      
                  // so, again, tell user here why app needs permissions in order  to do it's job;
      
                  // this is dispatched to the main queue because this request could be running on background thread
      
              });
      
              return;
      
          }
      
      
      
          // create contact
      
      
      
          CNMutableContact *contact = [[CNMutableContact alloc] init];
      
          contact.givenName = @"Test";
      
          contact.familyName = @"User";
      
          CNLabeledValue *homePhone = [CNLabeledValue labeledValueWithLabel:CNLabelHome value:[CNPhoneNumber phoneNumberWithStringValue:@"91012-555-1212"]];
      
          contact.phoneNumbers = @[homePhone];
      
      
      
          CNSaveRequest *request = [[CNSaveRequest alloc] init];
      
          [request addContact:contact toContainerWithIdentifier:nil];
      
      
      
          // save it
      
      
      
          NSError *saveError;
      
          if (![store executeSaveRequest:request error:&saveError]) {
      
              NSLog(@"error = %@", saveError);
      
          }
      
      }];
      
       }
      

      【讨论】: