3

i am trying to develop an app that takes input of some fields from the user about a contact and then export that contact as VCard. I can successfully export the contact but the when i try to import the contact using Contacts at iCloud.com, the picture is not being imported. All the other information is being imported but not the picture.

There are two ways i tried to export the VCard.

First Way Here

public  func getVCard() -> Data?{

    var contact = CNMutableContact()

    let homeEmail = CNLabeledValue(label:CNLabelHome, value:self.email as! NSString)
    contact.emailAddresses = [homeEmail]

    let phone = CNLabeledValue(label: CNLabelPhoneNumberMobile, value:CNPhoneNumber(stringValue: self.phone!))
    contact.phoneNumbers = [phone]

    contact.givenName = self.name!
    if let d = self.data{
        contact.imageData = d

    }


    let address = CNMutablePostalAddress()
    address.country = self.address!
    contact.postalAddresses = [CNLabeledValue(label: CNLabelHome, value: address)]

    contact.organizationName = self.company!

    contact.jobTitle = title!

    let sc1 = CNSocialProfile(urlString: "\(socialMedia1!)", username: self.name!, userIdentifier: self.name!, service: CNContactSocialProfilesKey)
    let sc2 = CNSocialProfile(urlString: "\(socialMedia2!)", username: self.name!, userIdentifier: self.name!, service: CNContactSocialProfilesKey)
    //contact.socialProfiles = [CNSocialProfile(]
    let sc3 = CNSocialProfile(urlString: "\(socialMedia3!)", username: self.name!, userIdentifier: self.name!, service: CNContactSocialProfilesKey)
    contact.socialProfiles = [CNLabeledValue(label: "Profile1", value: sc1), CNLabeledValue(label: "Profile2", value: sc2), CNLabeledValue(label: "Profile3", value: sc3)]

    contact.note = self.notes!
    let urlAddresses = CNLabeledValue(label: CNLabelURLAddressHomePage, value: self.web as! NSString)
    contact.urlAddresses = [urlAddresses]

    var data: Data?

    do{
        data = try CNContactVCardSerialization.data(with: [contact])
    }catch let error{
        print(error.localizedDescription)
    }

    return data
}

When i debug this function, i can see the picture data in the contact object but when that VCard is exported and i try to import it on iCloud.com it does not import picture of that contact.

Now the second method i used to export the contact is as follow and i would prefer the answer using this method because this is the best fit for me.

func textBasedVCard()-> Data?{

    var string = "BEGIN:VCARD\nVERSION:3.0\n"
    string += "N:\(self.name!);\nFN:\(self.name!)\nORG:\(self.company!)\nTITLE:\(self.title!)\nTEL;TYPE=WORK,VOICE:\(self.phone!)\nADR;TYPE=WORK:;;\(self.address!)\nNOTE:\(self.notes!)\nitem1.URL:\(self.web!)\nitem2.URL:\(self.blog!)\nitem3.URL:\(self.socialMedia1!)\nitem4.URL:\(self.socialMedia2!)\nitem5.URL:\(self.socialMedia3!)\nEMAIL;TYPE=PREF,INTERNET:\(self.email!)\nEND:VCARD"
    print(string)
    let utf8str = string.data(using: String.Encoding.utf8)
    //utf8str?.base64EncodedStringWithOptions(NSData.Base64EncodingOptions(rawValue: 0))
    if let base64Encoded = utf8str?.base64EncodedString(options: .init(rawValue: 0))
    {
        return Data(base64Encoded: base64Encoded)!

    }
    return nil
}

In this method i do not know how to attach picture data. Please guide me through this problem. I will be really grateful. Thanks.

Edit:

This question in no way a duplicate of any other question. You can see the link yourself someone posted just to make himself feel proud.

