Error trying to send an email using messageui library - swift

Using the code below found on another stack overflow page I keep getting this error when a button is pressed to form an email within my app I am currently working on. Can someone help?
error: [PPT] Error creating the CFMessagePort needed to communicate with PPT.
import Foundation
import MessageUI
class EmailHelper: NSObject, MFMailComposeViewControllerDelegate {
public static let shared = EmailHelper()
private override init() {
//
}
func sendEmail(subject:String, body:String, to:String){
if !MFMailComposeViewController.canSendMail() {
// Utilities.showErrorBanner(title: "No mail account found", subtitle: "Please setup a mail account")
return //EXIT
}
let picker = MFMailComposeViewController()
picker.setSubject(subject)
picker.setMessageBody(body, isHTML: true)
picker.setToRecipients([to])
picker.mailComposeDelegate = self
EmailHelper.getRootViewController()?.present(picker, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
EmailHelper.getRootViewController()?.dismiss(animated: true, completion: nil)
}
static func getRootViewController() -> UIViewController? {
(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController
// OR If you use SwiftUI 2.0 based WindowGroup try this one
// UIApplication.shared.windows.first?.rootViewController
}
}
Button(action: {
EmailHelper.shared.sendEmail(subject: "Anything...", body: "", to: "")
}) {
Text("Send Email")
}

Related

How to dismiss MFMailComposeViewController from class mail?

I have separated the mail functions from my UIviewController and placed them into a class ‘Mail’. Works fine, but now I do have trouble to dismiss my ‘MFMailComposeViewController’. The delegate ‘mailComposeController’ is not called, Any ideas how to fix?
import Foundation
import MessageUI
class Mail: UIViewController, MFMailComposeViewControllerDelegate{
static func createMail2(
fromViewController:UIViewController,
recipients :String,
messageTitle:String,
messageText :String,
attachment :AnyObject?)
{
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
//mail.mailComposeDelegate = self //Cannot assign value of type 'Mail.Type' to type 'MFMailComposeViewControllerDelegate?'
mail.mailComposeDelegate? = fromViewController as! MFMailComposeViewControllerDelegate //added ? (optional)
mail.setToRecipients([recipients]) //(["mail#to.me"])
mail.setSubject(messageTitle) //("your subject text")
mail.setMessageBody(messageText, isHTML: false)
//ggf. Attachment beifügen>>>
if attachment != nil {
//attachment vorhanden, also anhängen
let attachName = "\(messageTitle).pdf"
mail.addAttachmentData(
attachment as! Data,
mimeType: "application/octet-stream", //für binäre Daten, funktioniert immer
fileName: attachName)
}//end if attachment
//<<<ggf. Attachment beifügen
// Present the view controller modally
fromViewController.present(mail, animated: true) //show mail
} else {
// show failure alert
print("Mail services are not available")
let alert = UIAlertController(title: "Mail error", message: "Your device has not been configured to send e-mails", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
fromViewController.present(alert,animated: true, completion: nil)
}//end if else
}//end func createMail
//mail delegate
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
//It’s called when the user dismisses the controller, either by sending the email or canceling the action. Either way, you have to dismiss the controller manually.
//ggf. noch aktionen...
controller.dismiss(animated: true) //remove the mail view
}//end func mailComposeController
}//end class Mail
What you need is to create a static shared instance of your Mail controller and set if as the mailComposeDelegate. You should also create a controller property in your mail controller to keep a reference of the view controller that has invoked the mail composer and declare your createMail as an instance method (not static):
import UIKit
import MessageUI
class MailComposer: NSObject, MFMailComposeViewControllerDelegate {
static let shared = MailComposer()
private var controller: UIViewController?
func compose(controller: UIViewController, recipients: String,
messageTitle: String, messageText: String, fileURL: URL? = nil) { //, completion: #escaping ((MFMailComposeResult, Error?) -> Void)) {
self.controller = controller
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = MailComposer.shared
mail.setToRecipients([recipients])
mail.setSubject(messageTitle)
mail.setMessageBody(messageText, isHTML: false)
if let fileURL = fileURL {
do {
try mail.addAttachmentData(Data(contentsOf: fileURL), mimeType: "application/octet-stream", fileName: fileURL.lastPathComponent)
} catch {
print(error)
}
}
controller.present(mail, animated: true)
} else {
let alertController = UIAlertController(title: "Mail Compose Error",
message: "Your device has not been configured to send e-mails",
preferredStyle: .alert)
alertController.addAction(.init(title: "OK", style: .default) { _ in
alertController.dismiss(animated: true)
})
controller.present(alertController, animated: true)
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
// You should switch the result only after dismissing the controller
controller.dismiss(animated: true) {
let message: String
switch result {
case .sent: message = "Message successfully sent!"
case .saved: message = "Message saved!"
case .cancelled: message = "Message Canceled!"
case .failed: message = "Unknown Error!"
#unknown default: fatalError()
}
let alertController = UIAlertController(title: "Mail Composer",
message: message,
preferredStyle: .alert)
alertController.addAction(.init(title: "OK", style: .default) { _ in
alertController.dismiss(animated: true)
})
self.controller?.present(alertController, animated: true) {
self.controller = nil
}
}
}
}
Usage:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let recipients = "user#email.com"
let messageTitle = "New Message"
let messageText = "Mail Test"
MailComposer.shared.compose(controller: self, recipients: recipients, messageTitle: messageTitle, messageText: messageText)
}
}
The UIViewController that you're passing as the parameter for fromViewController should conform to MFMailComposeViewControllerDelegate protocol and you should be implementing mailComposeController(_: didFinishWith: in the definition of that controller.
class Mail: UIViewController {
static func createMail2(
fromViewController: UIViewController,
recipients :String,
messageTitle:String,
messageText :String,
attachment :AnyObject?) {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
if let fromViewController = fromViewController as? MFMailComposeViewControllerDelegate {
mail.mailComposeDelegate = fromViewController
} else {
print("fromViewController needs to conform to MFMailComposeViewControllerDelegate")
}
//...
}
}
}

