iOS Dev — Send Email 📤

Myrick Chow
ITNEXT
Published in
6 min readNov 26, 2022

--

Email is one of the best communication channels in the 21st century. Information can be delivered to a group of recipients in a few seconds. Have you ever thought of integrating the email function into your app?

Apple has provided a simple-to-use framework — MessageUI for developers. We can configure the following properties:

  1. Recipient list
  2. Subject
  3. Message body
  4. Attachments

However, there is also an interesting trick I have encountered in my previous app development. It is about the definition of sent of the MFMailComposeViewControllerDelegate. The delegate returns the sent result even if the iOS device is in aeroplane mode. With this interesting founding, I want to share them with you in this article. 😉

Step-by-Step Procedure

Step 1) Check if the device can send email

import MessageUI

// Confirm the user can send email
guard MFMailComposeViewController.canSendMail() else { return }

There are two possible cases that the app user cannot send out an email:

1. The app user has not yet set up his email account in the system mailing app

2. The iOS MDM profile has disabled the mailing function, referring to this StackOverflow answer.

Step 2) Configure the MFMailComposeViewController instance

// Construct the `MFMailComposeViewController` instance
let mfMailComposeViewController = MFMailComposeViewController()

// To set the recipients list, including the to, cc and bcc fields
mfMailComposeViewController.setToRecipients(["to@example.com"])
mfMailComposeViewController.setCcRecipients(["cc@example.com"])
mfMailComposeViewController.setBccRecipients(["bcc@example.com"])

// To set the email subject
mfMailComposeViewController.setSubject("Example - Subject Text")

// To set the email message body; It can be either plain text or HTML message
mfMailComposeViewController.setMessageBody("<h1>Example - HTML message body </h1>", isHTML: true)

// Presenet the `MFMailComposeViewController` to the app user
present(mfMailComposeViewController, animated: true)

We have to construct the MFMailComposeViewController which controls all the prefilled information of the composed email. It allows us to set the basic params:

  1. The recipients' list, including the to, cc & bcc
  2. The subject field
  3. The message body in either plain text or HTML message

You can now run the app, and the system Mail app is presented with the prefilled information. However, you should find that the email is NOT sent out when you click the send button. This is because you have to implement the MFMailComposeViewControllerDelegate beforehand.

Step 3) Dismiss the MFMailComposeViewController manually

// We must implement the `MFMailComposeViewControllerDelegate` in order to handle the user's action on the mail compo
mfMailComposeViewController.mailComposeDelegate = self

extension DemoViewController: MFMailComposeViewControllerDelegate {

// This is the only callback from the Mail composer to notify the app that the user has carried out certain action
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .cancelled:
print("The user has dismissed the mail composer and deleted the email draft.")

case .saved:
print("The user has dismissed the mail composer and saved the email draft.")

case .sent:
// The email request is queued in the user's mail app
// There is no guarantee that the email is actually sent out!!!
print("The user has \"sent\" the email out.")

case .failed:
print("The user cannot send out the email")
print("Error of sending out the email: \(error.debugDescription)")

}
controller.dismiss(animated: true)
}
}

The MessageUI framework does not dismiss the MFMailComposeViewController by default. It is the developer’s responsibility to close it. We have first to implement the MFMailComposeViewControllerDelegate and dismiss the MFMailComposeViewController at the didFinishWith callback.

There is also a result property to show the user’s action, including:

  1. cancelled — The user clicked the “Cancel” button and then deleted the email draft
  2. saved — The user clicked the “Cancel” button and then saved the email draft
  3. sent — The user has *sent* the email out
  4. fail — There is an error in queuing the email to the mail app

Boom! The email function has been implemented. You can now send the email out successfully.

Trick — Does the email REALLY sent out when the `sent` result is returned?

Did you try to send out the email when there is no network connection, e.g. aeroplane mode? Oh NO! The didFinishWith callback of MFMailComposeViewControllerDelegate actually returns a sent result!!!

What does that mean?

According to the below Apple documentation, the MFMailComposeViewController only send a request to the system mail program to queue the email instead of sending it out !!! In other words, the app can NEVER know if the email is sent out to the recipients successfully.

Apple Official Documentation — mailComposeController(_:didFinishWith:error:):

If the user has opted to send the email created by this interface, that email should be queued in the user’s Mail program by the time this method is called. If an error occurred while queueing the email message, the error parameter contains an error object that indicates the type of failure that occurred.

Reference: Link

Advance Feature - Adding an attachment to the Email

Sending attachments is a rare function in most of the use cases. However, MFMailComposeViewController allows us to attach a list of attachments in any file format to the composed email.

// Attach an image to the composed email
let attachmentImageData = UIImage(named: "example_image_name")!.pngData()!
mfMailComposeViewController.addAttachmentData(attachmentImageData, mimeType: "image/png", fileName: "example_file_name")

We have to first extract the data in the Data format and add it to the email with the addAttachmentData function. Make sure to choose the matched MIME type to preview the attachment in the email successfully. Below is the screenshot of attaching an image to the email:

Learn more

There are many more ways to send emails out. Firebase Extension Trigger Email provides an easy-to-use tool to help developers to send out emails by creating a Firestore document with specific pre-defined fields. Below is my other blog about the Trigger Email Extension. You are welcome to read it if you want to know more about it.

Conclusion

Apple provides an easy to use MessageUI framework to help apps to send out emails by delegating the job to the system mailing app. The app can set the recipients list, subject, message body, and attachment list. Once the user has any action, the MFMailComposeViewControllerDelegate fires the result back and the app has to dismiss the MFMailComposeViewController manually.

Bear in mind that the sent result from the MFMailComposeViewControllerDelegatedoes not really mean the email is sent out. The email request is only queued at the system mailing app!

Hope you would enjoy this blog and have a nice day!

--

--

Mobile Lead @REAL Messenger Inc. https://real.co Focus on Android & iOS Native programming.