iOS Swift 3 present MFMailComposeViewController leads to crash - ios

Hey I am using this code to send an email from within my app
#IBAction func sendEmail(_ sender: Any) {
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients(["test#gmail.com"])
composeVC.setSubject("Feedback")
composeVC.setMessageBody("", isHTML: false)
// Present the view controller modally.
self.present(composeVC, animated: true, completion: nil) //CRASHES
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult, error: Error?) {
// Check the result or perform other tasks.
// Dismiss the mail compose view controller.
controller.dismiss(animated: true, completion: nil)
}
The app is live and emails from users do reach me. Often they are empty except for "Sent from my iPhone" but I don't think that's a programming issue. What is an issue tho is that apparently, last week, 8 devices dropped a crash report because of the "//CRASHES" line.
I think the simulator crashes when trying to do this but I haven't used the simulator for this installation for weeks so those crashreports on iTunesConnect are legit crashes.
What happens if the user doesn't have the email app installed?

You can verify that the user has setup the device for sending mail by calling
[MFMailComposeViewController canSendMail]
before performing any actions

According to Apple Documentation MFMailComposeViewController
Before presenting the mail compose view controller, always call the the canSendMail() method to see if the current device is configured to send email. If the user’s device is not set up for the delivery of email, you can notify the user or simply disable the email dispatch features in your application. You should not attempt to use this interface if the canSendMail() method returns false.
So, initialization of mail compose view controller using MFMailComposeViewController() returns nil, if the user’s device is not set up for the delivery of email and app is terminating due to present a nil modal view controller.
Therefore, before presenting the mail compose view controller you should alway check device's mail settings.
So try this lines of code
#IBAction func sendEmail(_ sender: Any) {
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients(["test#gmail.com"])
composeVC.setSubject("Feedback")
composeVC.setMessageBody("", isHTML: false)
// Present the view controller modally.
self.present(composeVC, animated: true, completion: nil) //CRASHES
}

Related

Add recipient in MFMailComposeViewController from Label represented

I have an application that allows users to send emails by MFMailcomposeViewController.
composeVC.setToRecipients(["email#gmail.com"])
In the same view controller i have a label which is representing the email of the people which is downloaded from Firestore. How can i setToRecipients instead of (["email#gmail.com"]) to (["mailRepresentedLabel#gmail.com"])
I want it to pull the data from mail represented label and add it automatically to the recipient so the end user is not required to add the email to setToRecipient it will be automatically pulled from mailRepresentedLabel
Please help.
My current code is look like this
if !MFMailComposeViewController.canSendMail() {
print("Не удается отправить Имэйл")
return
}
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients(["\(String(describing: mailRepLabel))"])
composeVC.setSubject("Register your client details with us")
composeVC.setMessageBody("Dear agent please register your client with us by replying on that email in order for us to track the information that this client is came with you. if aftersometime the client would like to come without you we will always have the information that this client is came with you and we will send him back to you. Please reply with the following details: Client Name, Passport number Property Managers name.", isHTML: false)
// Present the view controller modally.
self.present(composeVC, animated: true, completion: nil)
print("done")
}
}
extension AgentViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let _ = error {
controller.dismiss(animated: true)
return
}
switch result {
case .cancelled:
print("Canceled")
case.failed:
print("Failed to send")
case.saved:
print("Saved")
case.sent:
print("Email Sent")
}
controller.dismiss(animated: true)
}
}```
You can access the text content of your label with the text attribute. So... Something like this?
mailComposer.setToRecipients([mailRepLabel.text])

Even though app seems to be working fine i did not receive any email

I made a camera app that takes a pic and once a picture is taken i tap on a uibutton to compose a mail with that image as attachment. everything seems to be working fine even the image file in attachment can be seen while composing the email. when tap on 'send', and open my email id I do not receive any new inbox. here is the code
func sendEmail() {
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
// Configure the fields of the interface.
composeVC.setToRecipients([email])
composeVC.setSubject("Hello!")
composeVC.setMessageBody("Hello this is my message body!", isHTML: false)
let imageData: NSData = UIImagePNGRepresentation(pickedImage.image!)! as NSData
composeVC.addAttachmentData(imageData as Data, mimeType: "image/jpeg", fileName: name)
// Present the view controller modally.
self.present(composeVC, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
I found the issue & it was because I didn't allow the mail app on my iOS device access to "Mobile Data". So, don't forget to allow that access as your device will be sending mail via the apple id you logged in with.

MFMailComposeViewControllerDelegate not working Swift 3

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 !

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