MFMailComposeViewControllerDelegate not working Swift 3 - ios

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 !

Related

How can I avoid an 1100 error using MFMailComposeViewControllerDelegate?

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?

MFMailComposeViewController won't dismiss

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.

how to dismiss mail view controller after tapping send or cancel button

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)
}

How to send email in app from UI Label (Swift 2 IOS 9)?

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;
}
}

App store Rejected me from the app crashing but it has never crashed for me through numerous testing

I submitted my app to the app store, but they rejected it saying that the app crashed when they clicked the feedback button which is a button that opens up a MFMailComposeViewController. The problem I am having is I have run it on many devices between the simulator and actual devices, yet I have never had this problem. I will post my functions for the feedback button below which I have called and are all connected to the button (Like I said it works completely fine every time I have tested it), and my question is: Am i doing something wrong in the code to where only they get the crash?
func giveFeedback()
{
let email = ["info#website.com"]
var fvc = view?.window?.rootViewController
var cev = MFMailComposeViewController()
cev.mailComposeDelegate = self
cev.setToRecipients(email)
cev.setSubject("MyApp")
fvc?.presentViewController(cev, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!)
{
controller.dismissViewControllerAnimated(true, completion: nil)
}
Also, I have imported MessageUI, and in the class I have my MFMailComposeViewControllerDelegate
One thing is that you don't call canSendMail. I believe that if you try to show the MFMailComposeViewController when mails are disabled, your app would crash.
In your function you would use it for example like this:
func giveFeedback(contextViewController: UIViewController) {
if MFMailComposeViewController.canSendMail() {
let email = ["info#website.com"]
var cev = MFMailComposeViewController()
cev.mailComposeDelegate = self
cev.setToRecipients(email)
cev.setSubject("MyApp")
contextViewController.presentViewController(cev, animated: true, completion: nil)
}
}
But it would be best to check the status earlier and display the button only if email is enabled on the device...
Well I will have to guess too.... There are two things that look suspicious to me... the first thing is the line
var fvc = view?.window?.rootViewController
could you change it this way?
func giveFeedback(contextViewController: UIViewController) {
let email = ["info#website.com"]
var cev = MFMailComposeViewController()
cev.mailComposeDelegate = self
cev.setToRecipients(email)
cev.setSubject("MyApp")
contextViewController.presentViewController(cev, animated: true, completion: nil)
}

Resources