MFMailComposerViewController not geting to didFinishWithResult

I have a created a very simple project that uses MFMailComposeViewController that gets into the Mail and the send button does the send successfully, but I am not getting to the function didFinishWithResult
I have tried with and without a UINavigationController
I have tried self.dismiss as well as controller.dismiss
putting a break point inside the didFinishWithResult. I never get there
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBAction func butTestMailTapped(_ sender: Any) {
if !MFMailComposeViewController.canSendMail() {
var alert = UIAlertView(title: "Alert", message: "Mail Server not available", delegate: nil, cancelButtonTitle: "ok", otherButtonTitles: "")
alert.show()
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients(["xxx#xx.xx.xx"])
// Present the view controller modally.
self.present(composeVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true,completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
You need latest update in Doc
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) { }

Closing mail view controller upon clicking Cancel or Delete Draft

I created a Mail Controller in my application, it works perfectly, the sending part is fine as well. But when I click on "Cancel" or "Delete Draft" the window wont close and it basically gets stuck on the email screen.
I tried searching, all the fixes did not work. Here is my code.
#IBAction func btnEmail(_ sender: Any)
{
let mailCompose = MFMailComposeViewController()
mailCompose.mailComposeDelegate = self
mailCompose.setToRecipients(["issam.barakat#hct.ac.ae"])
mailCompose.setSubject("Amazing Health App!")
mailCompose.setMessageBody("This application is amazing, keep it up!", isHTML: false)
if MFMailComposeViewController.canSendMail()
{
self.present(mailCompose, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
First of all, we need to import the MessageUI module.
Second, we need to specify that the View Controller will conform to the MFMailComposeViewControllerDelegate protocol. Later, we’ll actually implement the method that this protocol outlines, which will allow us to make the email composer screen go away once the user is finished either sending an e-mail or cancels out of sending one.
try this..
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.present(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self // Extremely important to set the --mailComposeDelegate-- property, NOT the --delegate-- property
mailComposerVC.delegate = self
mailComposerVC.setToRecipients(["someone#somewhere.com"])
mailComposerVC.setSubject("Sending you an in-app e-mail...")
mailComposerVC.setMessageBody("Sending e-mail in-app is not so bad!", isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
reference https://www.andrewcbancroft.com/2014/08/25/send-email-in-app-using-mfmailcomposeviewcontroller-with-swift/
Just add MFMailComposeViewControllerDelegate to your class declaration. Example:
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
...
}

UIButton does not bring up MFMailViewController

I'm having an error with a bit of code. I am new to this and I am trying to teach myself. I am finding most of my answers online but can't seem to find anything about this particular issue. I want to send an email within the app but anytime I press the email button, the MFMailViewController does not come up. It is like my UIButton isn't working. But I know I have it as an IBAction. Here is my code so far. Any help is much appreciated.
import UIKit
import MessageUI
class RequestService: UIViewController,MFMailComposeViewControllerDelegate {
#IBOutlet weak var CustomerName: UITextField!
#IBOutlet weak var emailButton: UIButton!
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
}
}
You made an error in the syntax in your sendMail function. The code you posted will only open the view controller if the device can't send mail. Change it to this:
#IBAction func sendEmail(_ sender: UIButton) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setToRecipients(["jwelch#ussunsolar.com"])
composeVC.setSubject("New Support Ticket")
composeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(composeVC, animated: true, completion: nil)
}
You only attempt to display the mail controller if the device can't send email. That's backwards.
#IBAction func sendEmail(_ sender: UIButton) {
if MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
let ComposeVC = MFMailComposeViewController()
ComposeVC.mailComposeDelegate = self
ComposeVC.setToRecipients(["jwelch#ussunsolar.com"])
ComposeVC.setSubject("New Support Ticket")
ComposeVC.setMessageBody(CustomerName.text!, isHTML: false)
self.present(ComposeVC, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController,didFinishWithResult result:MFMailComposeResult, error: NSError?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
And you need the delegate method outside of the other method.

swift - dismissing mail view controller from Sprite Kit

I am trying to add a send email button to a Sprite Kit game. I can get the email dialog to show up. But if I hit cancel, the app will crash or do nothing. If I hit send, the email will send, but the dialog stays. I cannot get the mailComposeController function to fire...please help!
Code:
import Foundation
import UIKit
import MessageUI
class MailViewController: UIViewController, MFMailComposeViewControllerDelegate {
let systemVersion = UIDevice.currentDevice().systemVersion
let devicemodel = UIDevice.currentDevice().model
let appVersion = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as! String
let appBuild = NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as! String
let myrootview2 = UIApplication.sharedApplication().keyWindow?.rootViewController
let mailComposerVC = MFMailComposeViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.view.window?.rootViewController = mailComposerVC
print("This is the rootview2: \(myrootview2)")
myrootview2!.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
var msgbody: String
mailComposerVC.mailComposeDelegate = self
msgbody = "\n\nDevice: \(devicemodel)\niOS Version: \(systemVersion)\nApp Version: \(appVersion)\nApp Build Number: \(appBuild)\n"
mailComposerVC.setToRecipients(["test1#test.com"])
mailComposerVC.setSubject("test subject")
mailComposerVC.setMessageBody(msgbody, isHTML: false)
//print(mailComposerVC)
return mailComposerVC
}
func showSendMailErrorAlert() {
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// THIS DOESN'T GET CALLED WHEN SENDING OR CANCELLING EMAIL!
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
let test1 = result.rawValue
print(test1)
print(controller)
print(self)
print(myrootview2)
}
The issue is you are making the mailVC as the root view, you have to present it on your view like given below
#IBAction func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
 func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}