24

Is it possible to somehow lock the fields in an MFMailComposeViewController so that the body, recipients etc cannot be changed by the user? I need the e-mail the user sends to go to a particular account and the body to meet certain criteria so if the user drastically edits the format everything could go horribly wrong..At the moment the body is filled in from data that the user inputs to textfields and date-pickers in the previous view.

Basically I think it would just be more professional to have the fields locked rather than display an alert or something saying "Please Don't Edit the Message", so its not a massive problem if the fields can't be locked, but any help would be greatly appreciated.

Bhavin Ramani
  • 3,133
  • 5
  • 29
  • 41
Jack Nutkins
  • 1,545
  • 5
  • 33
  • 70
  • Why don't you send the email in the background? And show an alert when its done saying "email has been sent" i have some code i can share if you are interested. – Louie Jun 08 '11 at 23:23
  • @Louie , if you could that would be fantastic, however I heard that Apple aren't happy with background e-mailing? Thanks, Jack – Jack Nutkins Jun 09 '11 at 01:05
  • i know of several apps that are emailing in background - i just submitted mine this past week it should be going to "in review" within the next day or two so Ill let you know how it goes. in the mean time Ill format an answer so it can easily be seen by others. – Louie Jun 09 '11 at 01:36

1 Answers1

43

Download the framework from the link below. Then I have put together some code that sends the email with a nice "please wait" overlay. I have attached an image of what this looks like while its running (for the few seconds it takes). Please note, I take no credit for creating the SMTP framework. It was downloaded from the internet after searching for it forever. The zip file that you can download includes the overlay images that I created for user feedback. It has both @2x and regular. You will have to go into interface builder and create the label though that says "sending test drive..". Its already in the code but I didnt add it from code. So youll have to add it in IB.

1. Make sure to add the framework you downloaded to your project.

2. Make sure to add the CFNetwork framework to your project

3. Make sure to attach the UILabel name "loadingLabel" in interface builder

4. The username and password that the code is refering to is an smtp server. If you dont have one create a gmail account and use gmail settings. If you are not familiar with gmail settings google "gmail smtp" you will find what you need.

Find Framework & Art here

For your .h file make sure to include:

//for sending email alert
UIActivityIndicatorView * spinner;
UIImageView * bgimage;
IBOutlet UILabel * loadingLabel;

}
@property (nonatomic, retain)IBOutlet UILabel * loadingLabel;
@property (nonatomic, retain)UIImageView * bgimage;
@property (nonatomic, retain)UIActivityIndicatorView * spinner;
-(void)sendEmail;
-(void)removeWaitOverlay;
-(void)createWaitOverlay;
-(void)stopSpinner;
-(void)startSpinner;

For your .m file include:

@synthesize bgimage,spinner,loadingLabel;

// add this in ViewDidLoad
//set loading label to alpha 0 so its not displayed
loadingLabel.alpha = 0;

everything else is its own function

-(void)sendEmail {


    // create soft wait overlay so the user knows whats going on in the background.
    [self createWaitOverlay];

    //the guts of the message.
    SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
    testMsg.fromEmail = @"youremail@email.com";
    testMsg.toEmail = @"targetemailaddress@email.com";
    testMsg.relayHost = @"smtpout.yourserver.net";
    testMsg.requiresAuth = YES;
    testMsg.login = @"yourusername@email.com";
    testMsg.pass = @"yourPassWord";
    testMsg.subject = @"This is the email subject line";
    testMsg.wantsSecure = YES; // smtp.gmail.com doesn't work without TLS!



    // Only do this for self-signed certs!
    // testMsg.validateSSLChain = NO;
    testMsg.delegate = self;

    //email contents
    NSString * bodyMessage = [NSString stringWithFormat:@"This is the body of the email. You can put anything in here that you want."];


    NSDictionary *plainPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/plain",kSKPSMTPPartContentTypeKey,
                               bodyMessage ,kSKPSMTPPartMessageKey,@"8bit",kSKPSMTPPartContentTransferEncodingKey,nil];

    testMsg.parts = [NSArray arrayWithObjects:plainPart,nil];

    [testMsg send];

}


