Embedding Images in Outbound Email Using Cocoa Touch

One of the more commonly asked questions about iPhone SDK development is “how do you send attachments in Email using the iPhone SDK?”

The answer is: you can’t.  At least, not yet.

Well, then how are developers seemingly able to do this?  Many of you have seen apps where this looks like this is being done.  The Apple Photos app, for example, seems to be able to do this.

What’s the secret?

Come closer.  You Ready?

Embedded Images.

Embedded images are most commonly seen in your junk email and are a favorite trick used by spammers to circumvent your email filters to slip content in that can’t be scanned textually.

Here, we will use them for good and not evil.  Promise.

Let’s look at some screen grabs and code from my upcoming iPhone App Interpolate.  Interpolate is a numerical analysis app that finds missing range points given a table of data points representing some function, like y=x squared.  In addition to calculating interpolants, it also produces cartesian graphs of each function set, and allows you to send the chart to your friends using email.

img_0012

The code below is from a function call to create my email message.  In my app, I am taking a UIView (or at least, a class derived from UIView) and converting it into a PNG image.  Once I convert the view to PNG, I then convert the image data to a Base64 string, and use that Base64 string as the source for my embedded image in the email.


ChartView *cv = (ChartView *)cvc.view;


UIGraphicsBeginImageContext(cv.bounds.size);
[cv.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


NSData *imageData = UIImagePNGRepresentation(viewImage);
char encodeArray[64 * 1024];
memset(encodeArray,'', sizeof(encodeArray));

// Exercise for the reader - encode takes the image data buffer and encodes it to base64.

// I can't do everything for you - that spoils the fun.
encode([imageData length], (char *)[imageData bytes], sizeof(encodeArray), encodeArray);


NSString *dataStr = [NSString stringWithCString:encodeArray length:strlen(encodeArray)];


// Save to photo library... maybe another time!
// UIImageWriteToSavedPhotosAlbum(viewImage, self, nil, nil);


NSString *body       = [@"" stringByAppendingFormat:@"<b><img src='data:image/png;base64,%@' alt='Interpolate Chart'></b>", dataStr];
NSString *encoded    = [body stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *title      = [[@"" stringByAppendingFormat:@"Interpolate: Function %@", cvc.title] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString * urlString = [@"" stringByAppendingFormat:@"mailto:me@you.com?subject=%@&body=%@", title, encoded];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];

The really interesting bits are:

  • Converting the UIView to a UIImage using UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  • Converting the UIImage to NSData using NSData *imageData = UIImagePNGRepresentation(viewImage);
  • Convert the NSData to Base64
  • Converting the Base64 encoded image data into an NSString using NSString *dataStr = [NSString stringWithCString:encodeArray length:strlen(encodeArray)];
  • Embedding the image into email using NSString *body       = [@"" stringByAppendingFormat:@"<b><img src='data:image/png;base64,%@' alt='Interpolate Chart'></b>", dataStr];

IMPORTANT: the <b> and </b> in the embedded image code above must be present, or mail will strip your image.  Don’t ask me why – it just does.

Wait a minute… where is the Base64 encoding mumbo-jumbo done?

Hey – like my old math teacher used to say – the rest is left as an exercise for the reader.

But if you’ve followed along this far, finding a Base64 C routine to use in your own code is the easy part.

Happy Coding.

About these ads
Tagged , , , , , ,

30 thoughts on “Embedding Images in Outbound Email Using Cocoa Touch

  1. david says:

    I tried this and it opens the mailto: app on the phone, but, the resulting email that I get has the image stripped out. Do you know what could be causing this?

    • davidjhinson says:

      David, the most likely cause is actually a little kludgey thing you have to do to keep Apple Mail from stripping the image out – you have to surround the tag with <b> and </b> – don’t ask me why, it just works that way. If you look at my code, you’ll see me doing it. You may have thought that it was simply stuff you didn’t need – but I ran into the same issue originally in my code and had stumbled upon someone else’s workaround.

      I should have made a bigger deal of that in my article, and will most likely re-emphasize it a little later this evening in the original post.

      Hope this gets you going.

      - D

  2. Monkey says:

    thanks for not posting the complete solution.

  3. David says:

    You are right! That did it. Funny thing, internet explorer does not display the embedded image. Firefox does. There’s some formatting that can be done to deal with it.

  4. This is a very clever solution. However, note, the sent emails did not show images in Gmail or Outlook Express. They do work on the iPhone.

    • davidjhinson says:

      Since many spammers use this method to embed images in Email, most email clients disable this ‘feature’ by default. Your comment that it doesn’t work in Gmail or Outlook Express is not accurate; you simply have to change your settings in these clients to be able to see the embedded images.

  5. The man says:

    Is the translated into a ‘bold’ html tag in the message? If so is this method useful for sending html formatted emails with images?

  6. Mark Edmonds says:

    I get everything you did – very good.

    I’ve been searching high and low for an algorithm to convert an image to base64. Would you be willing to share this to those less capable than yourself!

  7. [...] is used to convert binary data into text that can be transmitted using HTTP (like, say, for embedding images in email) or for somewhat obfuscating user ids and passwords to be sent “in the clear” over HTTP [...]

  8. John says:

    Found this post after independently coming to the same thought myself and wondering if anyone had found a good workaround for gmail.

    I’m pretty sure there’s no client settings within gmail that allows this to function.

    It’s a shame as unless you get nearly 100% coverage of mainstream clients it’s worthless in an app as it turns into a support nightmare.

  9. davidjhinson says:

    John,

    This article posits a possible alternative to embedding images: http://www.campaignmonitor.com/blog/post/1759/embedding-images-revisited/

    I may try it out and publish a follow up if I can get it working.

    - David

  10. [...] a comment » My solution for sending messages via email on the iPhone – detailed here – works for every mail client… except [...]

  11. Anand Mahajan says:

    Hi,
    Thanks for great information.
    But I having problem in converting NSData to base64.
    Can you please help me on that?

    Thanks,
    Anand

  12. davidjhinson says:

    Anand and others – please see this post (http://davidjhinson.wordpress.com/2009/03/09/objective-c-and-http-basic-authentication/) for an example of Base64 encoding in Objective-C.

  13. Roost says:

    Hi All,

    This is proving to be a little of an irritation. I know that my base 64 encoding is correct as I have used it to send with a custom SMTP client, however I am struggling to get this code to work- it suites my application.

    Any guidance would be appreciated.

    “Snippet”

    …..
    NSDictionary *vcfPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/directory;\r\n\tx-unix-mode=0644;\r\n\tname=\"image.jpg\"",kSKPSMTPPartContentTypeKey,
    @"attachment;\r\n\tfilename=\"image.jpg\"",kSKPSMTPPartContentDispositionKey,[dataObj encodeBase64ForData],kSKPSMTPPartMessageKey,@”base64″,kSKPSMTPPartContentTransferEncodingKey,nil];

    NSString *body = [@"" stringByAppendingFormat:@"", vcfPart];

    ….

    Thanx

    • davidjhinson says:

      Don’t know if this is a cut and paste error, but your original code was missing %@. You had:

      NSString *body = [@"" stringByAppendingFormat:@"<b></b>", vcfPart];

      It should be:

      NSString *body = [@"" stringByAppendingFormat:@"<b>%@</b>", vcfPart];

      • Roost says:

        Doh. Silly.

        Now I have a new issue. This change seems to block up the mail app and everything seems to hang- eventually the phone reboots. I cant even get an email out now to see if the image is attached. Hmmmm.

        Any other suggestions please.

      • davidjhinson says:

        Have you checked your iPhone’s crash logs? Might be a good place to start to indicate where the lock up is occurring.

        Then, grab a drink and hunker down with the debugger.

        Good luck.

      • Roost says:

        OK, now we are getting somewhere.

        It now fills the body with the Base 64 code and not the image. Probably a rudimentary issue, but I must be doing something wrong with this;

        NSString *body = [@"" stringByAppendingFormat:@"%@", vcfPart];

        Have tried

        NSString *body = [@"" stringByAppendingFormat:@"img src=image.jpg,%@", vcfPart];

        and various other versions.

        Other ideas please.

      • davidjhinson says:

        It looks like you’re not constructing the IMG tag properly.

        You’re not including the < at the front of the tag or the > at the end of the tag, and so you’re seeing Base64 characters in the body of your message.

        Your string should look like the following:

        NSString *body = [@"" stringByAppendingFormat:@"<b><img src='data:image/png;base64,%@' alt='Interpolate Chart'></b>", vcfPart];

        The src=’image.jpg,%@’ you’re attempting will totally not work.

  14. Roost says:

    OK, I am about to pull my hair out. I have tried everything that I can think of- even other examples. Still nothing. I now have a blank placeholder for the image- but still no image.

    I can only assume the encoding methodology I am using, is not working in this format. Please could you direct me to something that would work, as I am honestly now stumped.

    Your help would be greatly appreciated.

    • davidjhinson says:

      The most likely cause is that the email client you are using is not set to display embedded images. The Google Gmail client does not allow this, for example.

      If you are using any other mail client, look for an option to allow displaying embedded images. That should do the trick.

  15. Jindal Joshi says:

    hi David,

    can you please help regarding that exercise part given by you i tried to solve.

  16. Dave Hards says:

    I had no problems at all setting this up to work with my app, took me 10 minutes. However I’ve spent well over 2 hours trying to get my gmail to display the image. This works like a charm iPhone to iPhone, I just don’t see which gmail settings to use to make the image appear when I’m viewing mail on my MAC or PC, I’m using Firefox on both. Does anyone know what the settings are for gmail?? Thanks, great thread!! Dave

  17. Vibhor Goyal says:

    Hi David,

    Thats a great work u have done above to get this thing done. I was looking for something similar, tried your method and it worked fine.

    But then I looked into the iPhone sdk 3.0 and found out that we have an in app API for email and it allows u to add an attachment, plus it works from within the app. But definitely your code was helpful.

    I think that would be a great add on to your app, let me know if you need code snippet of that.

    Thanks again for posting this.

  18. Shesh says:

    Hi David,

    The thread appears to be old, nevertheless the concern is still valid.
    I want to be able to choose an attachment before sending my in-app email.
    Is that possible in some round about way OR any way for the iPhone?

    Rgds

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 6,731 other followers

%d bloggers like this: