MFMailComposeViewController not sending email or invoking delegate (MFMailComposeViewControllerDelegate) - ios

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

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

MessageComposeViewController only presents once

I'm using Xcode 8 to build an application for iPhone.My simple app has an button. When user tap on this button, messageComposeViewController is called and with phone number and message content filled.The message went through successfully when I clicked on Send button.The problem is MessageComposeViewController is showing only once.After the message sent, when I tapped on the button to call it, a black screen showed up instead of the message composer.My code is attached below. I appreciate any help.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
let msg = MFMessageComposeViewController()
#IBOutlet weak var coordinate_label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func sendMessage(_ sender: AnyObject) {
self.msg.body = "Message Content"
self.msg.recipients = ["xxx-xxx-xxxx"]
self.msg.messageComposeDelegate = self
self.present(msg, animated: false, completion: nil)
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch result.rawValue {
case MessageComposeResult.cancelled.rawValue:
dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
dismiss(animated: true, completion: nil)
default:
break;
}
}
}
Try this code
import Foundation
import MessageUI
let textMessageRecipients = ["1-800-867-5309"] // for pre-populating the recipients list (optional, depending on your needs)
class MessageComposer: NSObject, MFMessageComposeViewControllerDelegate {
// A wrapper function to indicate whether or not a text message can be sent from the user's device
func canSendText() -> Bool {
return MFMessageComposeViewController.canSendText()
}
// Configures and returns a MFMessageComposeViewController instance
func configuredMessageComposeViewController() -> MFMessageComposeViewController {
let messageComposeVC = MFMessageComposeViewController()
messageComposeVC.messageComposeDelegate = self // Make sure to set this property to self, so that the controller can be dismissed!
messageComposeVC.recipients = textMessageRecipients
messageComposeVC.body = "Hey friend - Just sending a text message in-app using Swift!"
return messageComposeVC
}
// MFMessageComposeViewControllerDelegate callback - dismisses the view controller when the user is finished with it
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
}
Note, latest syntax 2017 ..
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
}
In Your viewController ..
import UIKit
class ViewController: UIViewController {
// Create a MessageComposer
let messageComposer = MessageComposer()
#IBAction func sendTextMessageButtonTapped(sender: UIButton) {
// Make sure the device can send text messages
if (messageComposer.canSendText()) {
// Obtain a configured MFMessageComposeViewController
let messageComposeVC = messageComposer.configuredMessageComposeViewController()
// Present the configured MFMessageComposeViewController instance
// Note that the dismissal of the VC will be handled by the messageComposer instance,
// since it implements the appropriate delegate call-back
presentViewController(messageComposeVC, animated: true, completion: nil)
} else {
// Let the user know if his/her device isn't able to send text messages
let errorAlert = UIAlertView(title: "Cannot Send Text Message", message: "Your device is not able to send text messages.", delegate: self, cancelButtonTitle: "OK")
errorAlert.show()
}
}
}

MFMailComposeViewController does not dismiss

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.

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