1

Iam presenting MFMailComposeViewController from my custom class(not a viewController). In iOS5 it is working fine but in iOS6 it is getting crash immediately after presenting the compose sheet. I found the issue the dealloc method is getting called after presenting the view, so self is deallocating. Due to this mailcomposer cannot call the delegate method on self so it is crashing. I didnt get a solution for that. Am using ARC. How to prevent self from deallocating until the delegate method is getting called?

  -(void)shareOnViewController:(UIViewController *)viewController  completion:(ShareCompletionHandler)completion
{

  if ([MFMailComposeViewController canSendMail]) {

    MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
    mailer.mailComposeDelegate = self;
    mailer.modalPresentationStyle = UIModalPresentationPageSheet;
    [mailer setSubject:[self.userInfo objectForKey:@"title"]];

    NSData *imageData = UIImagePNGRepresentation([self.userInfo objectForKey:@"image"]);
    if (imageData) {
        [mailer addAttachmentData:imageData mimeType:@"image/png" fileName:@"AttachedImage"];
    }


    NSURL *emailBody = [self.userInfo objectForKey:@"url"];
    if (![emailBody isEqual:@""]) {
        [mailer setMessageBody:[emailBody absoluteString] isHTML:NO];
    }

    [viewController presentModalViewController:mailer animated:YES];

   } else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unable to send mail"
                                                    message:@"Device is not configured to send mail"
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
   }

self.completionHandler = completion;

}
Anil Varghese
  • 42,757
  • 9
  • 93
  • 110
  • please be sure some other class keeps your `self` (or custom class) alive, until the `MFMailComposeViewController` is dismissed properly. who owns your `self` (or custom class)? – holex Jul 08 '14 at 11:59
  • @Anil maybe you have found a solution? Same issue here... – Sasha Grievus Nov 10 '14 at 22:19
  • 1
    @ElisabettaFalivene Inorder to prevent the delegate object from deallocating someone has to hold it strongly. I have created a property on a controller which is likely to be live always to hold the delegate object. To be clear if Assume `A`is controller `B` is a view which is presenting the `Mail composer` and delegate to . I have keep a reference of `B` in the controller `A` – Anil Varghese Nov 11 '14 at 04:56

3 Answers3

1

According to me , The presentModalViewController method is deprecated in iOS 6.0 .

Instead you need to use

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion 

Else can you show the crash log ??

Mansi Panchal
  • 2,357
  • 18
  • 27
  • I tried both.. same issue... crash log method sent to deallocated instance – Anil Varghese Jun 12 '13 at 05:14
  • 1
    Crash log says you are trying to call a method that is not implemented.With that method change i was able to present the mail controller in iOS 6.1 successfully.May be your completion handler is not properly called.Check that proper. – Mansi Panchal Jun 12 '13 at 05:22
  • iam not calling any method, mailcomposeViewController calling its delegate method that time delegate(self) is deallocated. __[RSEmailService respondsToSelector:]: message sent to deallocated instance 0x9c91600__ – Anil Varghese Jun 12 '13 at 06:17
  • What you are trying to do with 'self.completionHandler = completion;' – Mansi Panchal Jun 12 '13 at 06:19
  • Later i need that completion block to use with the delegate method.. The problem is not with the completion block – Anil Varghese Jun 12 '13 at 06:27
  • And so is not with the mail composer too. RSEmailService is calling a method and that is not been implemented. – Mansi Panchal Jun 12 '13 at 06:29
0
  if ([MFMailComposeViewController canSendMail]) {
        MFMailComposeViewController *mailComposer = [[MFMailComposeViewController alloc]
                                                     init];

    NSData *imageData = UIImagePNGRepresentation(image);
    mailComposer.mailComposeDelegate = self;
    [mailComposer setSubject:subject];
    NSArray * recipents = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%@",NSLocalizedString(@"client_email", @"")],nil];

    [mailComposer setToRecipients:recipents];

    [mailComposer addAttachmentData:imageData mimeType:@"image/png" fileName:[NSString stringWithFormat:@"imageProfile-%@-%@",someId,lastname]];



    mailComposer.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

    [vc presentViewController:mailComposer animated:YES completion:nil];
}
else
{
    UIAlertView *tmp = [[UIAlertView alloc]
                        initWithTitle:@"Email Account Not Found"
                        message:@"You need to setup an Email Account!"
                        delegate:self
                        cancelButtonTitle:nil
                        otherButtonTitles:@"Ok", nil];

    [tmp show];
}
DrDev
  • 452
  • 2
  • 7
0

One of the possible root cause is ARC.

 MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];

mailer will be auto-released as soon as the method body is fully executed.

You could create a reference to hold the object to avoid ARC releasing it before it is ready.

@property (nonatomic, strong) MFMailComposeViewController * composer;
....


self.composer = [[MFMailComposeViewController alloc] init];
...
[viewController presentViewController:self.composer animated:YES];

[Edited suggestion]

Sorry, I miss out the first part of your question where you were calling the method from another custom class.

I came across the similar situation also. In the end, I converted the "Custom Class" into a Singleton class.

#pragma mark Singleton Methods

+ (id)sharedInstance {
    static CustomClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

This will make sure the class will be retained nicely. But this will depend on the usage and design of the CustomClass. Just to share what I did.

[[CustomClass sharedInstance] shareOnViewController:vc completion:...];
JapCon
  • 418
  • 5
  • 10
  • I think the OP's problem not about the `mailer` object, it is about the `self` which is a delegate of the `MFMailComposeViewController` object. on the other hand the `mailer` won't be released until the scope runs out, so that is safe enough. – holex Jul 08 '14 at 12:01
  • @holex: mailer won't be released if the project is NOT using ARC. However, under ARC setting, variable that created within the function scope will be auto-released right after the function block completed (unless it is being retained by a reference). – JapCon Jul 16 '14 at 12:01
  • in `ARC` it will be released if nothing keeps the object alive after the scope runs out. if you add that _local_ mailer controller to another controller inside the scope that will keep it alive, even is you don't case about it anymore. therefore you don't need to keep it alive, not even via a public property. I'm talking about its `delegate` is released too soon, thus the mailer cannot call back any object. – holex Jul 16 '14 at 12:47
  • @holex thanks, I got what you mean. I didnt realize his issue was calling the method from another custom class. – JapCon Jul 22 '14 at 08:35