MFMailComposeViewController does not dismiss - ios

I'm trying to implement MFMailComposeViewController in case of sending the emails from within my application. The problem is that after presenting MFMailComposeViewController it is not dismissing by "Cancel" or "Send" buttons, just a bit scrolls up.
Here is the presenting of it:
func mailButtonDidPressed {
let emailTitle = "Test email"
let messageBody = "some body bla bla bla"
let toRecipents = "email#gmail.com"
let emailComposer = MFMailComposeViewController()
emailComposer.setSubject(emailTitle)
emailComposer.setMessageBody(messageBody, isHTML: false)
emailComposer.setToRecipients([toRecipents])
emailComposer.mailComposeDelegate = self
self.presentViewController(emailComposer, animated: true, completion: nil)
}
and dismissing delegate code:
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
switch (result) {
case MFMailComposeResultSent:
print("You sent the email.")
break
case MFMailComposeResultSaved:
print("You saved a draft of this email")
break
case MFMailComposeResultCancelled:
print("You cancelled sending this email.")
break
case MFMailComposeResultFailed:
print("Mail failed: An error occurred when trying to compose this email")
break
default:
print("An error occurred when trying to compose this email")
break
}
controller.dismissViewControllerAnimated(true, completion: nil)
}
I have surfed through the StackOverflow and other services like this and could not find any answer.

Swift 5
Remember to add both delegates:
emailComposer.mailComposeDelegate = self
emailComposer.delegate = self
If you only add one, it won't dismiss. Also make sure to implement the delegate method:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
dismiss(animated: true)
}

If anybody's having this problem in Swift 3.0, I think there might be two methods for MFMailComposeViewController that look similar to the CORRECT method.
Make sure you are using this method
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}

for swift 3 you have to add
composer.mailComposeDelegate = self as MFMailComposeViewControllerDelegate

I solved writing in this way removing the completion in this way:
extension UIViewController: MFMailComposeViewControllerDelegate {
func sendEmail() {
//send email
}
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}

Assuming mailComposeControllermailComposeControlleris a function of the same class as mailButtonDidPressedmailButtonDidPressed, it should be its instance that dismiss the MFMailComposeViewController, so
self.dismissViewControllerAnimated(true, completion: nil)
instead of
controller.dismissViewControllerAnimated(true, completion: nil)

You didn't add the delegate :
emailComposer.delegate = self
your code should be like this :
func mailButtonDidPressed {
...
let emailComposer = MFMailComposeViewController()
emailComposer.delegate = self
...
}

I had this problem and the answer was extremely simple. Make sure to include
MFMailComposeViewControllerDelegate
in the top as when you are listing your inheritances, and then include the stuff from the other answers.

Related

Why didFinishWith delegate method is called automatically while using MFMailComposeViewController in swift iOS?

I am trying to present MFMailComposeViewController to send email but the problem is that MFMailComposeViewController pop is dismissed automatically as soon as it presents. The didFinishWith delegate method is called automatically just after present the MFMailComposeViewController, I don't know why it's happening.
import UIKit
import Alamofire
import MessageUI
class MyProfileViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let senderMail = ["xyz#gmail.com"]
let mailComposer = MFMailComposeViewController()
guard MFMailComposeViewController.canSendMail() else{
CommonUtils.showToast(message: "Mail services are not available")
return
}
mailComposer.setToRecipients(senderMail)
mailComposer.mailComposeDelegate = self
self.present(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch (result){
case MFMailComposeResult.cancelled:
print("Mail cancelled");
break;
case MFMailComposeResult.saved:
print("Mail saved");
break;
case MFMailComposeResult.sent:
print("Mail sent");
break;
case MFMailComposeResult.failed:
print("Mail sent failure: %#", error?.localizedDescription);
break;
default:
break;
}
// Close the Mail Interface
controller.dismiss(animated: true)
}
It's working in real device but not in simulator

is there any way to create a common method to Email (Like:- Contact us in app)

i am getting an error:- // Cannot assign value of type 'UIViewController' to type 'MFMailComposeViewControllerDelegate?' //
and if i am not assigning the delegate then i cant able to come back after click on CANCEL in message VC.
See this code:-
now i am doing something like this:-
extension UIViewController {
func contactUs() {
if MFMailComposeViewController.canSendMail() {
mc.mailComposeDelegate = self
if let name = CurrentUser.name, let phoneNo = CurrentUser.phone {
mc.setMessageBody("\n\n\nThanks & Regards\n\(name)\n\(phoneNo)", isHTML: false)
}
mc.setSubject(emailTitle)
mc.setToRecipients(toRecipents)
self.present(mc, animated: true, completion: nil)
}
}
}
extension ViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller:MFMailComposeViewController, didFinishWith result:MFMailComposeResult, error:Error?) {
switch result {
case .cancelled:
print_debug("Mail cancelled")
case .saved:
print_debug("Mail saved")
case .sent:
print_debug("Mail sent")
case .failed:
print_debug("Mail sent failure: \(error?.localizedDescription ?? "Mail not sent")")
default:
break
}
self.dismiss(animated: true, completion: nil)
}
}
Hi Vipul you are getting error because in your code you are assigning viewcontroller to self so what you need to do is typecast that controller
mc.mailComposeDelegate = self as? MFMailComposeViewControllerDelegate
this will fix your problem and I will also suggest you to give a default implementation to MFMailComposeViewControllerDelegate like this
import MessageUI
extension MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?){
switch result {
case .cancelled:
print_debug("Mail cancelled")
case .saved:
print_debug("Mail saved")
case .sent:
print_debug("Mail sent")
case .failed:
print_debug("Mail sent failure: \(error?.localizedDescription ?? "Mail not sent")")
default:
break
}
controller.dismiss(animated: true, completion: nil)
}
}
and you don't need to set mc.mailComposeDelegate

