Send mail with file attachment - swift

I search solution to send a mail with attachment.
I have this code but the file is not attached...
if let url = URL(string: "mailto:\(email)?subject=report&body=see_attachment&attachment=/Users/myname/Desktop/report.txt") {
NSWorkspace.shared().open(url)
}
I have see it maybe work with MessageUI, but I can't import this framework I don't know why. I get this error message : No such module 'MessageUI'
I checked in General > Linked Frameworks and Libraries, but there are not MessageUI...
Anyone have a solution to add file in mail?
Thanks

It seems that attachment in mailto: URLs are not supported on macOS (not always at least...details seems sketchy dependent on where you look on the internet :))
What you can use instead I found out from this blog post, is an instance of NSSharingService documented here
Here is an example demonstrating how to use it.
And in your case you could do something like:
let email = "your email here"
let path = "/Users/myname/Desktop/report.txt"
let fileURL = URL(fileURLWithPath: path)
let sharingService = NSSharingService(named: NSSharingServiceNameComposeEmail)
sharingService?.recipients = [email] //could be more than one
sharingService?.subject = "subject"
let items: [Any] = ["see attachment", fileURL] //the interesting part, here you add body text as well as URL for the document you'd like to share
sharingService?.perform(withItems: items)
Update
So #Spire mentioned in a comment below that this won't attach a file.
It seems there is a gotcha to be aware of.
For this to work you need to look into your App Capabilities.
You can either:
disable App Sandbox
enable read access for the folders from where you would like to fetch content.
I've attached a couple of screenshots.
Here is how this looks if I have disabled App Sandbox under Capabilities
And here is an image where I have enabled App Sandbox and allowed my app to read content in my Downloads folder
If I do the above, I can access my file called document.txt, located in my Downloads folder, using this URL
let path = "/Users/thatsme/Downloads/document.txt"
let fileURL = URL(fileURLWithPath: path)
And attach that to a mail
Hope that helps you.

