BuySellAds.com

Until Dec 3rd, 44% off all Manning books, including Barcodes with iOS! Promo code: mobicftw
Our DNA is written in Objective-C
Jump

Sending E-Mail Attachments

Ravikumar Gajjavelly asks:

I want to send email It contains image as an attachment.How can we send am email.how to attach image.can anybody please give solution.

When creating my app GeoCorder I was faced with the exact same dilemma of needing to attach a file to an email. I did some research and this is what I found.

Embed via Data URL

E-Mails on the iPhone (pre OS 3.0) are created by calling a specific method and giving mailto URL. The documentation neglects to mention this, but you can also pass HTML code as message text. This HTML can also include base64-encoded data in an so-called data URL. RFC 2397 covers this. Here is an impressive working example that I found on the internet.

NSMutableString buffer=[NSMutableString stringWithString:
@"<title>PAGE</title>example<img alt="Red dot" />"];
NSString subjectLine =@"Example HTML formatted EMAIL with Image";
 
NSString *str1 = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorSystemDefault, (CFStringRef) subjectLine, NULL, CFSTR(";/?:@&amp;=+$,"), kCFStringEncodingUTF8);
NSString *str2 = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorSystemDefault, (CFStringRef)buffer, NULL, CFSTR(";/?:@&amp;=+$,"), kCFStringEncodingUTF8);
 
NSString *urlString = [NSString stringWithFormat:@"mailto:?subject=%@&amp;body=%@",str1 ,str2 ];
 
[UIApplication sharedApplication openURL:NSURL URLWithString:urlString];

This hack might look like a good idea at first, until you discover that it only works for a very limited number of e-mail clients, mostly just in the Apple and Linux world which still leaves out around 80% of your customers. Therefore you should refrain from using it.

Preload to Web Server

Another viable possibility is to upload the file to your webserver in the background and then attach only a link to it to a new mail. Such a link could either be a literal link or an img tag referencing the image.

But the problem is that once you start saving files on your web server you also burden yourself with maintaining this cache. Your customers might be irked if you remove old files which they had thought to be present inside their mailbox.

Another drawback is that it also forcing the user to quit your app which is something I did not want.

Custom SMTP Sender

You could also create a class that speaks SMTP directly to the target e-mail’s message exchanger. In a world without spam this would work like a charm. There are some major catches mostly having to do with fighting unwanted e-mails.

This is a simple thing to try out with telnet to port 25 of the mail exchanger of your choice. But my tests have shown that besides of having to look up the MX record of the target email’s domain you can never be certain that this SMTP will also accept your mails.

The reason being that modern SMTP servers consult IP blacklists that contain ranges of IP addresses that are used dynamically. If you are on 3G or even on a dynamic IP subnet via Cable/DSL your IP is most likely on this blacklist. The only SMTP server that will accept your mails is the one belonging to your domain.

So the variant that showed the most promise was a complete no go as well.

Web Server

My app GeoCorder allows users to mail a GPS track to an e-mail address. I found that the simplest solution is to have a web server which constructs the e-mail exactly like you want it, including attachments, and sends it to the recipient via e-mail.

On the web server I have a simple script that takes the GPS XML data in a HTTP post, constructs an E-Mail and mails it out via my own SMTP server. I found this to be working like a charm.

- (void) mailGPX:(NSString *)toMail delegate:(id)delegate
{
	myDelegate = delegate;
 
	NSString *subject = self.title;
	myDelegate = delegate;
 
	//build GPX text
	NSMutableString *buf = [[NSMutableString alloc] init];
 
	[buf appendString:[self asXML]];
 
	NSString *percentEscapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[@"Track recorded " stringByAppendingString:subject] , NULL, (CFStringRef)@"=&amp;", kCFStringEncodingUTF8);
 
	NSString *URL=[NSString stringWithFormat:@"http://www.myserver.com/mailGPX.aspx?to=%@&amp;subject=%@", toMail, percentEscapedString];
	NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:URL]
															cachePolicy:NSURLRequestUseProtocolCachePolicy
														timeoutInterval:10.0];
	[theRequest setHTTPMethod:@"POST"];
	[theRequest addValue:@"text/xml" forHTTPHeaderField: @"Content-Type"];
 
	//create the body
	NSMutableData *postBody = [NSMutableData data];
	[postBody appendData:[buf dataUsingEncoding:NSUTF8StringEncoding]];
 
	//add the body to the post
	[theRequest setHTTPBody:postBody];
 
	NSURLConnection *theConnection=[[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
	if (theConnection) {
 
		// Create the NSMutableData that will hold
		// the received data
		// receivedData is declared as a method instance elsewhere
		if (!receivedData)
		{
			receivedData=[[NSMutableData data] retain];
		}
	} else {
		NSLog(@"Error");
		// inform the user that the download could not be made
	}
 
 
	[buf release];
}
 
 
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    // this method is called when the server has determined that it
    // has enough information to create the NSURLResponse
 
    // it can be called multiple times, for example in the case of a
    // redirect, so each time we reset the data.
    // receivedData is declared as a method instance elsewhere
    [receivedData setLength:0];
	//NSLog(@"didReceiveResponse");
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    // append the new data to the receivedData
    // receivedData is declared as a method instance elsewhere
    [receivedData appendData:data];
	//NSLog(@"didReceiveData");
 
}
 
- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error
{
    // release the connection, and the data object
  //  [connection release];
    // receivedData is declared as a method instance elsewhere
    [receivedData release];
	receivedData = nil;
 
    // inform the user
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Cannot Send GPX E-Mail" message:[error localizedDescription] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
	[alert show];
	[alert release];
 
   // NSLog(@"Connection failed! Error - %@ %@",
   //       [error localizedDescription],
   //       [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
 
 
	 if (myDelegate &amp;&amp; [myDelegate respondsToSelector:@selector(sendingDone:)])
	 {
		 [myDelegate performSelector:@selector(sendingDone:) withObject:self];
	 }
 
}
 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
 //   NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
	 if (myDelegate &amp;&amp; [myDelegate respondsToSelector:@selector(sendingDone:)])
	 {
		[myDelegate performSelector:@selector(sendingDone:) withObject:self];
	 }
}

Waiting for OS 3.0

And finally the most optimal solution requires waiting for iPhone OS 3.0. This supports attachments as well as in-app e-mailing. So you will be able to construct custom e-mails including attachments within your app and allow the user to mail it from there.


Categories: Q&A

%d bloggers like this: