Embedding Images in Outbound Email Using Cocoa Touch

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.

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

  1. 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?

    Like

    1. 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

      Like

  2. 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.

    Like

    1. 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.

      Like

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

    Like

  4. 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!

    Like

    1. Mark,

      Googling “Base64 encoder c” yields 191,000 hits.

      There are a minimum of three encoding solutions I saw on the first page that will work just fine for encoding NSData to Base64.

      Like

  5. 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.

    Like

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

    Thanks,
    Anand

    Like

  7. 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

    Like

    1. 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];

      Like

      1. 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.

        Like

      2. 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.

        Like

      3. 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.

        Like

      4. 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.

        Like

  8. 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.

    Like

    1. 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.

      Like

  9. 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

    Like

  10. 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.

    Like

  11. 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

    Like

Leave a comment