Send iphone app via email programmatically

I am writing an iPhone application that requires me to send an email application programmatically. An attachment is a csv file that I create through code. Then I attach the file to the letter, and the application appears on the phone. However, when I send an email to myself, the attachment does not appear in the email. Here is the code I'm using.

[self exportData]; if ([MFMailComposeViewController canSendMail]) { NSString *filePath = [[NSBundle mainBundle] pathForResource:@"expenses" ofType:@"csv"]; NSData *myData = [NSData dataWithContentsOfFile:filePath]; MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init]; mailer.mailComposeDelegate = self; [mailer setSubject:@"Vehicle Expenses from myConsultant"]; NSString *emailBody = @""; [mailer setMessageBody:emailBody isHTML:NO]; [mailer addAttachmentData:myData mimeType:@"text/plain" fileName:@"expenses"]; [self presentModalViewController:mailer animated:YES]; [mailer release]; } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Failure" message:@"Your device doesn't support the composer sheet" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } } - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { switch (result) { case MFMailComposeResultCancelled: NSLog(@"Mail cancelled: you cancelled the operation and no email message was queued."); break; case MFMailComposeResultSaved: NSLog(@"Mail saved: you saved the email message in the drafts folder."); break; case MFMailComposeResultSent: NSLog(@"Mail send: the email message is queued in the outbox. It is ready to send."); break; case MFMailComposeResultFailed: NSLog(@"Mail failed: the email message was not saved or queued, possibly due to an error."); break; default: NSLog(@"Mail not sent."); break; } // Remove the mail view [self dismissModalViewControllerAnimated:YES]; 

Created successfully - I checked the simulator files.

 - (void) exportData { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); NSString *documentsDir = [paths objectAtIndex:0]; NSString *root = [documentsDir stringByAppendingPathComponent:@"expenses.csv"]; NSString * temp=@ "Date,Purpose,Start Odometer,End Odometer, Total Driven, Fees, "; for(int i = 0; i < expenses.count; i++){ VehicleExpense *tempExpense = [expenses objectAtIndex:i]; temp = [temp stringByAppendingString:tempExpense.date]; temp = [temp stringByAppendingString:@", "]; temp = [temp stringByAppendingString:tempExpense.purpose]; temp = [temp stringByAppendingString:@", "]; temp = [temp stringByAppendingString:[NSString stringWithFormat: @"%.02f",tempExpense.start_mile]]; temp = [temp stringByAppendingString:@", "]; temp = [temp stringByAppendingString:[NSString stringWithFormat: @"%.02f",tempExpense.end_mile]]; temp = [temp stringByAppendingString:@", "]; temp = [temp stringByAppendingString:[NSString stringWithFormat: @"%.02f",tempExpense.distance]]; temp = [temp stringByAppendingString:@", "]; temp = [temp stringByAppendingString:[NSString stringWithFormat: @"%.02f",tempExpense.fees]]; temp = [temp stringByAppendingString:@", "]; } [temp writeToFile:root atomically:YES encoding:NSUTF8StringEncoding error:NULL]; NSLog(@"got here in export data--- %@", documentsDir); } 
+6
source share
3 answers

try [mailer addAttachmentData:myData mimeType:@"text/csv" fileName:@"expenses.csv"];