import MessageUI
class ViewController: UIViewController,MFMailComposeViewControllerDelegate {
func sendMail() {
if( MFMailComposeViewController.canSendMail()){
print("Can send email.")
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
//Set to recipients
mailComposer.setToRecipients(["yakupad#yandex.com"])
//Set the subject
mailComposer.setSubject("email with document pdf")
//set mail body
mailComposer.setMessageBody("This is what they sound like.", isHTML: true)
let pathPDF = "\(NSTemporaryDirectory())contract.pdf"
if let fileData = NSData(contentsOfFile: pathPDF)
{
print("File data loaded.")
mailComposer.addAttachmentData(fileData as Data, mimeType: "application/pdf", fileName: "contract.pdf")
}
//this will compose and present mail to user
self.present(mailComposer, animated: true, completion: nil)
}
else
{
print("email is not supported")
}
func mailComposeController(_ didFinishWithcontroller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
{
self.dismiss(animated: true, completion: nil)
}
}

First of all you should import import MessageUI. For this add framework to the project.
Example:
After investigate MFMailComposeViewControllerDelegate for knowing when you end sending email.
Example of the creating of the email:
if( MFMailComposeViewController.canSendMail() ) {
println("Can send email.")
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
//Set the subject and message of the email
mailComposer.setSubject("Have you heard a swift?")
mailComposer.setMessageBody("This is what they sound like.", isHTML: false)
if let filePath = NSBundle.mainBundle().pathForResource("swifts", ofType: "wav") {
println("File path loaded.")
if let fileData = NSData(contentsOfFile: filePath) {
println("File data loaded.")
mailComposer.addAttachmentData(fileData, mimeType: "audio/wav", fileName: "swifts")
}
}
self.presentViewController(mailComposer, animated: true, completion: nil)
}
Working example is presented by this link.

Related

Is there anyway for me to send a PDF and PNGs by text using MFMessageComposeViewController() in Swift?

Whenever I try to use .addAttachmentURL, it does not attach anything. The ViewController is presented with nothing within the body of the text. The URL is a path to the pdf data (I don't know if that makes a difference) in my file defaults. Is there any way I can send a PDF through text like this? I have not found anything by looking through documentation or StackOverflow. Also, I haven't implemented it yet, but I was wondering if there was a way to also attach PNGs to this message I am sending along with the PDF.
func getFileManager() -> NSString {
let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString)
return filePath
}
func displayMessageInterface() {
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self
// Configure the fields of the interface.
composeVC.recipients = ["000000000"]
var url = URL(string: self.getFileManager() as String)!
url.appendPathComponent("my_report.pdf")
composeVC.addAttachmentURL(url, withAlternateFilename:
"this file")
// Present the view controller modally.
if MFMessageComposeViewController.canSendText() {
self.present(composeVC, animated: true, completion: nil)
} else {
print("Can't send messages.")
}
}
You are using the wrong URL initializer. URL(string:) initializer expects a scheme, in this case file://. You need to use URL(fileURLWithPath:) initializer or simply get the document directory URL using FileManager urls method:
extension URL {
static let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
let url = URL.documentDirectory.appendingPathComponent("my_report.pdf")
I am not sure what you mean when you say "The URL is a path to the pdf data in my file defaults". If you have included your file in your project Bundle you need to use its url(forResource:) method.
let url = Bundle.main.url(forResource: "my_report", withExtension: "pdf")!

Writing iOS application logs on a text file

I am using this answer to log messages in my app.
import Foundation
class Log: TextOutputStream {
func write(_ string: String) {
let fm = FileManager.default
let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
if let handle = try? FileHandle(forWritingTo: log) {
handle.seekToEndOfFile()
handle.write(string.data(using: .utf8)!)
handle.closeFile()
} else {
do {
try string.data(using: .utf8)?.write(to: log)
} catch {
print(error)
}
}
}
static var log: Log = Log()
private init() {}
}
Used as follows using the Singleton pattern,
print("\(#function) Test Log", to: &Log.log)
This would append the String to the log.txt file. I cannot see the file being created in the Files.app and it doesn't produce an error either. If I print the path of the file where it's being saved it shows,
file:///var/mobile/Containers/Data/Application/00EBA5E5-7132-495E-B90E-E6CF32BA3EA7/Documents/
Where should it be saved? Do I have to do any prior setup? I can't seem to make this work. Do I have to do do something before to create the folder? Nothing shows up in the Files.app.
EDIT: I am not using the Simulator, I need to use a real device.
Okay I got confused and I totally forgot this document is not supposed to show up in the Files.app. It's stored inside the app's container. If you want to share it from the documents sheet and send it to another device via AirDrop or whatever add this action to trigger when you tap a button intended to share the document.
let fm = FileManager.default
let fileUrl = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("log.txt")
var filesToShare = [Any]()
filesToShare.append(fileUrl)
let activityViewController = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)

open .pkpass file from Wallet using share extension iOS

I am trying to open .pkPass file from wallet using share extension, but its not opening. Basically its returning data and I want URL. but If I try to open .pkpassfile data in PKAddPassesViewController its not open it says Attempt to present on whose view is not in the window hierarchy!.
ShareViewController
override func didSelectPost() { if let item = self.extensionContext?.inputItems[0] as? NSExtensionItem{
print("Item \(item)")
for ele in item.attachments!{
print("item.attachments!======>>> \(ele as! NSItemProvider)")
let itemProvider = ele as! NSItemProvider
print(itemProvider)
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
print("imageType : \(imageType)")
if itemProvider.hasItemConformingToTypeIdentifier(imageType){
print("True")
itemProvider.loadItem(forTypeIdentifier: imageType, options: nil, completionHandler: { (item, error) in
print(item)
var imgData: Data!
if let url = item as? Data{
imgData = url
//try! Data(contentsOf: url)
self.openPass(withName: url)
}else {
print("sorry it does not work on others")
}
})
}
}
}self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
open PkPass file Functions
func openPass(withName passData: Data?) {
var error: Error? = nil
var newPass: PKPass? = nil
do {
newPass = try PKPass(data: passData as! Data)
} catch {
}
if error != nil {
UIAlertView(title: "Passes error", message: error!.localizedDescription, delegate: nil, cancelButtonTitle: "Ooops", otherButtonTitles: "").show()
return
}
let addController = PKAddPassesViewController(pass: newPass!)
addController!.delegate = self
present(addController!, animated: true)
}
its not opening it says Attempt to present on whose view is not in the window hierarchy!
basically I want to open .pkpass file from wallet app using share extension in my app and then user can print or share the pass using my application. I want to get url of .pkpass file then save it to userdefults, now the issue is this .pkpass file is returning data instead of url
so can anyone help to fix the issue or give suggestion regarding opening .pkpass file from wallet app using share extension thankx

Unable to share PDF in Social media

When I tried sharing a text, I got all the apps that are available in my iPhone but when I try to share PDF, the only options that are visible are Mail and Whatsapp. Sharing on FB option was not there. I am unable to share the PDF in Whatsapp also even though the size is 86KB. I got the following error
"This item can't be shared. Try sharing some other item"
In the following link, it is possible to share on FB it seems.
How to Share PDF file in all eligable and appear iOS app?.
Can anyone give me some idea?. I tried the following code
func actionMenuViewControllerShareDocument(_ actionMenuViewController: ActionMenuViewController) {
self.dismiss(animated: true, completion: nil)
if let lastPathComponent = pdfDocument?.documentURL?.lastPathComponent,
let documentAttributes = pdfDocument?.documentAttributes,
let attachmentData = pdfDocument?.dataRepresentation() {
let shareText = attachmentData
let activityViewController = UIActivityViewController(activityItems: [shareText], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
}
}
I used UIDocumentInteractionController instead of UIActivityViewController.The following code solved my issue in sending PDF to Whatsapp, Add to Notes, Mail,etc..
let url = Bundle.main.url(forResource: "Sample", withExtension: "pdf")!
docController = UIDocumentInteractionController(url: url)
docController?.delegate = self
docController?.presentOptionsMenu(from: self.view.frame, in:self.view, animated:true)

swift pdf file not attaching to an email

I have the below code which is suppose to attach a file named "file.pdf" which was created and placed into the temp directory as such
pdfFileUrl = "\(NSTemporaryDirectory())file.pdf"
But the email does not have my attachment as I am expecting it to
#IBAction func sendEmail(sender: AnyObject) {
createReportPDF()
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["dpreston10#gmail.com"])
mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)
if let filePath = NSBundle.mainBundle().pathForResource(pdfFileUrl, ofType: "pdf") {
print("File path loaded.")
if let fileData = NSData(contentsOfFile: filePath) {
print("File data loaded.")
mail.addAttachmentData(fileData, mimeType: "application/pdf", fileName: "file.pdf")
}
}
presentViewController(mail, animated: true, completion: nil)
} else {
// show failure alert
}
}
Because your file is put into the user's temporary folder, I do not think it will be accessible from the a NSBundle method. That said, if you are writing the NSData to disk in the temporary folder and then directly emailing it, you may want to consider just passing that variable (pdfData) to the mail controller like:
mail.addAttachmentData(pdfData, mimeType: "application/pdf", fileName: "file.pdf")