5

I seek to create a "Utility Email sender class" that I can use in several iPhone projects.

I created MailSender header and implementation for that purpose.

MailSender.h:

@interface MailSender : NSObject<MFMailComposeViewControllerDelegate>

- (id) initWithParent:(UIViewController*) mainController;

- (void) invokeMailSender:(NSString*) to:(NSString*) subject:(NSString*) failureTitle:(NSString*) failureMessage:(NSString*) failureCancel;

@end

MailSender.m:

#import "MailSender.h"

@implementation MailSender

MFMailComposeViewController* mailer;
UIViewController* mailParentController;

- (id) initWithParent:(UIViewController*) mainController
{
    if( self = [super init])
    {
      mailParentController = mainController;
    }
    return self;
}

- (void) invokeMailSender:(NSString*) to:(NSString*) subject:(NSString*) failureTitle:(NSString*) failureMessage:(NSString*) failureCancel;

{
    if([MFMailComposeViewController canSendMail])
    {
        mailer = [[MFMailComposeViewController alloc] init];

        mailer.mailComposeDelegate = self;

        [mailer setSubject:subject];

        NSArray *toRecipients = [NSArray arrayWithObjects:to, nil];

        [mailer setToRecipients:toRecipients];
       [mailParentController presentModalViewController:mailer animated:YES];
    }
    else
    {
        UIAlertView* alert = [[UIAlertView alloc] initWithTitle:failureTitle message:failureMessage
                                                       delegate:nil cancelButtonTitle:failureCancel otherButtonTitles: nil];

        [alert show];
    }
}

-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
    // Do nothing
    [mailParentController dismissModalViewControllerAnimated:YES];
    mailer = nil;
}



@end

I called the class from a View Controller (in a button touch down action) using the following instructions:

@implementation InfoViewController

MailSender *sender;

- (IBAction)openMail:(id)sender
{
    sender = [[MailSender alloc] initWithParent:self];
    [sender invokeMailSender:@"test@test.com" :@"123" :@"123" :@"123" :@"123"];
}

.... 
@end

When I run the code, I am able to show the email views correctly. However, this is then followed by a crash. Note that I do not have a crash when using MFMailComposeViewController directly from my UIViewController (And assigning the View Controller as the delegate),

Any ideas? Sorry I am still a new to Objective C :)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
SiN
  • 3,704
  • 2
  • 31
  • 36
  • 1
    i don't see any problem in this code...should work fine !!! just try clean building/restarting xcode. I think it is crashing from somewhere else. – arun.s Mar 29 '12 at 14:38
  • If I remove: mailer.mailComposeDelegate = self; it doesn't crash anymore! Anything wrong with my delegate? – SiN Mar 29 '12 at 15:12
  • At what time does it crash ? Is it when the `didFinishWithResult` delegate method is called by any chance ? Also could you post the code of your view controller that calls the `initWithParent`method ? – Mutix Mar 29 '12 at 15:41
  • The crash occurs before or during didFinishResult is called. It just never goes inside the didFinishResult method, I put a debug breakpoint in there and it's never hit. – SiN Mar 29 '12 at 16:01

1 Answers1

8

You need to retain your sender MailSender instance. It is being released after you call the invoke message.

You could do this by declaring a property named sender. E.g.

@property (strong, nonatomic) MailSender *sender;
...
@synthesize sender = _sender;
...
self.sender = [[MailSender alloc] initWithParent:self];
[self.sender invokeMailSender:@"noor@dimachk.com" :@"123" :@"123" :@"123" :@"123"];

By the way, your method declaration is a bit funny. You should name the arguments. E.g.

- (void)invokeMailSender:(NSString *)sender 
                      to:(NSString *)to 
                 subject:(NSString *)subject 
            failureTitle:(NSString *)failureTitle 
          failureMessage:(NSString *)failureMessage 
failureCancelButtonTitle:(NSString *)failureCancelButtonTitle
Paul Hunter
  • 4,453
  • 3
  • 25
  • 31
  • Thanks a lot, that work. Thank also for your hints for the method declaration. I have two questions I don't know if you can answer me: 1- How come the ARC was releasing the sender instance knowing that I did not nullify it? 2- With the solution you provided, the property *sender is now accessible by all classes (since it is in the header), any way to make it private (implementation specific and not in the header)? Sorry for any misconception from my side. – SiN Mar 30 '12 at 01:00
  • 1
    It was being released because you did not use it anymore after you called the invoke message. You can make the property private by using a category. See this link http://stackoverflow.com/questions/172598/best-way-to-define-private-methods-for-a-class-in-objective-c – Paul Hunter Mar 30 '12 at 13:31