Edit: This is the code that I use in my application:

 - (IBAction) ExportData:(id)sender { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:kExportFileName]; self.timeRecords = [[NSMutableArray alloc] init]; for (int i=0; i< [self.selectedTimeEntries count]; i++) for (int j=0; j<[[self.selectedTimeEntries objectAtIndex:i] count]; j++) if ([[self.selectedTimeEntries objectAtIndex:i] objectAtIndex:j] == [NSNumber numberWithBool:YES]) [self.timeRecords addObject:[self timeEntriesForDay:[self.uniqueArray objectAtIndex:i] forIndex:j]]; if( !([self.timeRecords count]!=0)) { UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"There are no time entries selected!" message:@"Please select at least one time entry before proceeding" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; return; } NSMutableString *csvLine; NSError *err = nil; NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd"]; NSString *dateString = nil; NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setPositiveFormat:@"###0.##"]; NSString *formattedNumberString = nil; if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; } for (timeEntries *timeEntry in self.timeRecords) { csvLine = [NSMutableString stringWithString:timeEntry.client]; [csvLine appendString:@","]; [csvLine appendString:timeEntry.category]; [csvLine appendString:@","]; [csvLine appendString:timeEntry.task]; [csvLine appendString:@","]; dateString = [dateFormatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:timeEntry.date]]; [csvLine appendString:dateString]; [csvLine appendString:@","]; formattedNumberString = [numberFormatter stringFromNumber:timeEntry.duration]; [csvLine appendString:formattedNumberString]; [csvLine appendString:@","]; [csvLine appendString:timeEntry.description]; [csvLine appendString:@"\n"]; if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSString *oldFile = [[NSString alloc] initWithContentsOfFile:filePath]; [csvLine insertString:oldFile atIndex:0]; BOOL success =[csvLine writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:&err]; if(success){ } [oldFile release]; } } if (!appDelegate.shouldSendCSV) { self.csvText = csvLine; } if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { [self emailExport:filePath]; } self.selectedTimeEntries =nil; self.navigationController.toolbarHidden = NO; } - (void)emailExport:(NSString *)filePath { NSLog(@"Should send CSV = %@", [NSNumber numberWithBool:appDelegate.shouldSendCSV]); MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init]; picker.mailComposeDelegate = self; // Set the subject of email [picker setSubject:@"My Billed Time Export"]; // Add email addresses // Notice three sections: "to" "cc" and "bcc" NSString *valueForEmail = [[NSUserDefaults standardUserDefaults] stringForKey:@"emailEntry"]; NSString *valueForCCEmail = [[NSUserDefaults standardUserDefaults] stringForKey:@"ccEmailEntry"]; if( valueForEmail == nil || [valueForEmail isEqualToString:@""]) { UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"Please set an email address before sending a time entry!" message:@"You can change this address later from the settings menu of the application!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; return; } else { [picker setToRecipients:[NSArray arrayWithObjects:valueForEmail, nil]]; } if(valueForCCEmail != nil || ![valueForCCEmail isEqualToString:@""]) { [picker setCcRecipients:[NSArray arrayWithObjects:valueForCCEmail, nil]]; } // Fill out the email body text NSString *emailBody = @"My Billed Time Export File."; // This is not an HTML formatted email [picker setMessageBody:emailBody isHTML:NO]; if (appDelegate.shouldSendCSV) { // Create NSData object from file NSData *exportFileData = [NSData dataWithContentsOfFile:filePath]; // Attach image data to the email [picker addAttachmentData:exportFileData mimeType:@"text/csv" fileName:@"MyFile.csv"]; } else { [picker setMessageBody:self.csvText isHTML:NO]; } // Show email view [self presentModalViewController:picker animated:YES]; // Release picker [picker release]; } 
+11
source

Danut Pralea's answer is excellent, but the code seems too long for those who are looking for an easy way to send attachments via email programmatically.

The gist

I cut off his answer in order to take out only the important bits, and also reorganized it like this:

 MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; mailComposer.mailComposeDelegate = self; mailComposer.subject = @"Sample subject"; mailComposer.toRecipients = @[@" arthur@example.com ", @" jeanne@example.com ", ...]; mailComposer.ccRecipients = @[@" nero@example.com ", @" mashu@example.com ", ...]; [mailComposer setMessageBody:@"Sample body" isHTML:NO]; NSData *fileData = [NSData dataWithContentsOfFile:filePath]; [mailComposer addAttachmentData:fileData mimeType:mimeType fileName:fileName]; [self presentViewController:mailComposer animated:YES completion:nil]; 

This is basically the essence of this, it is enough, as it is. If, for example, you put this code in the action of a button, it will present a screen for composing an e-mail with the corresponding fields pre-filled, as well as the file that you want to add to the letter.

additional literature

Framework

MFMailComposeViewController is under the MessageUI Framework, so to use it, import (if you have not done so already) the Framework, for example:

 #import <MessageUI/MessageUI.h> 

Checking Mail Capabilities

Now, when you run the source code and have not yet set up an email account on your device (not sure what this behavior is on the simulator), this code will crash your application. It appears that if the email account has not yet been configured, executing [[MFMailComposeViewController alloc] init] will still cause mailComposer be zero, which caused the failure . Since the answer in a related question reads:

You must verify that MFMailComposeViewController can send your mail immediately before sending

You can do this using the canSendMail method:

 if (![MFMailComposeViewController canSendMail]) { [self openCannotSendMailDialog]; return; } 

This can be done right before you make [[MFMailComposeViewController alloc] init] so that you can immediately inform the user about it.

CannotSendMail Processing

If canSendMail returns false according to Apple Dev Docs, this means that the device is not configured to send mail. This may mean that the user may not have set up a Mail account yet. To help the user with this, you can offer to open the Mail application and set up your account. You can do it like this:

 NSURL *mailUrl = [NSURL URLWithString:@"message://"]; if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) { [[UIApplication sharedApplication] openURL:mailUrl]; } 

