I have the following code in my app to send an e-Mail:
let ToRecipents = ["recipient here"]
let subject = "subject here"
let MessageBody = "message here"
let mc : MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(ToRecipents)
mc.setSubject(subject)
mc.setMessageBody(MessageBody, isHTML: false)
self.present(mc, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
controller.dismiss(animated: true, completion: nil)
}
I can't seem to get MFMailComposeViewController to dismiss, what am I doing wrong??
In a general way when delegate methods are not called:
• Check if the delegate is set. You do it correctly with mc.mailComposeDelegate = self
• Check if the delegate method you set is compliant with the protocol.
• Check if the delegate method is correctly implemented (that's where lies your issue) in that object you passed as delegate. Read the doc, or remove the method declaration and let the compiler/IDE/XCode autocomplete it for you. In Swift between various tutorials they are often issues because Swift 1, Swift 2, Swift 3/4 have renamed the methods and causing issues, tutorial being focused on a Swift version only.
Your method:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?)
The method from the documentation:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
Since they are not the same, and the iOS SDK should check the if the delegate responds to the correct method, then yours shouldn't be called because it doesn't match.
Related
I'm trying to send an email from with my Swift app. Nothing fancy. Subject. Body. Recipients. I'm doing this like the examples I've seen which is to say NOT using a separate view controller - just a basic vc but specifying MFMailComposeViewControllerDelegate like so:
class MyVC: UIViewController, MFMailComposeViewControllerDelegate
I then have the two methods I see in all the examples:
#IBAction func sendEmail()
{
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setSubject(subject)
mail.setToRecipients(recipients)
mail.setMessageBody(content, isHTML: true)
present(mail, animated: true)
} else {
// show failure alert
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
{
controller.dismiss(animated: true)
}
But when the email gets cancelled (Delete Draft) I get:
[AXRuntimeCommon] AX Lookup problem - errorCode:1100 error:Permission denied portName:'com.apple.iphone.axserver'
No crashing. Emails get sent just fine. But I noticed that the cancelled emails pile up in Mail. The drafts are not being deleted. What am I missing? Does anyone have this kind of thing working?
I have been using the mail composer in a few of my apps for awhile now and as of recent the mailComposeDelegate no longer gets call.I wasn't sure if this was something to do with the new release of Swift. So, I thought I would ask and see if anyone else is having similar issues.I can present the mail composer but it never gets dismissed due to the delegate not working.
Below is an exact copy of the code I have been using:
func launchFeedback() {
guard MFMailComposeViewController.canSendMail() else {
return
}
let emailTitle = "Feedback"
let messageBody = ""
let toRecipents = ["johnappleseed#icloud.com"]
mailComposer.mailComposeDelegate = self
mailComposer.setSubject(emailTitle)
mailComposer.setMessageBody(messageBody, isHTML: false)
mailComposer.setToRecipients(toRecipents)
self.show(mailComposer, sender: self)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
print(error)
controller.dismiss(animated: true, completion: nil)
}
This is clearly an Xcode bug. The only way to get around this (after searching though StackOverflow life for an hour) was this:
#objc(mailComposeController:didFinishWithResult:error:)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult,error: NSError?) {
controller.dismiss(animated: true)
}
See the #objc macro before the method implementation. Also note that the last parameter has to be NSError type instead of Error as suggested by Apple documentation (and autocompleted by Xcode)
Swift 3 no longer has unnamed first parameters by default (see this proposal), so you'll need to add an underscore to your function:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
print(error)
controller.dismiss(animated: true, completion: nil)
}
Swift 4, Xcode 9.1.
My issue was that MFMailComposeViewController was working fine but if you click cancel, dismiss, and then trying to open it one more time both cancel and send button will not fire didFinishWith delegate function.
It was happening because I've declared MFMailComposeViewController as lazy variable and solution was to create new instance of MFMailComposeViewController every time you want to open it.
Problem:
lazy var mailComposeViewController: MFMailComposeViewController = {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.mailComposeDelegate = self
mailComposeViewController.setToRecipients(["example#test.test"])
mailComposeViewController.setSubject("subject")
mailComposeViewController.setMessageBody("test body", isHTML: false)
return mailComposeViewController
}()
Solution:
func createMailComposeViewController() -> MFMailComposeViewController {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.mailComposeDelegate = self
mailComposeViewController.setToRecipients(["example#test.test"])
mailComposeViewController.setSubject("subject")
mailComposeViewController.setMessageBody("test body", isHTML: false)
return mailComposeViewController
}
After wasting 2 good hours, i came to the conclusion that as of Xcode 8.3. MFMailComposeViewController does not work on a mixed swift/objc code base. It pops odd compile errors, which first i thought were due to my stupidity, but no.
This is so frustrating apple. Most of us old timers do have tons of code on obj-c, so a pure swift scenario is close to impossible. So as i move classes to swift i have to deal with extra pain as well.
Adding
#import <MessageUI/MessageUI.h>
to the AppName-Bridging-Header.h did the job !
I converted my app to swift 3.0 and have a problem with MailComposeController. When I call function:
`func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?){
controller.dismiss(animated: true, completion: nil)
}`
first of all I have an error with info:
what is strange for my, becouse I copy and paste this method from MFMailComposeViewControllerDelegate. When I change Error to NSError it works, but I receive a warning with info, this method needs to be private to avoid this warning.
When I'm in mailComposer and see email and try to tap Cancel this controller doesn't disapear. Any solution how to dismiss this controller?
I had the same problem because I was importing RealmSwift, which has its own Error struct.
Fix it by specifying which Error type you want. In this case, Swift.Error.
Full Code:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Swift.Error?){
controller.dismiss(animated: true, completion: nil)
}
while sending mail, after tapping send or cancel button view controller stays there and app stalls.
//swift 2.2 ; xcode 7.3.1 ;
if( MFMailComposeViewController.canSendMail() ) {
print("Can send email.")
}
var subjectText = "Verification"
var toReceipients = ["notorious.roman#gmail.com"]
// var msgBody = "Verified"
var mc:MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(subjectText)
mc.setMessageBody("Verified", isHTML: false)
mc.setToRecipients(toReceipients)
self.presentViewController(mc, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
Swift 4.0 Update.
Swift 5.0 Update.
Allow me to add something to the discussion...
In Swift 4 and 5 the delegate method slightly changed; As it's posted by you now, won't do any effect and won't get called. It happened to me, drove me crazy!
The Xcode warning suggest three fixes but first two could be misleading. It's just a tiny fix...
Here's the delegate method fixed for Swift 3, 4 and 5:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
Still, Victor's answer should be the correct/accepted one.
Enjoy!
I think #rmaddy answer your question in his comment, nevertheless I going to explain you what's happening. You're trying to dismiss the UIViewController that presents the MFMailComposeViewController not the MFMailComposeViewController.
As Apple specify in his documentation:
The mail compose view controller is not dismissed automatically. When the user taps the buttons to send the email or cancel the interface, the mail compose view controller calls the mailComposeController:didFinishWithResult:error: method of its delegate. Your implementation of that method must dismiss the view controller explicitly.
So you need to set the method in this way:
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
// Dismiss the mail compose view controller.
controller.dismissViewControllerAnimated(true, completion: nil)
}
I hope this help you.
is had an Switch Statement that controls it for me:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue :
print("Cancelled")
case MFMailComposeResult.failed.rawValue :
print("Failed")
case MFMailComposeResult.saved.rawValue :
print("Saved")
case MFMailComposeResult.sent.rawValue :
print("Sent")
default: break
}
self.dismiss(animated: true, completion: nil)
}
With manipulation of this tutorial http://www.raywenderlich.com/113772/uisearchcontroller-tutorial
I have a table view displaying people, when the cell is clicked the user is redirected to another view showing their picture and their email. I want to be able to have the user click on the email address and email them. I have researched and found similar tutorials
https://www.andrewcbancroft.com/2014/08/25/send-email-in-app-using-mfmailcomposeviewcontroller-with-swift/
The problem with the tutorial above is when test running the new code the ios simulator pops up an error and wont show the composed email (Maybe a Glitch?)and If the simulator did not give an error I dont know how to display multiple emails based on which person the user selected. Any help on solutions to this problem or any alternative will be great Thanks!!
The simulator will crash when you try to open the mail. Try it on an actual device instead.
To compose a mail do the following
Add these to your class
MFMessageComposeViewControllerDelegate and MFMailComposeViewControllerDelegate
In your didSelectRowAtIndexPath, this is the function called when you presses a row in your tableView. Do the following:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let candy = candies[indexPath.row]
var mail: MFMailComposeViewController!
// yourArray is the array that you use to populate the tableView
// .mail is the variable in the object (I´m assuming you´re using objects in your array)
let toRecipients = [candy[indexPath.row].email]
let subject = "Feedback"
let body = "<br><br><p>I have a \(UIDevice.currentDevice().modelName).<br> And iOS version \(UIDevice.currentDevice().systemVersion).<br</p>"
mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(toRecipients)
mail.setSubject(subject)
mail.setMessageBody(body, isHTML: true)
presentViewController(mail, animated: true, completion: nil)
}
The delegate methods below if you need to use them
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
dismissViewControllerAnimated(true, completion: nil)
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}