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
NSAttributedStringinstance containing the e-mail body to send.
NSURLinstance which points to a local file that you want to attach in the e-mail.
NSSharingServiceobject and select the one for sharing via e-mail.
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!