- (void)messageSent:(SKPSMTPMessage *)message
    {
    [message release];

    //message has been successfully sent . you can notify the user of that and remove the wait overlay
    [self removeWaitOverlay];



    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Message Sent" message:@"Thanks, we have sent your message"
                                                   delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
    [alert release];
}

- (void)messageFailed:(SKPSMTPMessage *)message error:(NSError *)error
{
    [message release];
    [self removeWaitOverlay];

    NSLog(@"delegate - error(%d): %@", [error code], [error localizedDescription]);

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Email Error" message:@"Sending Failed - Unknown Error :-("
                                                   delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
    [alert release];
}



-(void)createWaitOverlay {

    // fade the overlay in
    loadingLabel = @"Sending Test Drive...";
    bgimage = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,480)];
    bgimage.image = [UIImage imageNamed:@"waitOverLay.png"];
    [self.view addSubview:bgimage];
    bgimage.alpha = 0;
    [bgimage addSubview:loadingLabel];
    loadingLabel.alpha = 0;


    [UIView beginAnimations: @"Fade In" context:nil];
    [UIView setAnimationDelay:0];
    [UIView setAnimationDuration:.5];
    bgimage.alpha = 1;
    loadingLabel.alpha = 1;
    [UIView commitAnimations];
    [self startSpinner];

    [bgimage release];

}

-(void)removeWaitOverlay {

    //fade the overlay out

    [UIView beginAnimations: @"Fade Out" context:nil];
    [UIView setAnimationDelay:0];
    [UIView setAnimationDuration:.5];
    bgimage.alpha = 0;
    loadingLabel.alpha = 0;
    [UIView commitAnimations];
    [self stopSpinner];


}

-(void)startSpinner {

    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    spinner.hidden = FALSE;
    spinner.frame = CGRectMake(137, 160, 50, 50);
    [spinner setHidesWhenStopped:YES];
    [self.view addSubview:spinner];
    [self.view bringSubviewToFront:spinner];
    [spinner startAnimating];
}

-(void)stopSpinner {

    [spinner stopAnimating];
    [spinner removeFromSuperview];
    [spinner release];

}

The final results are shown below. The screen appears to dim a bit (kind of like when an UIAlert is shown). It shows a message saying its being sent, and then "brightens" back up when the message is sent.

Happy Coding!!

enter image description here

