如何创建 vCard/vcf 文件以在共享 sheet 中使用?

How to create vCard/vcf file to use in share sheet?

我是 swift 的新手,我发现的关于我的问题的方法已被弃用。我正在构建一个目录应用程序,我正在从 API 中提取联系人数据,而不是从 phone 的地址簿中提取联系人数据。

在 iOS 中,如果您转到地址簿,您可以 select 一个联系人并选择 'Share Contact',这会显示共享 sheet。我想在我的应用程序中使用这个确切的功能。

认为我已经弄清楚了分享表,这是我的代码:

    @IBAction func actShare(sender: AnyObject) {

    let activityViewController = UIActivityViewController(activityItems: ["text" as NSString], applicationActivities: nil)
    presentViewController(activityViewController, animated: true, completion: {})
}

我想把"text" as NSString改成vCard,因为那是iOS从通讯录分享的对象,对吧?假设我是对的,我想从我自己的应用程序的联系人对象创建一个 vCard,以便将其共享到适当的应用程序(电子邮件、短信等)。

我怎样才能在 Swift 中做到这一点?如果我错了,请纠正我并告诉我我需要做什么。谢谢。

编辑:好的,这是我的更改。

@IBAction func actShare(sender: AnyObject) {
    do {
        var contactData = NSData()
        try contactData = CNContactVCardSerialization.dataWithContacts([createContact()])

        let activityViewController = UIActivityViewController(activityItems: [contactData as NSData], applicationActivities: nil)
        presentViewController(activityViewController, animated: true, completion: {})
    } catch {
        print("CNContactVCardSerialization cannot save address")
    }

func createContact() -> CNMutableContact {
    let contactCard = CNMutableContact()
    contactCard.givenName = "John"
    contactCard.familyName = "Doe"
    contactCard.emailAddresses = [
        CNLabeledValue(label: CNLabelWork, value: "john.doe@email.com")
    ]

    return contactCard
}

但是,当我单击共享按钮并显示我的共享 sheet 时,我 select 我想共享到的应用程序,它没有 add/attach 联系人预期的数据。我该如何实现?

您可以使用 CNContact(需要 iOS 9):

let contact = CNMutableContact()
    contact.givenName = "John"
    contact.familyName = "Doe"
    contact.emailAddresses = [
      CNLabeledValue(label: CNLabelWork, value: "john.doe@email.com")
    ]

这里的技巧是使用 CNContactVCardSerialization.dataWithContacts 将联系人保存到 VCard (.vcf) 文件,然后将文件 URL 传递给 UIActivityViewController。 activity 视图控制器从文件扩展名检测 VCard 格式,并显示支持该格式的应用程序(例如消息、邮件、便签、Airdrop 等)

示例:

@IBAction func buttonTapped(button: UIButton) {

    let contact = createContact()

    do {
        try shareContacts([contact])
    }
    catch {
        // Handle error
    }
}

func shareContacts(contacts: [CNContact]) throws {

    guard let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first else {
        return
    }

    var filename = NSUUID().UUIDString

    // Create a human friendly file name if sharing a single contact.
    if let contact = contacts.first where contacts.count == 1 {

        if let fullname = CNContactFormatter().stringFromContact(contact) {
            filename = fullname.componentsSeparatedByString(" ").joinWithSeparator("")
        }
    }

    let fileURL = directoryURL
        .URLByAppendingPathComponent(filename)
        .URLByAppendingPathExtension("vcf")

    let data = try CNContactVCardSerialization.dataWithContacts(contacts)

    print("filename: \(filename)")
    print("contact: \(String(data: data, encoding: NSUTF8StringEncoding))")

    try data.writeToURL(fileURL, options: [.AtomicWrite])

    let activityViewController = UIActivityViewController(
        activityItems: [fileURL],
        applicationActivities: nil
    )

    presentViewController(activityViewController, animated: true, completion: {})
}

func createContact() -> CNContact {

    // Creating a mutable object to add to the contact
    let contact = CNMutableContact()

    contact.imageData = NSData() // The profile picture as a NSData object

    contact.givenName = "John"
    contact.familyName = "Appleseed"

    let homeEmail = CNLabeledValue(label:CNLabelHome, value:"john@example.com")
    let workEmail = CNLabeledValue(label:CNLabelWork, value:"j.appleseed@icloud.com")
    contact.emailAddresses = [homeEmail, workEmail]

    contact.phoneNumbers = [CNLabeledValue(
        label:CNLabelPhoneNumberiPhone,
        value:CNPhoneNumber(stringValue:"(408) 555-0126"))]

    return contact
}

It is very simple to create contact from your iOS app and share over the external apps.

首先,您需要创建 分享按钮操作,如下所示:-

-(void)shareContactAction:(UIButton*)sender
{  
    CNMutableContact *selectedCon = [[CNMutableContact alloc] init];
    selectedCon.givenName = @"ABC";
    selectedCon.familyName = @"XYZ";
    NSString *phoneNum = @"+91-XXXXXXXXXXX"
    CNLabeledValue *contactNum1 = [CNLabeledValue labeledValueWithLabel:CNLabelHome value:[CNPhoneNumber phoneNumberWithStringValue:phoneNum]];
    selectedCon.phoneNumbers = @[contactNum1];
    NSString *url=[self saveContactDetailsToDocDir:selectedCon];
    UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[@"Contact", [NSURL fileURLWithPath:url]] applicationActivities:nil];
    [self presentViewController:activityViewController animated:YES completion:nil];     
}

