Reputation: 185
I'm new to swift and methods I am finding are deprecated regarding my issue. I'm building a directory app and I'm pulling contact data from an API, not from the phone's address book.
In iOS, if you go to your address book, you can select a contact and choose 'Share Contact' which brings up a share sheet. I want this exact functionality in my app.
I think I've got Share Sheets figured out, and here's my code for that:
@IBAction func actShare(sender: AnyObject) {
let activityViewController = UIActivityViewController(activityItems: ["text" as NSString], applicationActivities: nil)
presentViewController(activityViewController, animated: true, completion: {})
}
I want to to change "text" as NSString
to be a vCard, as that is the object that iOS shares from the address book, right? Assuming I'm right, I want to create a vCard from my own app's contact object in order to share it to appropriate apps (email, sms, etc).
How can I achieve that in Swift? If I'm wrong, please correct me and show me what I need to do. Thanks.
EDIT: Okay, here's my changes.
@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")
}
and
func createContact() -> CNMutableContact {
let contactCard = CNMutableContact()
contactCard.givenName = "John"
contactCard.familyName = "Doe"
contactCard.emailAddresses = [
CNLabeledValue(label: CNLabelWork, value: "[email protected]")
]
return contactCard
}
However, when I click the share button and it brings up my share sheet, I select the application I want to share to and it doesn't add/attach the contact data as intended. How do I accomplish this?
Upvotes: 10
Views: 9516
Reputation: 518
Successful approach:
First get all Contacts then save it and after this make .vcf file and also share the .vcf in Emails, WhatsApp, Messaging, Skype and save in iPhone files....
@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
}
Upvotes: 0
Reputation: 77
Updated for 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:"[email protected]")
let workEmail = CNLabeledValue(label:CNLabelWork, value:"[email protected]")
contact.emailAddresses = [homeEmail, workEmail]
contact.phoneNumbers = [CNLabeledValue(
label:CNLabelPhoneNumberiPhone,
value:CNPhoneNumber(stringValue:"(408) 555-0126"))]
return contact
}
Upvotes: 1
Reputation: 878
It is very simple to create contact from your iOS app and share over the external apps.
First you need to create Share Button Action like below :-
-(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;
}
Upvotes: 2
Reputation: 5265
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:"[email protected]")
let workEmail = CNLabeledValue(label:CNLabelWork, value:"[email protected]")
contact.emailAddresses = [homeEmail, workEmail]
contact.phoneNumbers = [CNLabeledValue(
label:CNLabelPhoneNumberiPhone,
value:CNPhoneNumber(stringValue:"(408) 555-0126"))]
return contact
}
Upvotes: 14
Reputation: 1996
You may use a CNContact (requires iOS 9):
let contact = CNMutableContact()
contact.givenName = "John"
contact.familyName = "Doe"
contact.emailAddresses = [
CNLabeledValue(label: CNLabelWork, value: "[email protected]")
]
Upvotes: 3