views:

917

answers:

2

Im using the MFMailComposeViewController to send mail from within an application. I have added the code from the Apple example to a UITableViewController, and all works as expected when I trigger the modalViewController from a UIToolBarButton. The problem comes when I put a UIActionSheet between the UIToolBarButton and the MFMailComposeViewController code.

I want to present the user with the option to send by email or post to Facebook. When I call the MFMailComposeViewController methods after the dismissal of the UIActionSheet, my app crashes when the method tries to load the modalViewController. Code below, any ideas??

// UIToolBarButton generates the email string and displays the UIActionSheet with email options
-(void)onEmailButtonTouch {

int mySection;
int myRow;
emailString = [NSString stringWithFormat:@"<b><p>Ten Essentials Check List</b><br />%@</p>", [myList valueForKey:@"listName"]];

for (mySection = 0; mySection < [[fetchedResultsController sections] count]; mySection ++) {
 NSString *sectionName = [NSString stringWithFormat:@"<p><b>%@ Group</b></p><ul>", [[[fetchedResultsController sections] objectAtIndex:mySection] name]];
 emailString = [emailString stringByAppendingString:sectionName];
 id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:mySection];
 for (myRow = 0; myRow < [sectionInfo numberOfObjects]; myRow ++) {

  // Get the managedObject
  NSIndexPath *indexPath = [NSIndexPath indexPathForRow:myRow inSection:mySection];

  NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
  //Get the related Item object
  Item *item  = [managedObject valueForKey:@"item"];
  NSString *itemName = [NSString stringWithFormat:@"<li>%@</li>", item.itemName];
  emailString = [emailString stringByAppendingString:itemName];

 }
 emailString = [emailString stringByAppendingString:@"</ul>"];
}
NSLog(@"email string = :\n%@", emailString);
[self showEmailOptions];
}

// Display an UIActionSheet with email/facebook buttons
-(void)showEmailOptions {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"eMail Options" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:NULL otherButtonTitles:@"Send List via Email", @"Post List to Facebook", NULL];
[actionSheet showFromToolbar:self.navigationController.toolbar];
[actionSheet release];
}

// Call the MFMailComposeViewController methods if the user selects the Email button of the actioSheet
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {

if (buttonIndex == 0){
 NSLog(@"Opening email");
 [self showPicker];
}

}

#pragma mark -
#pragma mark Send Mail

-(void)showPicker
{
// This sample can run on devices running iPhone OS 2.0 or later  
// The MFMailComposeViewController class is only available in iPhone OS 3.0 or later. 
// So, we must verify the existence of the above class and provide a workaround for devices running 
// earlier versions of the iPhone OS. 
// We display an email composition interface if MFMailComposeViewController exists and the device can send emails.
// We launch the Mail application on the device, otherwise.

Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil)
{
 // We must always check whether the current device is configured for sending emails
 if ([mailClass canSendMail])
 {
  [self displayComposerSheet];
 }
 else
 {
  [self launchMailAppOnDevice];
 }
}
else
{
 [self launchMailAppOnDevice];
}
}




// Displays an email composition interface inside the application. Populates all the Mail fields. 
-(void)displayComposerSheet {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;

[picker setSubject:@"Here is your gear check list!"];


// Attach an image to the email
NSString *path = [[NSBundle mainBundle] pathForResource:@"Checkmark_icon" ofType:@"png"];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"Checkmark_icon"];

// Fill out the email body text
NSString *emailBody = emailString;
[picker setMessageBody:emailBody isHTML:YES];

// CRASH HAPPENS ON THE LINE BELOW //
[self presentModalViewController:picker animated:YES];
[picker release];
}


// Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error 
{   
//message.hidden = NO;
// Notifies users about errors associated with the interface
switch (result)
{
 case MFMailComposeResultCancelled:
  NSLog (@"Result: canceled");
  break;
 case MFMailComposeResultSaved:
  NSLog (@"Result: saved");
  break;
 case MFMailComposeResultSent:
  NSLog (@"Result: sent");
  break;
 case MFMailComposeResultFailed:
  NSLog (@"Result: failed");
  break;
 default:
  NSLog (@"Result: not sent");
  break;
}
[self dismissModalViewControllerAnimated:YES];
}


#pragma mark -
#pragma mark Workaround

// Launches the Mail application on the device.
-(void)launchMailAppOnDevice {
NSString *recipients = @"mailto:[email protected][email protected],[email protected]&subject=Here is your gear check list!";
NSString *body = emailString;

NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
A: 

I think your problem is that you need

[emailString retain];

it's an autorelease object, and I think it's getting freed after onEmailButtonTouch returns, so that it's invalid when your alert notification fires.

David Maymudes
emailString is an ivar, so is not released until the VC is deallocatedJk
Alpinista
if it were a property marked (retain), then this would be the case, but then you'd need to refer to it as self.emailString when you did the assignment. As it is, it's not being auto-retained for you.
David Maymudes
A: 

Actualy, David (above comment) put me on the right track. The UIActionSheet was causing my emailString ivar to become invalid, so I moved the call to the UIActionSheet to BEFORE building the emailString. Now all is working!

Alpinista