Programmatically Sending Rich Text e-mail with Attachments on the Mac

MailFor a long time now iOS has MFMailComposeViewController for apps to pre-compose an e-mail that the user can send out from his account. However sending e-mail from a Mac OS X app is not as straightforward as it is on iOS. As of this writing, Apple haven’t gotten around to provide an equivalent of that iOS gem.

For years, many apps on the Mac tried to accomplish this by either using mailto URLs that contains a message body or use Scripting Bridge to control OS X’s built-in mail client. However each of these approaches has their limitations – mailto URLs can’t attach files and sandboxing severely limits an app’s capability to script other applications.

Fortunately the OS X provides NSSharingService class that you can use to pre-compose an HTML e-mail with attachments for your users. This may be quite handy if your user needs to create a report of some sort for other people to consume; your app can generate the report as an e-mail with everything ready to go.

Without further ado, here’s how you use NSSharingService to compose a rich text e-mail with attachments:

  1. Prepare these two objects
    • An NSAttributedString instance containing the e-mail body to send.
    • An NSURL instance which points to a local file that you want to attach in the e-mail.
  2. Compose the above two objects into an array. 
  3. Create an NSSharingService object and select the one for sharing via e-mail.
  4. Tell the object to share your array.

Here’s some sample code that may help you

NSAttributedString* textAttributedString = ... // generate the message body here.
NSURL* tempFileURL = ... // write the attachment to that file

NSSharingService* mailShare = [NSSharingService sharingServiceNamed:NSSharingServiceNameComposeEmail];
NSArray* shareItems = @[textAttributedString,tempFileURL];
[mailShare performWithItems:shareItems];

But I have HTML text to send as e-mail!

No worries. You can simply convert the HTML string into an NSAttributedString with a line of code.

NSString* htmlText = @"<html><body>Hello, <b>World</b>!</body></html>";
NSData* textData = [NSData dataWithBytes:[htmlText UTF8String] length:[htmlText lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
NSAttributedString* textAttributedString = [[NSAttributedString alloc] initWithHTML:textData options:options documentAttributes:nil];

But I dynamically generate the attachment in-memory!

Well, you just have to write out the attachment into a file. Use NSTemporaryDirectory  create a subdirectory under that and write out the file named as what you want to eventually show up in the e-mail message. You shouldn’t worry about removing the file since OS X will clean it up some time after your app quits. Besides, the file needs to be there for Mail to read it and needs to stay there until the user either sends the e-mail message or saves it as draft.

NSUUID* uuid = [NSUUID new];
NSString* tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[uuid UUIDString]];
NSFileManager* fm = [NSFileManager new];
[fm createDirectoryAtPath:tempDir withIntermediateDirectories:YES attributes:nil error:nil];

NSString* tempFile = [tempDir stringByAppendingPathComponent:@"report.csv"];
NSURL* tempFileURL = [NSURL fileURLWithPath:tempFile];
NSData* csv = ...; // generate the data here
[csv writeToURL:tempFileURL atomically:NO];
// At this point you can pass tempFileURL to NSSharingService

How do I pre-fill the recipient’s e-mail?

Frankly, I haven’t figured this one out yet. It appears that NSSharingService doesn’t provide a way to specify the destination of the message. I’ve tried adding another NSURL object that contains a mailto scheme in the array of objects to share, but that just appends the URL as a link in the message body. If you figured this out, please do let me know.

That’s all folks. Hope this helps!

Avoid App Review rules by distributing outside the Mac App Store!

Get my FREE cheat sheets to help you distribute real macOS applications directly to power users.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

Avoid Delays and Rejections when Submitting Your App to The Store!

Follow my FREE cheat sheets to design, develop, or even amend your app to deserve its virtual shelf space in the App Store.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

4 thoughts on “Programmatically Sending Rich Text e-mail with Attachments on the Mac

  1. Since MacOS 10.8 you can easily send e-mails with pre-set recipients and subject like this:

    class SendEmail: NSObject {
    static func send() {
    let service = NSSharingService(named: NSSharingServiceNameComposeEmail)!
    service.recipients = [""]
    service.subject = "Vea software"

    service.performWithItems(["This is an email for auto testing through code."])



  2. Hi Samsito,

    I used this article and was able to attach a file only with the Mac Mail App.

    The same code doesn’t attach my file with outlook or thunderbird ?

    Any idea or help ?

  3. Great article, thanks! :)
    Looking at the headers of NSSharingService for 10.9, it seems that Apple has added a property recipients of type NSArray. This should allow pre-filling the recipient’s e-mail addresses.

    1. Hi Götz Fabian,

      I am assuming that you could implement this.

      Did your implementation work across clients ? Say for MS Outlook or Mozilla Thunderbird etc.

Leave a Reply