Salman Ali
  • 255
  • 8
  • 25
  • Possible duplicate of [Correct vCard format for iOS devices?](https://stackoverflow.com/questions/13913157/correct-vcard-format-for-ios-devices) – nathan Jul 26 '17 at 21:26
  • ? Dupe reporting is part of SO. You said you were having trouble exporting/importing the PHOTO tag. That response has a working solution for that, please read it and report back. – nathan Jul 26 '17 at 21:29
  • @nathan that question is totally different from mine. Either you are not so much qualified to understand it or you are here just to feel proud by making such comments. Is there even a single work in that question that says how to add picture in vcard? Or anything that is asked in this question? – Salman Ali Jul 26 '17 at 21:32
  • `In this method i do not know how to attach picture data. Please guide me through this problem.` ? – nathan Jul 26 '17 at 21:33
  • What is this? Is this a duplicate line? – Salman Ali Jul 26 '17 at 21:34

1 Answers1

3

From the response posted above (ABPersonCreatePeopleInSourceWithVCardRepresentation):

Address Book supports vCard version 3.0.

VCARD > PHOTO - An image or photograph of the individual associated with the vCard. It may point to an external URL or may be embedded in the vCard as a Base64 encoded block of text.

2.1: PHOTO;JPEG:http://example.com/photo.jpg
2.1: PHOTO;JPEG;ENCODING=BASE64:[base64-data]
3.0: PHOTO;TYPE=JPEG;VALUE=URI:http://example.com/photo.jpg
=> 3.0: PHOTO;TYPE=JPEG;ENCODING=b:[base64-data]             // Works in iOS/macOS
4.0: PHOTO;MEDIATYPE=image/jpeg:http://example.com/photo.jpg
4.0: PHOTO:data:image/jpeg;base64,[base64-data]

How to create a VCF 3.0 card for iOS/macOS

Testing shows the 2nd way works: encode the JPEG photo using base64.

PHOTO;ENCODING=b;TYPE=JPEG:\(base64EncodedImage)

Working PoC:

struct VCFBuilder {
    private let name: String? = "Name1"
    private let company: String? = "Company"
    private let title: String? = "Title1"
    private let phone: String? = "555-1234-1234"
    private let address: String? = "aaa"
    private let notes: String? = "PROnotes"
    private let web: String? = "web"
    private let blog: String? = "bolgg"
    private let socialMedia1: String? = "social1"
    private let socialMedia2: String? = "social2"
    private let socialMedia3: String? = "social3"
    private let email: String? = "email@mail.com"
    private let imageUrl: String? = "http://0.gravatar.com/avatar/3f009d72559f51e7e454b16e5d0687a1"

    func vcf() -> String? {

        do {
            let url = URL(string: self.imageUrl!)
            let data = try Data(contentsOf: url!).base64EncodedString()
            return generateVCF(withEncodedImage: data)
        } catch {
            print(error)
            return nil
        }
    }

    // TODO: Don't !
    private func generateVCF(withEncodedImage imageBase64: String) -> String {
        return """
        BEGIN:VCARD
        VERSION:3.0
        N:\(self.name!);
        FN:\(self.name!)
        ORG:\(self.company!)
        TITLE:\(self.title!)
        TEL;TYPE=WORK,VOICE:\(self.phone!)
        ADR;TYPE=WORK:;;\(self.address!)
        NOTE:\(self.notes!)
        PHOTO;ENCODING=b;TYPE=JPEG:\(imageBase64)
        item1.URL:\(self.web!)
        item2.URL:\(self.blog!)
        item3.URL:\(self.socialMedia1!)
        item4.URL:\(self.socialMedia2!)
        item5.URL:\(self.socialMedia3!)
        EMAIL;TYPE=PREF,INTERNET:\(self.email!)
        END:VCARD
        """
    }
}

let vcf = VCFBuilder().vcf()
print(vcf)

How to import VCF 3.0 card in iOS/macOS:

  • Manually using Contacts.app
  • iOS <= 9: ABPersonCreatePeopleInSourceWithVCardRepresentation(_:_:) (docs)
  • iOS >= 9: CNContactVCardSerialization.contacts(with:) (docs)
guard let data = VCFBuilder().vcf()?.data(using: .utf8),
      let contact = try? CNContactVCardSerialization.contacts(with: data).first
        else { return }

print(contact.imageData!.base64EncodedString())

Imported using iOS' Contacts.app:

enter image description here

Community
  • 1
  • 1
nathan
  • 9,329
  • 4
  • 37
  • 51
  • So i have to covert image data to base64 string and append it to the vCard? Will it then also import picture to contacts when imported? – Salman Ali Jul 26 '17 at 21:59
  • Yes, you need to append the base64 encoded image as shown in the poc code – nathan Jul 26 '17 at 22:10
  • Whenever i try to do that, i get error while importing. Invalid data in VCard. – Salman Ali Jul 27 '17 at 13:16
  • @SalmanAli works as excepted. Image MUST be a JPEG. See attached image – nathan Jul 27 '17 at 17:34
  • I can import this vcard into my Contacts on iCloud.com but not in the android. What could be the problem? – Salman Ali Jul 28 '17 at 03:46
  • Android might support different versions of the vcard spec. You should probably create a new question if you can't figure it out – nathan Jul 28 '17 at 03:48
  • @SalmanAli using the POC code, which seems to include a JPEG, generates this error. Did you or @nathan also have this issue? `Error: initQRCode cannot find proper rs block info (input data too big?)2018-11-19 01:21:34.151967 Tofu[20737:5262252] subsystem: com.apple.BackBoardServices.fence, category: Workspace, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0` – Crashalot Nov 19 '18 at 09:23
  • how to add a new line in notes. – Pathak Ayush Sep 20 '19 at 13:12
  • Hello @nathan please update the method it's not adding the image to vcard anymore. – Pathak Ayush Nov 05 '19 at 13:35
  • If you're search for how to attach an image to a vcf file using swift as I was for days I found the answer here - https://stackoverflow.com/questions/39638149/how-to-get-vcf-data-with-contact-images-using-cncontactvcardserialization-datawi/39873729#39873729 – Kurt L. Feb 24 '21 at 22:02