How to get VCF data with contact images using CNContactVCardSerialization dataWithContacts: method?
As a workaround you can create PHOTO field inside of VCard.
NSError* error = nil;
NSData* vCardData = [CNContactVCardSerialization dataWithContacts:@[contact] error:&error];
NSString* vcString = [[NSString alloc] initWithData:vCardData encoding:NSUTF8StringEncoding];
NSString* base64Image = contact.imageData.base64Encoding;
NSString* vcardImageString = [[@"PHOTO;TYPE=JPEG;ENCODING=BASE64:" stringByAppendingString:base64Image] stringByAppendingString:@"\n"];
vcString = [vcString stringByReplacingOccurrencesOfString:@"END:VCARD" withString:[vcardImageString stringByAppendingString:@"END:VCARD"]];
vCardData = [vcString dataUsingEncoding:NSUTF8StringEncoding];
For some reasons CNContactVCardSerialization does not use any photo of contact. VCard after serialization is looks like:
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//iPhone OS 9.3.2//EN
N:Contact;Test;;;
FN: Test Contact
END:VCARD
After insertion the PHOTO field inside VCard you will get
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//iPhone OS 9.3.2//EN
N:Contact;Test;;;
FN: Test Contact
PHOTO;TYPE=JPEG;ENCODING=BASE64:<photo base64 string>
END:VCARD
After this insertion contact will looks fine in CNContactViewController
Contact image doesn't send when contact saved by CNContact
I figured out the problem. When I use CNContactVCardSerialization.dataWithContacts()
, I get the image as NSData. If I convert it as UIImageJPEGRepresentation
/UIImageJPEGRepresentation
and then append the image with the vCard string as @"PHOTO;BASE64;ENCODING=b;TYPE=JPEG%@"
and then append the image data, then picture is sent with vCard.
CNContactVCardSerialization.dataWithContacts giving exception
I found out my mistake. On the keys to fetch contact, I was missing CNContactVCardSerialization.descriptorForRequiredKeys()
. After adding it, the code is working flawlessly.
How to create vCard/vcf file to use in share sheet?
The trick here is to save the contact to a VCard (.vcf) file using CNContactVCardSerialization.dataWithContacts
, then pass the file URL to the UIActivityViewController
. The activity view controller detects the VCard format from the file extension, and shows the apps where the format is supported (e.g. Messages, Mail, Notes, Airdrop, etc)
Example:
@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
}
ios: I want to create .vcf/vcard which contains all my contacts. How do I do this?
I found a way to do this finally.
func createContact() -> [CNContact] {
let contactStore = CNContactStore()
var contacts = [CNContact]()
let fetchRequest = CNContactFetchRequest(keysToFetch:[CNContactVCardSerialization.descriptorForRequiredKeys()])
do {
try contactStore.enumerateContactsWithFetchRequest(fetchRequest) {
(contact123, stop) in
// Array containing all unified contacts from everywhere
contacts.append(contact123)}
}
catch {
print("unable to fetch contacts")
}
return contacts
}
The above code will create a list of all your contacts which consists of all the details. You can also select only the keys you want to fetch from all the contacts(This may act as a filter for your search).
Then simply create a .vcard file using the CNContactVCardSerialization
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: NSData?
do {
data = try CNContactVCardSerialization.dataWithContacts(contacts)
print("filename: \(filename)")
print("contact: \(String(data: data!, encoding: NSUTF8StringEncoding))")
do {
try data!.writeToURL(fileURL, options: [.AtomicWrite])
}
catch {
print("eeror\(error)")
}
}
catch {
print("error\(error)")
}
let activityViewController = UIActivityViewController(
activityItems: [fileURL],
applicationActivities: nil
)
presentViewController(activityViewController, animated: true, completion: {})
}
The above code will give you an option to share it via Mail, whatsapp etc etc.
Hope this helps someone.
Export Picture in VCard Swift
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:
Exporting all contacts in one .vcf file using Contacts.Framework in Objective - C
You can use this method to get all the contacts in .vcf file. It return the same output that you get using AddressBook.framework.
- (void)getContacts {
NSMutableArray *contactsArray=[[NSMutableArray alloc] init];
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
});
return;
}
NSMutableArray *contacts = [NSMutableArray array];
NSError *fetchError;
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[[CNContactVCardSerialization descriptorForRequiredKeys], [CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]]];
BOOL success = [store enumerateContactsWithFetchRequest:request error:&fetchError usingBlock:^(CNContact *contact, BOOL *stop) {
[contacts addObject:contact];
}];
if (!success) {
NSLog(@"error = %@", fetchError);
}
// you can now do something with the list of contacts, for example, to show the names
CNContactFormatter *formatter = [[CNContactFormatter alloc] init];
for (CNContact *contact in contacts) {
[contactsArray addObject:contact];
// NSString *string = [formatter stringFromContact:contact];
//NSLog(@"contact = %@", string);
}
//NSError *error;
NSData *vcardString =[CNContactVCardSerialization dataWithContacts:contactsArray error:&error];
NSString* vcardStr = [[NSString alloc] initWithData:vcardString encoding:NSUTF8StringEncoding];
NSLog(@"vcardStr = %@",vcardStr);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folderPath = [paths objectAtIndex:0];
NSString *filePath = [folderPath stringByAppendingPathComponent:@"Contacts.vcf"];
[vcardStr writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}];
}
Related Topics
Libz.Dylib Versus Libz.1.2.3.Dylib Versus Libz.1.2.5.Dylib
How to Draw on an Image in Swift
Phonegap on iOS with Absolute Path Urls for Assets
What Is the Swift Preprocessor Equivalent to iOS Version Check Comparison
Firebase Dynamic Link Always Goes to App Store Url Even If the App Is Installed
Admob Sdk - iOS -File Not Found
Font Size on Universal Storyboard
iOS Swift 3:Convert "Yyyy-Mm-Dd'T'Hh:Mm:Ssz" Format String to Date Object
Calling Instance Method During Initialization in Swift
iOS Client Certificates and Mobile Device Management
Navigationcontroller.Navigationitem VS Navigationitem
Document Directory Path Change When Rebuild Application
Ios7 - Device Unique Identifier
How to Read Incoming Sms by Using Application in iOS
Horizontally Scroll All Rows of Uicollectionview Together
Extension for Uicolor with Custom Colors It Is Real
Following in App Purchase, App Crashing on Startup. Productidentifier=Nil