Then you can implement openCannotSendMailDialog as follows:

 - (void)openCannotSendMailDialog { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:@"Cannot send mail." preferredStyle:UIAlertControllerStyleAlert]; NSURL *mailUrl = [NSURL URLWithString:@"message://"]; if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) { [alert addAction: [UIAlertAction actionWithTitle:@"Open Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [[UIApplication sharedApplication] openURL:mailUrl]; }]]; [alert addAction: [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; } else { [alert addAction: [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; } [self presentViewController:alert animated:YES completion:nil]; } 

Mime Types

If you, like me, you forgot / don't know which mimeType use, here is a resource that you can use. Most likely, text/plain sufficient if the attached file is plain text or image/jpeg / image/png for images.

delegate

As you probably noticed, Xcode gives us a warning in the following line:

 mailComposer.mailComposeDelegate = self; 

This is due to the fact that we have not yet established ourselves to comply with the relevant protocol and implement its delegation method. If you want to receive messages about whether the mail was canceled, saved, sent or even failed to send, you need to set your class in accordance with the MFMailComposeViewControllerDelegate protocol, and process the following events :

  • MFMailComposeResultSent
  • MFMailComposeResultSaved
  • MFMailComposeResultCancelled
  • MFMailComposeResultFailed

According to Apple Dev Docs (emphasis mine):

The comment submission controller for mail is not automatically disabled . When the user selects the buttons to send an email message or cancel the interface, the mail message view controller calls the mailComposeController: didFinishWithResult: error: delegate method. Your implementation of this method should explicitly reject the view controller .

With this in mind, we can then implement the delegate method as follows:

 - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { switch (result) { case MFMailComposeResultSent: // Mail was sent break; case MFMailComposeResultSaved: // Mail was saved as draft break; case MFMailComposeResultCancelled: // Mail composition was cancelled break; case MFMailComposeResultFailed: // break; default: // break; } // Dismiss the mail compose view controller. [controller dismissViewControllerAnimated:YES completion:nil]; } 

Conclusion

The final code might look like this:

 - (void)openMailComposerWithSubject:(NSString *)subject toRecipientArray:(NSArray *)toRecipientArray ccRecipientArray:(NSArray *)ccRecipientArray messageBody:(NSString *)messageBody isMessageBodyHTML:(BOOL)isHTML attachingFileOnPath:(NSString)filePath mimeType:(NSString *)mimeType { if (![MFMailComposeViewController canSendMail]) { [self openCannotSendMailDialog]; return; } MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; mailComposer.mailComposeDelegate = self; mailComposer.subject = subject; mailComposer.toRecipients = toRecipientArray; mailComposer.ccRecipients = ccRecipientArray; [mailComposer setMessageBody:messageBody isHTML:isHTML]; NSData *fileData = [NSData dataWithContentsOfFile:filePath]; NSString *fileName = filePath.lastPathComponent; [mailComposer addAttachmentData:fileData mimeType:mimeType fileName:fileName]; [self presentViewController:mailComposer animated:YES completion:nil]; } - (void)openCannotSendMailDialog { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:@"Cannot send mail." preferredStyle:UIAlertControllerStyleAlert]; NSURL *mailUrl = [NSURL URLWithString:@"message://"]; if ([[UIApplication sharedApplication] canOpenURL:mailUrl]) { [alert addAction: [UIAlertAction actionWithTitle:@"Open Mail" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [[UIApplication sharedApplication] openURL:mailUrl]; }]]; [alert addAction: [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; } else { [alert addAction: [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; } [self presentViewController:alert animated:YES completion:nil]; } - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error { NSString *message; switch (result) { case MFMailComposeResultSent: message = @"Mail was sent."; break; case MFMailComposeResultSaved: message = @"Mail was saved as draft."; break; case MFMailComposeResultCancelled: message = @"Mail composition was cancelled."; break; case MFMailComposeResultFailed: message = @"Mail sending failed."; break; default: // break; } // Dismiss the mail compose view controller. [controller dismissViewControllerAnimated:YES completion:^{ if (message) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Confirmation" message:message preferredStyle:UIAlertControllerStyleAlert]; [alert addAction: [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:alert animated:YES completion:nil]; } }]; } 

When the button is pressed, the action is as follows:

 - (IBAction)mailButtonTapped:(id)sender { NSString *reportFilePath = ... [self openMailComposerWithSubject:@"Report Files" toRecipientArray:mainReportRecipientArray ccRecipientArray:subReportRecipientArray messageBody:@"I have attached report files in this email" isMessageBodyHTML:NO attachingFileOnPath:reportFilePath mimeType:@"text/plain"]; } 

I went overboard a bit, but you can take and use the code that I posted here with salt. Of course, you need to adapt it to your requirements, but it is up to you. (I also modified this answer from my working source code, so there may be errors somewhere, please make a comment if you find one :))

+2
source

The problem was that I was not looking for a suitable place for the file. Thanks @EmilioPalesz.

Here is the code I need:

  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); NSString *documentsDir = [paths objectAtIndex:0]; NSString *root = [documentsDir stringByAppendingPathComponent:@"expenses.csv"] NSData *myData = [NSData dataWithContentsOfFile:root]; 
0
source

Source: https://habr.com/ru/post/899627/


All Articles