Louie
  • 5,868
  • 4
  • 28
  • 44
  • @Louie , how is you add this framework to the project? I've tried a few things and its not recognising it at all. Thanks, Jack – Jack Nutkins Jun 09 '11 at 13:18
  • i just dragged and dropped the folders into the project – Louie Jun 09 '11 at 14:36
  • make sure to "copy" them there as well. – Louie Jun 09 '11 at 14:36
  • @Louie Ok, I've imported all the header files into the right class but I'm getting the following error: +[NSStream(SKPSMTPExtensions) getStreamsToHostNamed:port:inputStream:outputStream:] in NSStream+SKPSMTPExtensions.o Any idea whats causing this? – Jack Nutkins Jun 09 '11 at 14:43
  • I believe all the testmsg attributes to have correct values as well – Jack Nutkins Jun 09 '11 at 14:56
  • hmm... I have no idea.. I didnt see that msg. When I get back home today Ill take another look at my code to make sure I sent everything over. – Louie Jun 09 '11 at 15:00
  • I fixed the error, I needed to include the CFNetwork.framework :) Thanks for all your help @Louie. You should add that to your answer. – Jack Nutkins Jun 09 '11 at 20:36
  • 7
    Oh yes! sorry! I forgot to tell you that! Good news is I received my response back from apple today, they allowed this in the app. My app has been approved. Just thought you would want to know its good to go!! – Louie Jun 09 '11 at 20:46
  • can i test if my mail is going or not from iphone simulator.xcode4 and simulator 4.3? – Mann Aug 08 '11 at 20:40
  • @Manjinder - Yes, your iphone simulator should be sending the mail. I am not 100% familiar with what the Gmail SMTP relay settings are as I have my own server doing it for me. I just know that they allow it, I think... – Louie Aug 08 '11 at 21:25
  • 3
    Here are the Gmail settings I found online Gmail SMTP server address: smtp.gmail.com Gmail SMTP user name: Your full Gmail address (e.g. me@gmail.com) Gmail SMTP password: Your Gmail password Gmail SMTP port: 465 Gmail SMTP TLS/SSL required: yes – Louie Aug 08 '11 at 21:27
  • Yes that is ok but can you explian all fields which i specified ? i am having confusion which emails they are reffering to? server email or email id from which email is sent? and for relayhost i have to specify server name? like "smtpout.servername@gmail.com"? – Mann Aug 08 '11 at 21:28
  • in the answer above in relayHost = @"smtpout.yourserver.net" i have to put "smtp.gamail.com" or my gmail account name which i have made gmail server? what u put in yours? you have described really good answer. one thing more. Can i use it in commercial application? apple will accept it? – Mann Aug 08 '11 at 21:34
  • 1
    Apple has accepted 6 of my apps that use this code. It shouldnt be a problem. Your setup looks correct. Just send a test message see if it works? – Louie Aug 08 '11 at 22:00
  • How can i make it possible to detect whether a client is already logged in with his email provider, so that i don't have to hard-code the smtp server, user email and his password, i want to make code dynamic because different users will be using different servers to mail. Kindly help me with this. – Jamal Zafar Apr 02 '12 at 07:25
  • @ Louie: Good effort. Its working properly when I change the `relayPort` number. – bharath May 24 '12 at 16:15
  • Is it possible to attach a mp4 video to the email? Thank you. Code works great otherwise! – Szwedo Jul 24 '12 at 19:03
  • @Louie thanks for the guidence but i have a query i want to send the data in a table format which i think i have to pass the html as a string but i am fail to create a string with html is you have a idea please tell me how can i achive this.. – Ravi Sharma Aug 15 '12 at 07:07
  • @Louie How can I send the Image as an attachment to the email? Your help is greatly appreciated. – hp iOS Coder Dec 28 '12 at 11:08
  • Our project uses arc so I had to change SKPSMTPMessage.h delegate to retain, otherwise it would crash. – Daniel Ryan Jan 07 '13 at 23:21
  • @Zammbi Can you please help me on this it would really help. I liked this project a lot & if it facilitates me to send attachment then I am planning to use in my iOS app currently on store. – hp iOS Coder Jan 08 '13 at 13:57
  • @Zammbi Hey I tried it. It works perfectly fine on Simulator but on iOS device it fails to send email with error message **delegate - error(-2): unsupported login mechanism** – hp iOS Coder Jan 09 '13 at 09:17
  • @hp iOS Coder sounds odd, not sure what the problem would be. We are using a gmail account(with custom domain name) and it's working fine for us on the device. So try a gmail account and see if the problem is still there. – Daniel Ryan Jan 10 '13 at 01:36
  • @Louie Can't we use already signed in gmail account for this rarther than typing username and password? – iDia Jan 22 '13 at 04:13
  • @Louie:I want to attach a .csv file with email. So can you show how to attach file using this code – Kirit Vaghela May 03 '13 at 11:47
  • 3
    Hello, @Louie, Please tell me how to send multiple mail I mean in group with your code, there is in your code like testMsg.toEmail but there is no CC. and BCC Please tell how is this possible Thanks in Advance – Bhavesh Lathigara Nov 27 '13 at 08:31
  • can you please suggest me what is problem , i use above code and run it is give error as : delegate - error(-5): timeout sending message i am using microsoft domain @Louie – Anjaneyulu Mar 26 '14 at 06:14
  • 2
    @Louie- The link to your code is throws a 404 error. Are you not offering it anymore? – Marsman Jul 24 '16 at 23:16
  • @Marsman I have fixed the link. Sorry I was gone for so long :) – Louie Nov 02 '17 at 20:08