I am the author of iOS 17 Fundamentals, Building iOS User Interfaces with SwiftUI, and eight other courses on Pluralsight.
Deepen your understanding by watching!
Send Email In-App – Using MFMailComposeViewController with Swift
Updated on October 11, 2016 – Swift 3.0
In this writing, I want explore how to use MFMailComposeViewController
with Swift to send e-mails within your app as a walkthrough. My focus here is “quick and dirty” pragmatism, so that we can easily see what the inter-working components of MFMailComposeViewController
are. That being said, here’s an important disclaimer – I’m going to overload the View Controller’s responsibilities in the examples to follow.
An op-ed with my thoughts and experimentation on how to keep the View Controller clean by factoring out some of the configuration and delegate methods to another class is now live as well.
Defining the requirements
As part of your app requirements, you need to be able to send an e-mail within your app without leaving it. Additionally, you need to pre-populate some standard e-mail fields such as “To”, “Subject”, and “Body”.
Not only is this possible, the API for accomplishing it is pretty straight forward.
Implementation overview
In order to implement the solution for this requirement, you need a few things:
- A View Controller from which your user will initiate the display of the email composer screen, presumably by tapping on a button or something else that’s wired up to an `@IBAction`.
- A configured `MFMailComposeViewController` to present.
- An `MFMailCompseViewControllerDelegate` to handle dismissing the email composer screen.
Note that you may have trouble in the iOS 8 Simulator, with symptoms of the composer presenting itself and immediately dismissing. Running the app on an actual device running iOS 8 should work fine, as the problem seems to be isolated to the simulator, only.
An example View Controller class that implements the three steps above is proposed here. All that would be left for you to do is to design a user interface and wire up the @IBAction
. Lines of code that are of special importance, such as module imports, protocol conformance and assignment, checking for the ability to send e-mail, and the protocol method implementation are highlighted. Take a look:
1import Foundation
2import UIKit
3import MessageUI
4
5class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
6
7 override func viewDidLoad() {
8 super.viewDidLoad()
9 }
10
11 @IBAction func sendEmailButtonTapped(sender: AnyObject) {
12 let mailComposeViewController = configuredMailComposeViewController()
13 if MFMailComposeViewController.canSendMail() {
14 self.present(mailComposeViewController, animated: true, completion: nil)
15 } else {
16 self.showSendMailErrorAlert()
17 }
18 }
19
20 func configuredMailComposeViewController() -> MFMailComposeViewController {
21 let mailComposerVC = MFMailComposeViewController()
22 mailComposerVC.mailComposeDelegate = self // Extremely important to set the --mailComposeDelegate-- property, NOT the --delegate-- property
23
24 mailComposerVC.setToRecipients(["[email protected]"])
25 mailComposerVC.setSubject("Sending you an in-app e-mail...")
26 mailComposerVC.setMessageBody("Sending e-mail in-app is not so bad!", isHTML: false)
27
28 return mailComposerVC
29 }
30
31 func showSendMailErrorAlert() {
32 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")
33 sendMailErrorAlert.show()
34 }
35
36 // MARK: MFMailComposeViewControllerDelegate Method
37 func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
38 controller.dismiss(animated: true, completion: nil)
39 }
40}
Step-by-step implementation
With the above example in front of you, let’s explore what’s going on here in detail.
Setting up the View Controller
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.
sendEmailButtonTapped()
This is the method that responds to the user tapping on a button. Assuming this is wired up to an appropriate element in the UI, it kicks off everything related to creating and showing the email composer screen. The logic is as follows:
- Obtain a configured `MFMailComposeViewController` instance
- Check to make sure the device can send e-mail at this moment
- If it can, present the configured `MFMailComposeViewController`
- Otherwise, show an alert with an error message
configuredMailComposeViewController()
I decided to encapsulate the configuration of an MFMailComposeViewController
instance inside a function. I found that it made things a little more readable, perhaps more testable, and kept the spirit of decomposing sub-steps of a process into individual, single-responsibility functions.
One vital property to set is the mailComposeDelegate
property (otherwise, you can never get rid of the e-mail composer screen after it’s presented). Now, there’s a “gotcha” here – MFMailComposeViewController
instances also have a property named delegate
. The delegate property is not the one to set (I did this at first and wondered why my implemented delegate “callback” method never got called). Set the mailComposeDelegate
property to the instance of whatever you want to handle dismissing the email composer screen once the user is finished sending an e-mail or cancels. In the example, I set it to self
, since the View Controller itself will implement the appropriate delegate method (Read my thoughts on cleaning this up a bit).
As you can see, setting up the “To”, “Subject”, and “Body” are simply a matter of setting properties of an MFMailComposeViewController
instance. Notice that setToRecipients()
accepts an array of e-mail address strings, so don’t forget to wrap that argument in an array, even for a single recipient. The same would work for Cc, and Bcc recipients, had I configured those.
showSendMailErrorAlert()
This method shows a simple UIAlertView if the user’s device cannot send an e-mail at the moment.
MFMailComposeViewController’s delegate method
The implementation of this delegate method simply dismisses the email composer screen.