Than you can call the function below which will return the path of the contact card in above button event.

- (NSString *)saveContactDetailsToDocDir:(CNContact *)contact {

    NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory,     NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString * contactCardPath = [documentsDirectory stringByAppendingString:@"/vCardName.vcf"];
    NSArray *array = [[NSArray alloc] initWithObjects:contact, nil];
    NSError *error;
    NSData *data = [CNContactVCardSerialization dataWithContacts:array error:&error];
    [data writeToFile:contactCardPath atomically:YES];

    return contactCardPath;
}

已更新 Swift 4:

@IBAction func buttonTapped(button: UIButton) {

    let contact = createContact()

    do {
        try shareContacts(contacts: [contact])
    }
    catch {
    // Handle error
    }
}

func shareContacts(contacts: [CNContact]) throws {

    guard let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
    return
    }

    var filename = NSUUID().uuidString

    // Create a human friendly file name if sharing a single contact.
    if let contact = contacts.first, contacts.count == 1 {

    if let fullname = CNContactFormatter().string(from: contact) {
        filename = fullname.components(separatedBy: " ").joined(separator: "")
    }
    }

    let fileURL = directoryURL
    .appendingPathComponent(filename)
    .appendingPathExtension("vcf")

    let data = try CNContactVCardSerialization.data(with: contacts)

    print("filename: \(filename)")
    print("contact: \(String(describing: String(data: data, encoding: String.Encoding.utf8)))")

    try data.write(to: fileURL, options: [.atomicWrite])

    let activityViewController = UIActivityViewController(
    activityItems: [fileURL],
    applicationActivities: nil
    )

    present(activityController, animated: true, completion: nil)
}

func createContact() -> CNContact {

    // Creating a mutable object to add to the contact
    let contact = CNMutableContact()

    contact.imageData = NSData() as Data // The profile picture as a NSData object

    contact.givenName = "John"
    contact.familyName = "Appleseed"

    let homeEmail = CNLabeledValue(label:CNLabelHome, value:"john@example.com")
    let workEmail = CNLabeledValue(label:CNLabelWork, value:"j.appleseed@icloud.com")
    contact.emailAddresses = [homeEmail, workEmail]

    contact.phoneNumbers = [CNLabeledValue(
    label:CNLabelPhoneNumberiPhone,
    value:CNPhoneNumber(stringValue:"(408) 555-0126"))]

    return contact
}

成功的做法:

首先获取所有联系人然后保存,然后制作 .vcf 文件并在电子邮件、WhatsApp、消息、Skype 中共享 .vcf 并保存在 iPhone 个文件中....

@IBAction func vcfPressed(_ sender: UIButton) {

    let arrayContacts = self.fetchAllContacts()
    self.saveContactsInDocument(contacts: arrayContacts)
     let contact = fetchAllContacts()
     do {
     try shareContacts(contacts: contact)
     }
     catch {
     }
}


func shareContacts(contacts: [CNContact]) throws {

    guard let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
 return}

    var filename = NSUUID().uuidString
    if let contact = contacts.first, contacts.count == 1 {
    if let fullname = CNContactFormatter().string(from: contact) {
    filename = fullname.components(separatedBy: " ").joined(separator: "")}}
    let fileURL = directoryURL.appendingPathComponent(filename).appendingPathExtension("vcf")
    let data = try CNContactVCardSerialization.data(with: contacts)
    print("filename: \(filename)")
    print("contact: \(String(data: data, encoding: String.Encoding.utf8))")
    try data.write(to: fileURL, options: [.atomicWrite])
    let activityViewController = UIActivityViewController(activityItems: [fileURL],applicationActivities: nil)
    present(activityViewController, animated: true, completion: {})
 }


func fetchAllContacts() -> [CNContact] {

    var contacts : [CNContact] = []
    let contactStore = CNContactStore()
    let fetchReq = CNContactFetchRequest.init(keysToFetch: [CNContactVCardSerialization.descriptorForRequiredKeys()])
    do {
        try contactStore.enumerateContacts(with: fetchReq) { (contact, end) in
            contacts.append(contact)
        }}
    catch  {print("Failed to fetch")}
    return contacts
}