55

I am trying to set up an app with send email option.

I have this code:

import Foundation
import MessageUI
import UIKit

class emailClass: UIViewController, MFMailComposeViewControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()

        if !MFMailComposeViewController.canSendMail() {
            print("Mail services are not available")
            return
        }        
        sendEmail() 
    }

    func sendEmail() {      
        let composeVC = MFMailComposeViewController()
        composeVC.mailComposeDelegate = self
        // Configure the fields of the interface.
        composeVC.setToRecipients(["address@example.com"])
        composeVC.setSubject("Hello!")
        composeVC.setMessageBody("Hello this is my message body!", isHTML: false)
        // 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 didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

So I get this message: "Mail services are not available". Now I've logged in the simulator device in iCloud... So i think it should do it but it's not. Why isn't this working? Can you tell me whats wrong and how can I move forward?

swiftBoy
  • 35,607
  • 26
  • 136
  • 135
H.N.
  • 1,207
  • 2
  • 12
  • 28
  • Did you configure your device with an email address? if no, then do it ...and it may solve your issue. – dev_binod Nov 30 '16 at 11:57
  • I am using the simulator. How can I configure the device with an email address? when I go to settings I can't see options "email".... – H.N. Nov 30 '16 at 11:59
  • I thing , you have solved this issue.. Meanwhile here is link to configure an email for an iOS device ... https://support.apple.com/en-in/HT201320 – dev_binod Nov 30 '16 at 14:19
  • **100% working and tested** here,https://stackoverflow.com/questions/36838166/how-to-send-a-mail-from-my-ios-application-swift/49169455#49169455 – Mr.Javed Multani Mar 08 '18 at 09:21

5 Answers5

49

It will not work with simulator. Please test it on iPhone device. You can refer Apple Developer Portal - MFMailComposeViewController

SandeepM
  • 2,601
  • 1
  • 22
  • 32
39

Here's how I did it. It looks like you followed the documentation very well, I thought I'd add my variation in case it helps someone else. Plus, this is a little more updated to current (Aug 2017) syntax.

Conform to the MFMailComposeViewControllerDelegate protocol, and check if the device can send mail.

import Foundation
import UIKit
import MessageUI

class WelcomeViewController: UIViewController, MFMailComposeViewControllerDelegate {


override func viewDidLoad() {
    super.viewDidLoad()

    if !MFMailComposeViewController.canSendMail() {
        print("Mail services are not available")
        return
    }
}

My app uses an IBAction to initiate the mail composition.

@IBAction func sendFeedbackButtonTapped(_ sender: Any) {

    let composeVC = MFMailComposeViewController()
    composeVC.mailComposeDelegate = self

    // Configure the fields of the interface.
    composeVC.setToRecipients(["exampleEmail@email.com"])
    composeVC.setSubject("Message Subject")
    composeVC.setMessageBody("Message content.", isHTML: false)

    // Present the view controller modally.
    self.present(composeVC, animated: true, completion: nil)

}

About the following mailComposeController function, the documentation says

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(_:didFinishWith:error:) method of its delegate. Your implementation of that method must dismiss the view controller explicitly, as shown in Listing 3. You can also use this method to check the result of the operation.

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

Source Apple Documentation: MFMailComposeViewController

Josh
  • 643
  • 7
  • 11
  • 1
    To emphasize something that will break things: in older versions, the type of the `error:` in the `...didFinishWith result:...` header was `NSError?`. In new versions, it is simply `Error?`. If you don't have the ***EXACT*** same header as expected, your delegate won't quite work -- the part of the code that closes the View Controller won't run, and the mail will remain on top of your other view. Took me way to long to figure out that subtle difference; hopefully this helps somebody. – ConfusionTowers Mar 17 '18 at 03:12
13

Sending an Email is quit easy in Swift 5 you need to confirm and implement MFMailComposeViewControllerDelegate and check if we can send email on this device

Here is the tiny piece of code I was using for my task

import UIKit
import MessageUI

class ViewController: UIViewController, MFMailComposeViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK: IBAction Method for Button click
    @IBAction func sendEmail(_ sender: Any) {
        //TODO:  You should chack if we can send email or not
        if MFMailComposeViewController.canSendMail() {
            let mail = MFMailComposeViewController()
            mail.mailComposeDelegate = self
            mail.setToRecipients(["you@yoursite.com"])
            mail.setSubject("Email Subject Here")
            mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)
            present(mail, animated: true)
        } else {
            print("Application is not able to send an email")
        }
    }

    //MARK: MFMail Compose ViewController Delegate method
    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        controller.dismiss(animated: true)
    }
}

PS: Please don't forget you need to test this on real device

swiftBoy
  • 35,607
  • 26
  • 136
  • 135
9

Code seems to be good and works fine if the app is running in a real device

MFMailComposeViewController.canSendMail() // returns false for simulators.

You can't test it on simulator,You'll be able to test basic things like UI,How the things are happening on Cancel/Send button clicks.

To test,You have to use a device,The Mail application in the device should be configured with some mail(ex: abc@xyz.com).

Hope that helps.

Naresh Reddy M
  • 1,096
  • 1
  • 10
  • 27
1

The problem with your code is, that the

// Present the view controller modally. self.present(composeVC, animated: true, completion: nil)

presents itself in itself ;-)

If you don´t know the current view controller, just display it on the RootViewController, i.e.

UIApplication.shared.keyWindow?.rootViewController?.present(...

Tintenklecks
  • 507
  • 4
  • 6