Skip to content

Sending email from Cocoa apps

Any serious modern application should be email enabled – even if only for product feedback. It isn’t that long ago (ok, maybe it is) that I used to have a class to open a port connection and format its content into RFC 822 smtp; the hard part was picking up a server that would accept emails, but even that wasn’t hard – there were open sendmail servers back then (ones that were intentionally open, that is).

In the sequence of rewrites for Mail.app, Apple added in the Message framework with its NSMailDelivery class, which at first didn’t really work. But now it does. Then the mailto: URI format expanded from just opening a message window in conforming apps, to being able to handle a full mail message. Now that mail account information is considered to be a part of the user account (for MacOS X, at least), everything is available to make coding email sending as a simple part of any application.

I’m going to explain how to send an email in two different ways – first using mailto:, and then again, using NSMailDelivery. I’ll just touch on the basics, interested parties can research the options.

The mailto: URI takes a sequence of parameters; the first, and required, is the to address for your email. Like this: “mailto:email@hidden”. Other parameters need to be provided in a “key=value” format, joined to the URL in the standard convention, using “?” for the first keyword, and any remaining keywords joined using “&”. Like this: “mailto:email@hidden?SUBJECT=Hello&BODY=spamspamspam”. The entire URL should conform to URL encoding. This is described in RFC 2368, and can be used to construct any sort of email message.

In Cocoa, we need to create a URL with our full email message, and use this to create an NSURL object, then pass it to NSWorkspace to be opened; any app that supports mailto: can respond to this request.

NSString has methods to support URL encoding.

To the code:

NSString *encodedSubject = [NSString stringWithFormat:@"SUBJECT=%@", [[self subject] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
NSString *encodedBody = [NSString stringWithFormat:@"BODY=%@", [[self bodyString] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
NSString *encodedTo = [[self to] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString *encodedURLString = [NSString stringWithFormat:@"mailto:%@?%@&%@", encodedTo, encodedSubject, encodedBody];
NSURL *mailtoURL = [NSURL URLWithString:encodedURLString];
[[NSWorkspace sharedWorkspace] openURL:mailtoURL];

This is an extract from a utility class with instance variables that encapsulates the task of sending email from a standard application. You can drive this programmatically, or by binding into your user interface.

The next method is to use NSMailDelivery. First, include /System/Library/Frameworks/Message.framework in your project (make sure that it is set to be an absolute path), and then open the framework and read the NSMailDelivery header file.

The deliverMessage class method is all we need for normal emails. It takes an NSAttributedString for the body content (so it supports rich text), and a dictionary for headers. If you want to add further headers, perhaps cc, bcc, or even your own customer headers, just add them to the dictionary, and everything will be added as a header. It is perfectly possible to create fully rich content emails this way, with attachments and so on. The format parameter supports both MIME and ASCII format emails. Example:

NSMutableDictionary *headersDict = [NSMutableDictionary dictionary];
NSString *theMessage = [[[NSMutableAttributedString alloc] initWithPath:@"/tmp/ExampleDocument.rtfd" documentAttributes:NULL] autorelease];
[headersDict setObject:[self to] forKey:@"To"];
[headersDict setObject:[self ccArray] forKey:@"Cc"];
[headersDict setObject:[self subject] forKey:@"Subject"];
BOOL result = [NSMailDelivery deliverMessage:[self body] headers:headersDict format:NSMIMEMailFormat protocol:nil];

Note that the default from address (in both cases) will be taken from the default mail account for the user. The NSMailDelivery api also allows you to set the mail account to be used as sender.

You can download an example application including all the code and the utility class mentioned above.

Post a Comment

Your email is never published nor shared. Required fields are marked *