MFMailComposeViewController not sending email or invoking delegate (MFMailComposeViewControllerDelegate)

I have a MFMailComposeViewController setup and presented as such:
class MyViewController: UIViewController {
// MARK: - Properties
let composeViewController = MFMailComposeViewController()
// MARK: - Actions
#IBAction func didTapSendInEmailButton() {
composeViewController.mailComposeDelegate = self
composeViewController.setToRecipients([Constants.contactRecipientEmail])
composeViewController.setSubject(Constants.contactSubject)
composeViewController.setMessageBody(Constans.body, isHTML: false)
present(composeViewController, animated: true, completion: nil)
}
}
// MARK: - MFMailComposeViewControllerDelegate
extension MyViewController: MFMailComposeViewControllerDelegate {
private func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
switch result {
case .sent:
print("Email sent")
case .saved:
print("Draft saved")
case .cancelled:
print("Email cancelled")
case .failed:
print("Email failed")
}
controller.dismiss(animated: true, completion: nil)
}
}
I'm having this issue:
After pressing "Send" on the composer, the MFMailComposeViewControllerDelegate does NOT get invoked.
What may I be missing?
I'm not able to declare mailComposeController as public:
A few minor changes that I will highlight in comments:
import UIKit
import MessageUI
class MyViewController: UIViewController {
// MARK: - Properties
let composeViewController = MFMailComposeViewController()
// MARK: - Actions
#IBAction func didTapSendInEmailButton() {
composeViewController.mailComposeDelegate = self
// Entered a generic email in place of your constant value
composeViewController.setToRecipients(["someone#example.com"])
// Entered a generic subject in place of your constant value
composeViewController.setSubject("subject")
// You have a typo on "Constants" here
composeViewController.setMessageBody("body", isHTML: false)
present(composeViewController, animated: true, completion: nil)
}
}
// MARK: - MFMailComposeViewControllerDelegate
extension MyViewController: MFMailComposeViewControllerDelegate {
// Removed the private
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
switch result {
case .sent:
print("Email sent")
case .saved:
print("Draft saved")
case .cancelled:
print("Email cancelled")
case .failed:
print("Email failed")
}
controller.dismiss(animated: true, completion: nil)
}
}
And my console log shows email sent.
Have you confirmed that your Constants object contains valid data? Perhaps you should print it out or view it at a breakpoint when it arrives here to ensure there are no issues with its contents.
Also, make sure you have a valid email account set up on your device in the first place to send the email for you. If it is a development device, then it may have been reset and lost at some point.
Comparing the Error type in yours:
To mine:
Notice that the color of Error changes. This means it is using an Error that you have defined, which is scoped to private. You need to break this by renaming your Error to something else. Once that is done, it should resolve your error.
extension MyViewController: MFMailComposeViewControllerDelegate {
private func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?)
Check your delegate method again, it should be public.
Also make sure your mail account is configured properly.
I don't know where is your problem
i test Your code it works nice , compiler not in-force me to set delegate as private , Note Email composer not work on simulator just on device
import UIKit
import MessageUI
class MyViewController: UIViewController {
// MARK: - Properties
let composeViewController = MFMailComposeViewController()
// MARK: - Actions
#IBAction func didTapSendInEmailButton() {
if MFMailComposeViewController.canSendMail() {
composeViewController.mailComposeDelegate = self
composeViewController.setToRecipients(["abdela7ad#gmail.com"])
composeViewController.setSubject("Constants.contactSubjec")
composeViewController.setMessageBody("Constans.body", isHTML: false)
present(composeViewController, animated: true, completion: nil) } else {
}
}
}
// MARK: - MFMailComposeViewControllerDelegate
extension MyViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .sent:
print("Email sent")
case .saved:
print("Draft saved")
case .cancelled:
print("Email cancelled")
case .failed:
print("Email failed")
}
controller.dismiss(animated: true, completion: nil)
}
}
I have also face similar issue recently, My work around is i moved the declaration inside button action
class MyViewController: UIViewController {
// MARK: - Actions
#IBAction func didTapSendInEmailButton() {
let composeViewController = MFMailComposeViewController()
composeViewController.mailComposeDelegate = self
composeViewController.setToRecipients([Constants.contactRecipientEmail])
composeViewController.setSubject(Constants.contactSubject)
composeViewController.setMessageBody(Constans.body, isHTML: false)
present(composeViewController, animated: true, completion: nil)
}
}
// MARK: - MFMailComposeViewControllerDelegate
extension MyViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
switch result {
case .sent:
print("Email sent")
case .saved:
print("Draft saved")
case .cancelled:
print("Email cancelled")
case .failed:
print("Email failed")
}
controller.dismiss(animated: true, completion: nil)
}
}
Also you don't need to make delegate method private

