2011-10-19 13 views
6

Piszę aplikację na iPhone'a, która wymaga programowego dodania załącznika wiadomości e-mail. Załącznik jest plikiem csv, który tworzę poprzez kod. Następnie załączam plik do wiadomości e-mail, a załącznik wyświetla się na telefonie. Kiedy jednak wyślę do siebie wiadomość e-mail, załącznik nie pojawi się w wiadomości e-mail. Oto kod, którego używam.Wysyłanie załącznika iphone za pomocą programu pocztowego programowo

[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]; 

Powodzenie jest tworzone - sprawdziłem pliki symulatora.

- (void) exportData 
{ 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); 
    NSString *documentsDir = [paths objectAtIndex:0]; 
    NSString *root = [documentsDir stringByAppendingPathComponent:@"expenses.csv"]; 
    NSString *[email protected]"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); 

} 
+1

Spróbuj zalogować się myData, aby sprawdzić, czy nie zwraca wartości zerowej. – EmilioPelaez

+0

Jest pusty ... jak mogę uzyskać plik? Edytuję mój oryginalny wpis, aby pokazać, gdzie utworzę plik. – coder

+1

Wygląda na to, że 'filePath = [[NSBundle mainBundle] pathForResource: @" expenses "ofType: @" csv "];' nie otrzymuje ścieżki do pliku, którego szukasz. Jeśli mam rację, ta metoda po prostu pobiera zasoby w twoim folderze .app. Zalecam, aby uzyskać ścieżkę w taki sam sposób, jak w przypadku metody exportData. – EmilioPelaez

Odpowiedz

11

spróbować [mailer addAttachmentData:myData mimeType:@"text/csv" fileName:@"expenses.csv"];

Edit: Jest to kod używam w mojej aplikacji:

- (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]; 
} 
+0

Po prostu próbowałem, ale to nie pomogło. – coder

0

Problem było to, że nie szuka we właściwym miejsce dla pliku. Dzięki @EmilioPalesz.

Oto kod Potrzebowałem:

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

NSData *myData = [NSData dataWithContentsOfFile:root]; 
2

Danut Pralea „s answer jest wielka, jednak kod wydaje się być zbyt długo dla kogoś szukającego prosty sposób do wysłać załącznik za pośrednictwem poczty elektronicznej programowo .

Istotą

I przycina swoją odpowiedź do wykupienia tylko ważnych bitów, a także refactored go tak:

MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc] init]; 
mailComposer.mailComposeDelegate = self; 

mailComposer.subject = @"Sample subject"; 

mailComposer.toRecipients = @[@"[email protected]", @"[email protected]", ...]; 
mailComposer.ccRecipients = @[@"[email protected]", @"[email protected]", ...]; 

[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]; 

To w zasadzie sedno tego, to wystarczy jako to jest. Jeśli, na przykład, umieścisz ten kod w akcji przycisku, wyświetli on wiadomość e-mail przedstawiającą ekran z wypełnionymi polami, a także plik, który chcesz dołączyć do wiadomości e-mail.

Dalsze Reading

ramowa

MFMailComposeViewController jest zgodnie z ramami MessageUI, więc z niego korzystać, import (jeśli jeszcze tego nie zrobiły) Ramy tak:

#import <MessageUI/MessageUI.h> 

Sprawdzanie poczty Poczta

Teraz po uruchomieniu kodu źródłowego, a jeszcze nie skonfigurowałeś konta pocztowego na swoim urządzeniu, (nie wiesz, co to jest na symulatorze), ten kod spowoduje awarię aplikacji. Wygląda na to, że jeśli konto pocztowe nie jest jeszcze skonfigurowane, wykonanie [[MFMailComposeViewController alloc] init] nadal spowoduje, że mailComposer będzie zerowe, causing the crash.Jak answer w połączonej pytanie stwierdza:

Należy sprawdzić to MFMailComposeViewController są w stanie wysyłać pocztę tuż przed wysłaniem

Można to zrobić za pomocą metody canSendMail tak:

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

Możesz to naprawić przed wykonaniem [[MFMailComposeViewController alloc] init], aby natychmiast powiadomić użytkownika.

Handling cannotSendMail

If canSendMail zwraca false, według Apple Dev Docs, co oznacza, że ​​urządzenie nie jest skonfigurowany do wysyłania poczty. To could mean, że być może użytkownik nie skonfigurował jeszcze swojego konta pocztowego. Aby pomóc użytkownikowi w tym, możesz zaoferować open the Mail app i skonfigurować swoje konto. Można to zrobić tak:

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

Następnie można wdrożyć openCannotSendMailDialog tak:

- (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]; 
} 

typów MIME

Jeśli tak jak ja, że ​​zapomniałeś/pewności, który mimeType używać , here to zasób, którego możesz użyć. Najprawdopodobniej wystarcza text/plain, jeśli plik, który dołączasz, jest zwykłym tekstem lub image/jpeg/ dla obrazów.

Delegat

Jak zapewne zauważyliście, Xcode rzuca nam ostrzeżenie o następującej linii:

mailComposer.mailComposeDelegate = self; 

To dlatego, że nie zostały jeszcze ustalone Nas do dostosowania się do odpowiedniego protokołu i wdrożenia metoda delegata. Jeśli chcesz odbierać zdarzenia, czy poczta została anulowana, zapisywane wysyłane lub nawet udało wysyłania, trzeba ustawić klasę zgodne z protokołem MFMailComposeViewControllerDelegate i obsługiwać following events:

  • MFMailComposeResultSent
  • MFMailComposeResultSaved
  • MFMailComposeResultCancelled
  • MFMailComposeResultFailed

Według Apple Dev Docs (podkreślenie moje):

Administrator wiadomości e-mailowej komponentowej nie jest automatycznie usuwany z konta. Gdy użytkownik dotknie przycisków, aby wysłać wiadomość e-mail lub anuluje interfejs, kontroler widoku wiadomości e-mail decyduje o sposobie jego delegata. Twoja implementacja tej metody musi wyraźnie odwołać kontroler widoku.

Mając to na uwadze, możemy zaimplementować metodę delegata tak:

- (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]; 
} 

Wnioski

Ostateczny kod może wyglądać tak:

- (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]; 
     } 
    }]; 
} 

Wygląd przycisku:

- (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"];  
} 

W pewnym sensie wyskoczyłem tutaj, ale można, z przymrużeniem oka, wziąć i użyć kodu, który tutaj zamieściłem. Oczywiście istnieje potrzeba dostosowania go do swoich wymagań, ale to zależy od Ciebie. (Zmieniłem też tę odpowiedź z mojego działającego kodu źródłowego, więc mogą być gdzieś błędy, proszę zrób komentarz, jeśli znajdziesz :))

+0

@Shebuka W proponowanej edycji, jeśli program mailComposer jest tym, który ma zostać odrzucony, czy nie powinien to być "[controller dismissView ..." zamiast "[self dismissView ..."? – Keale

+1

tak, i używam 'kontrolera', ale oficjalne dokumenty Apple używają' self' ... https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller?language=objc Listing 3 – Shebuka

+0

@Shebuka Cóż, to jest dziwne. Otworzyłem link i na pewno zobaczyłem przykładowy kod używając '[self dismiss ...'. Wersja [Swift] (https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller) używa jednak 'controller.dismiss ...'. – Keale

Powiązane problemy