Ios Swift Sharing on WhatsApp and FB - ios

I am experiencing a few issue with the Share option in iOS8.
With the below code I am able to correctly share by Email and Text(SMS) including my text strings and my image.
But when I try to share to Facebook, the generated post only includes my image (skipping the text strings) while if I try to share on WhatsApp, the image is skipped and only the text strings are passed.
Does anybody have any suggestion?
My code:
#IBAction func shareButtonClicked(sender: UIBarButtonItem) {
var kidToShare1 : String? = noKids
if selectedKid == nil {
kidToShare1 = noKids
println("kid is nil")
} else {
kidToShare1 = selectedKid!.kidName
}
let kidToShare = NSLocalizedString("Kid: ", comment:"Kid to share") + kidToShare1!
let imageToShare = photo! ?? noPhoto!
let textToShare = NSLocalizedString("Event Description: ", comment:"Text to share") + descriptionText as NSString
let dateToShare = NSLocalizedString("Event Date: ", comment: "Date to share") + formatDate(date) as NSString
let message = NSLocalizedString("My App", comment: "Main sharing message")
let objectsToShare = [message, dateToShare, kidToShare, textToShare, imageToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAirDrop, UIActivityTypeAssignToContact, UIActivityTypeAddToReadingList,UIActivityTypeCopyToPasteboard,UIActivityTypeSaveToCameraRoll,UIActivityTypePrint]
self.presentViewController(activityVC, animated: true, completion: nil)
}
}

Related

Is there a way to create a url vCard with a description below it and share it using swift 4?... iOS Maps do it sharing a marked location

I'm trying to share a location through vCard like this:
var vCardString = [
"BEGIN:VCARD",
"VERSION:4.0",
"N:;Shared Location;;;",
"FN:Shared Location",
"item1.URL:http://maps.apple.com/?ll=\(coordinate.latitude),\(coordinate.longitude)",
"item1.X-ABLabel:map url",
"END:VCARD"
].joined(separator: "\n")
let myData = vCardString.data(using: .utf8)! as NSData
var items = [Any]()
let vCardActivity = NSItemProvider(item: myData!, typeIdentifier: kUTTypeVCard as String)
items.append(vCardActivity)
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(activityViewController, animated:true, completion: nil)
This actually works but I cannot add a description below the vCard...
I have this
I'm looking for this
I've tried adding another item:
var items = [Any]()
let vCardActivity = NSItemProvider(item: myData!, typeIdentifier: kUTTypeVCard as String)
items.append(vCardActivity)
let myTitle = "This is my title"
items.append(myTitle)
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(activityViewController, animated:true, completion: nil)
But it only shows the vCard
Thanks a lot
What if you add an address field to the VCard?
Like this:
let vCardString = [
"BEGIN:VCARD",
"VERSION:4.0",
"N:;Shared Location;;;",
"FN:Shared Location",
"ADR;TYPE=HOME;LABEL=42 Plantation St. Baytown, LA 30314 United States of America:;;42 Plantation St.;Baytown;LA;30314;United States of America",
"item1.URL:http://maps.apple.com/?ll=25.686613,-100.316116",
"item1.X-ABLabel:map url",
"END:VCARD"
].joined(separator: "\n")
You will get something like this:

How can I share location from my app to WhatsApp in Swift 3