Unable to dismiss MFMailComposeViewController from a separate ViewController

I have a DataHandler object that shows a MFMailComposeViewController onto different ViewControllers when instantiated.
For example, in a class, I have an IBAction that emails data and calls DataHandler in order to do so:
#IBAction func exportData(_ sender: Any) {
let dh = DataHandler()
dh.sendEmail(vc: self)
}
DataHandler then does this:
func configureEmail() -> MFMailComposeViewController{
let mailComposerVC = MFMailComposeViewController()
let dataHandler = DataHandler()
let data = dataHandler.getData(fileName: file!)
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients([emailAddr!])
mailComposerVC.setSubject("blah!")
mailComposerVC.addAttachmentData(data as Data, mimeType: "text/csv", fileName: "file.csv")
return mailComposerVC
}
func sendEmail(vc: UIViewController){
if MFMailComposeViewController.canSendMail() {
let emailClient = configureEmail()
vc.show(emailClient, sender: vc)
}
else{
print("Unable to send emails")
}
}
I'm able to send emails, however, am unable to dismiss the view controller after it's sent. I've already inserted this in DataHandler
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
I have scoured the internet for solutions and am at my wits' end. Please help! :)
Edit 1:
Changed controller.dismiss(animated: true, completion: nil) to self.dismiss(animated: true, completion: nil)
You need to set the delegate:
mailComposerVC.mailComposertDelegate = self
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
was defined outside of the scope of the IBAction. So the DataHandler object that was needed to dismiss the MFMailComposeViewController was not in the same scope of the dismiss function. The solution was to declare the DataHandler object as a variable of the class that was calling the DataHandler functions.

how to send a mail from my iOS application- SWIFT

I want to send a mail from my application. I am doing my first steps with SWIFT and i have stuck at a point. I want to press a button and open up the mail. Can you please tell me how to do the button connection? I think it should be an action but I don't know where to put it on the code
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
func sendEmail() {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self
mailVC.setToRecipients([])
mailVC.setSubject("Subject for email")
mailVC.setMessageBody("Email message string", isHTML: false)
presentViewController(mailVC, animated: true, completion: nil)
}
// MARK: - Email Delegate
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Import library first:
import MessageUI
set delegate like:
MFMailComposeViewControllerDelegate
Write pretty code:
#IBAction func buttonHandlerSendEmail(_ sender: Any) {
let mailComposeViewController = configureMailComposer()
if MFMailComposeViewController.canSendMail(){
self.present(mailComposeViewController, animated: true, completion: nil)
}else{
print("Can't send email")
}
}
func configureMailComposer() -> MFMailComposeViewController{
let mailComposeVC = MFMailComposeViewController()
mailComposeVC.mailComposeDelegate = self
mailComposeVC.setToRecipients([self.textFieldTo.text!])
mailComposeVC.setSubject(self.textFieldSubject.text!)
mailComposeVC.setMessageBody(self.textViewBody.text!, isHTML: false)
return mailComposeVC
}
Also write delegate method like:
//MARK: - MFMail compose method
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
100% working and tested
change sendEmail like this:
#IBAction func sendEmail(sender: AnyObject) {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = self
mailVC.setToRecipients([])
mailVC.setSubject("Subject for email")
mailVC.setMessageBody("Email message string", isHTML: false)
presentViewController(mailVC, animated: true, completion: nil)
}
and connect in Interface builder your button to this action
Swift 3
let composer = MFMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
composer.mailComposeDelegate = self
composer.setToRecipients(["Email1", "Email2"])
composer.setSubject("Test Mail")
composer.setMessageBody("Text Body", isHTML: false)
present(composer, animated: true, completion: nil)
}
Delegate method
class SendMailViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
dismiss(animated: true, completion: nil)
}
}
To send mail, generally MFMailComposer is used. It can be tested on device as it doesn't work on iOS simulator.
For testing whether mail service is available or not, use below function,
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
and to send mail, use below code in your function or action of button.
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients(["email_address#example.com"])
composeVC.setSubject("Hello World!")
composeVC.setMessageBody("Hello from iOS!", isHTML: false)
// Present the view controller modally.
self.presentViewController(composeVC, animated: true, completion: nil)
There is delegate method on completion of sending mail which can be defined as below shown,
func mailComposeController(controller: MFMailComposeViewController,
didFinishWithResult result: MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismissViewControllerAnimated(true, completion: nil)
}
to Anton Platonov add: import MessageUI at the begging of your source file, and when you declaring class for your view controller add protocol: class FirstVC: UIViewController, MFMailComposeViewControllerDelegate{

Resources