For 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:
- 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. - Compose the above two objects into an array.
- Create an
NSSharingService
object and select the one for sharing via e-mail. - 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!
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 = ["abc@dom.com"]
service.subject = "Vea software"
service.performWithItems(["This is an email for auto testing through code."])
}
}
Usage:
SendEmail.send()
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 ?
Great article, thanks! :)
Looking at the headers of
NSSharingService
for 10.9, it seems that Apple has added a propertyrecipients
of typeNSArray
. This should allow pre-filling the recipient’s e-mail addresses.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.