Back to Blog

Here is a guest submission by one of our developers, Hahnemann. The background for this article is that we needed to show inline email attachments in our clients’ Outlook accounts. This was a long battle, as Outlook has some quirks regarding attachments that I find to be antiquated after having seen inline attachments with other email systems for the last 3+ years. Happy holidays and happy new year! — Bekki

The process of composing an email using iOS is fairly simple. The MFMailComposeViewController class provides a standard interface to compose and send an email as follows:

MFMailComposeViewController *email = [[MFMailComposeViewController alloc] init];
[email setToRecipients:[NSArray arrayWithObject:@"someone@somewhere.com"]];
[email setSubject:@"Your Subject"];
[email setMessageBody:@"Your message." isHTML:NO];
[email setMailComposeDelegate:self];
[self presentModalViewController:email animated:YES];
[email release];

If you want to include an attachment, you need to reference the attachment and send it inside a message to the class instance. Here is an example that attaches a file containing HTML:

NSString *html = @"<html><head></head><body>Some markup.</body></html>";
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
[controller addAttachmentData:data mimeType:@"txt/html" fileName:@"markup.html"];

This works great, except for one problem. Email clients such as Gmail and Outlook will not display the attachment text inline. The attachment will be included in a preview pane for the user to open or select in order to display or download. This is a common feature used by most email clients to increase security and privacy.

The trick to make the attachment appear inline is to convert it to an image first, compose your email in HTML format, and attach the image instead. Obviously not all attachments can be converted to images (e.g. a SQL database) but our example contains markup that can be transformed to a JPEG or PNG when rendered inside a UIWebView.

Here’s how it works. First, you have to render the HTML inside a UIWebView that is hidden from the user:

NSString *html = @"<html><head></head><body>Some markup.</body></html>";
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 700, 300)];
webView.delegate = self;
webView.hidden = YES;
[self.view addSubview:webView];
[self.view sendSubviewToBack:webView];
[webView loadHTMLString:html baseURL:nil];
[webView release];

Next, you need to wait for the UIWebView to render the HTML before creating the image, or the image itself will be blank or incomplete. This is done inside the webViewDidFinishLoad instance method that is invoked by the UIWebView delegate when it finishes loading a frame. Notice we use here a performSelector that calls a separate method named composeEmail after a delay of one second, just enough time for the render process to finish before composing the email. The method needs the webView because this is where the markup is rendered:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [self performSelector:@selector(composeEmail:) withObject:webView afterDelay:1];
}

Finally, in composeEmail you create and save the image to your Documents app folder, before you attach it. Remember the email has to be in HTML format:

There are other recommended ways to do this, such as including images using Base64 encoding. This appears to work, but you also need control of the MIME standard in the code and unfortunately iOS does not allow this.