I'm trying to share a location from my iOS app to WhatsApp and I want it to look like this:
What I'm doing is sending vCard with this code :
func vCardURL(from coordinate: CLLocationCoordinate2D, with name: String?) -> URL {
let vCardFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Shared Location.loc.vcf")
let vCardString = [
"BEGIN:VCARD",
"VERSION:3.0",
//"PRODID:-//Apple Inc.//iPhone OS 10.3.2//EN",
"N:;My Location;;;",
"FN:My Location",
"item1.URL;type=pref:https://maps.apple.com/?ll=50.359890\\,12.934560&q=My%20Location&t=m",
"item1.X-ABLabel:map url",
"END:VCARD"
].joined(separator: "\n")
do {
try vCardString.write(toFile: vCardFileURL.path, atomically: true, encoding: .utf8)
} catch let error {
print("Error, \(error.localizedDescription), saving vCard: \(vCardString) to file path: \(vCardFileURL.path).")
}
print(vCardString)
return vCardFileURL
} // end of function
// calling the methood above
let vURL = LocationVCard.vCardURL(from: self.newLocation.coordinate, with: "Berlin")
let activityViewController = UIActivityViewController(activityItems: [vURL], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
But I always end up with this style instead of what I want:
No need to save the vCard string to a file, just convert it to Data and wrap it with NSItemProvider with the correct type identifier public.vcard, and it'll work just fine:
let data = vCardString.data(using: .utf8)! as NSData
let item = NSItemProvider(item: data, typeIdentifier: "public.vcard")
let activityViewController = UIActivityViewController(activityItems: [item], applicationActivities: nil)

How to share longitude and latitude using UIActivityViewController

I'm trying to share a (longitude, latitude) associated with your's location in a UIActivityViewController so that the user can share the location in an SMS with someone else and it shows up as a clickable little map as shown below.
I know how to share an address as a text. Here is my code for sharing the address:
#IBAction func didTapShareLocation(_ sender: UIButton) {
guard let carAddress = self.adressLabel.text else {
return
}
let textToShare = "My car is at this address: \(carAddress)"
let objectsToShare = [textToShare] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = sender
myParentVC?.present(activityVC, animated: true, completion: nil)
}
Here is a complete answer in Swift 3.1 which I put together after getting pieces of information from several places. I hope it helps someone.
#IBAction func didTapShareLocation(_ sender: UIButton) {
guard let carAddress = self.adressLabel.text, let lat = self.carCoordinates?.latitude, let lon = self.carCoordinates?.longitude else {
return
}
guard CLLocationCoordinate2DIsValid(self.carCoordinates!) else {
print("Location not valid!")
return
}
let carAddressString = "My car is at this address: \n\(carAddress)\n"
let vcardString = [
"BEGIN:VCARD",
"VERSION:3.0",
"N:;Shared Location;;;",
"FN:Shared Location",
"item1.URL;type=pref:http://maps.apple.com/?ll=\(lat),\(lon)",
"item1.X-ABLabel:map url",
"END:VCARD"
].joined(separator: "\n")
let directory = FileManager().urls(for: .cachesDirectory, in: .userDomainMask)
let path = directory.first!.path + "_vcard_for_location_sharing.loc.vcf"
do {
try vcardString.write(toFile: path, atomically: true, encoding: .ascii)
let url = NSURL(fileURLWithPath: path)
let objectsToShare = [url, carAddressString] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = sender
self.present(activityVC, animated: true, completion: nil)
}
catch {
print("problem saving vcard: \(error.localizedDescription)")
}
}

Sharing csv file through UIActivityViewController

I'm trying to share a csv file using UIActivityViewController.
I want to share both through email and other applications, like Telegram.
Through Telegram the file gets shared correctly, but using email, the email has no attachments.
Also, the csv file has no extension, should I set a MIME type? How?
#IBAction func shareSheet(sender: AnyObject) {
let firstActivityItem = "Hi, here is the csv file"
//do i really need this? what for?
let secondActivityItem : NSURL = NSURL(string: "http//:urlyouwant")!
let csv : NSData! = NSData(contentsOfFile: NSTemporaryDirectory() + "export.csv")
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, secondActivityItem, csv], applicationActivities: nil)
//set the email title
activityViewController.setValue(firstAcxtivityItem, forKey: "subject")
self.presentViewController(activityViewController, animated: true, completion: nil)
}
This is the code I use to send a CSV via the mail composer
let csvString = "Your CSV String"
let subject = "Subject of your email"
let fileName = "CSV Filename.csv"
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setSubject(subject)
if let csvData = csvString.data(using: String.Encoding.utf8) {
composeVC.addAttachmentData(csvData, mimeType: "text/csv", fileName: fileName)
}
self.present(composeVC, animated: true, completion: nil)

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

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: "john.doe#email.com")
]
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?
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
}
You may use a CNContact (requires iOS 9):
let contact = CNMutableContact()
contact.givenName = "John"
contact.familyName = "Doe"
contact.emailAddresses = [
CNLabeledValue(label: CNLabelWork, value: "john.doe#email.com")
]
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;
}
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:"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
}
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
}

Resources