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:

  1. 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.
  2. A configured MFMailComposeViewController to present.
  3. 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:

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.

  • Pingback: Choosing a Delegate – Clean View Controllers in Swift | Andrew Bancroft()

  • Pingback: Pick a Delegate… Any Delegate… On Clean View Controllers in Swift | Andrew Bancroft()

  • Pingback: Send Text Message In-App – Using MFMessageComposeViewController with Swift | Andrew Bancroft()

  • Pingback: Send Text Message In-App – Using MFMessageComposeViewController with Swift | Dinesh Ram Kali.()

  • http://maiconfs.wordpress.com/ Maicon 

    when execute the code let mailComposerVC = MFMailComposeViewController() the system crashes with error: *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Only RGBA or White color spaces are supported in this situation.’

    • Andrew Bancroft

      Interesting – I’ll try and see if I reproduce that. If I can and if I’m able to figure something out, I’ll let you know. If you’d like, you’re welcome to send a longer code snippet to andrew[at]andrewcbancroft.com

      • http://maiconfs.wordpress.com/ Maicon 

        Here is my code:

        var assunto = “Subject test.”
        var corpo = “Hello, this is a test.”
        var emailTo = [“teste@teste.com”]

        var mc: MFMailComposeViewController = MFMailComposeViewController() //CRASH WHEN TRY TO EXECUTE THIS LINE OF CODE

        mc.mailComposeDelegate = self
        mc.setSubject(assunto)
        mc.setMessageBody(corpo, isHTML: false)
        mc.setToRecipients(emailTo)

        self.presentViewController(mc, animated: true, completion: nil)
        ———END CODE——–

        Crash log:

        *** Assertion failure in -[UICGColor encodeWithCoder:], /SourceCache/UIKit/UIKit-3318.16.25/UIColor.m:1448
        *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Only RGBA or White color spaces are supported in this situation.’

        • Andrew Bancroft

          Man, that’s really odd. I copied and pasted your code into a simple setup and it worked fine. My test was a simple, single-view application with a button that says “Send Email”. When the user taps the button (ie, IBAction), the above code is executed and presents the mail compose view controller without issue.

          What else could we be missing? What’s your target version of iOS? What triggers the execution of your code? Is it a button tap or a table view cell tap or some other scenario?

          • http://maiconfs.wordpress.com/ Maicon 

            Hi Andrew ,

            I think this is something incompatible with my app configuration. I tried to use the same code in another test application and it works great .

            Well, my application uses a table view (static cells) on the first page. In this 1st page I have a button that shows an about screen (is a Segue Push) and on that about screen I would like to send an email with the code I’ve sent to you.

            I’m not using Auto Layout. I’d tried to remove the background image from the about screen, but the error persists.

          • http://maiconfs.wordpress.com/ Maicon 

            The problem is caused by the following code that i used on the previous page:

            self.tableView.backgroundColor = UIColor(patternImage: UIImage(named: “fundo_paper100.png”)!)
            UITableViewCell.appearance().backgroundColor = UIColor(patternImage: UIImage(named: “fundo_paper100.png”)!)

  • http://web.mac.com/russgum/Site/Photo_Blog/Photo_Blog.html RussGum

    mailComposerVC.setMessageBody(“Sending e-mail in-app is not so bad!”, isHTML: false

    is there a way to send an email as html. I would like to replace the text in setMessageBody with a simple html page generated by my app to be. When I try to put “” in the body I generate compile errors. when I escape the it compiles but crashes when run.

    • Andrew Bancroft

      Hi Russ!

      Yes – you should be able to use HTML in your message.

      1) You’ll want to set isHTML to true instead of false
      2) For tags that require quotations around attribute values, you may have to play with either escaping those quotation marks, or simply using single-quotes (maybe…)

      You shouldn’t have to escape the , though. If isHTML is set to true, those tags should be properly interpreted.

      • http://web.mac.com/russgum/Site/Photo_Blog/Photo_Blog.html RussGum

        mailComposerVC.setMessageBody(” n HTML Test “, isHTML: true)

        gives me

        2015-02-18 20:41:07.383 email[26887:18841468] viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 “The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)” UserInfo=0x7e176c40 {Message=Service Connection Interrupted}
        2015-02-18 20:41:28.522 email[26887:18841629] timed out waiting for fence barrier from com.apple.MailCompositionService

        mailComposerVC.setMessageBody(” mailtestHTML Test“, isHTML: true)

        gives compile error: /Users/russellgum/Desktop/Russ/email/email/ViewController.swift:37:69: Expected ‘,’ separator

        There must be something simple I am doing wrong.

        • Andrew Bancroft

          Okay – I’m getting the exact same error as your first example with what I’m I’m trying.

          One thing I want to investigate is whether or not these errors happen when running on the device itself (versus running in the simulator). Have you tried that?

          I plan to do that some time today… If I discover anything, I’ll let you know!

          • http://web.mac.com/russgum/Site/Photo_Blog/Photo_Blog.html RussGum

            There is a note on stack overflow that claims to solve this problem. It is in objective C and I haven’t figured out how to get in in Swift.

            http://stackoverflow.com/questions/7095662/ios-can-i-inject-html-code-in-the-emails-send-by-mail-app

          • Andrew Bancroft

            I did a test just now and confirmed that I get the error, no matter what, using XCode 6.1 AND XCode 6.1.1 with the Simulator. But when I run the same simple example on my actual iPhone, it doesn’t throw that error.

            I used the following code to test whether or not it’d read an HTML tag:
            mailComposerVC.setMessageBody(“<b>Sending e-mail</b> in-app is not so bad!”, isHTML: true)

            All worked fine on the actual device…

            Seems to me it’s a bug in XCode/Simulator, rather than MFMailComposeViewController itself.

  • Pingback: Crash Course in iOS | Sense & Scale()

  • Gary Roberts

    Andrew, this article was very helpful to me. I appreciate when someone shows the Swift version of coding. The only problem is now with the new update in place I get a compiler error on setting the delegate to ‘self’. Because of the new issues with type. As you have mentioned setting the delegate to ‘self is very important. Do you have a suggestion for correcting this

    var emailTitle = “Feedback”
    var messageBody = “Feature request or bug report?”
    var toRecipients = [“ggroberts@rclawapps.com”, “roberts.consulting.apps@gmail.com”]
    var mc:MFMailComposeViewController = MFMailComposeViewController()

    mc.mailComposeDelegate = self

    mc.setSubject(emailTitle)
    mc.setMessageBody(messageBody, isHTML: false)
    mc.setToRecipients(toRecipients)

    • Andrew Bancroft

      Hi Gary!

      My apologies – I haven’t had a chance to look at this issue yet, but I plan to try and investigate before too long. Have you made any progress?

    • Andrew Bancroft

      Hey Gary!

      I’m not sure if you’re still having problems with setting the delegate. One thing I thought of is this: Have you specified MFMailComposeViewControllerDelegate on the declaration line of the class you’re writing this code in? If you don’t, you may get a compiler error saying something to the effect of, “Type ‘ViewController’ does not conform to the protocol ‘MFMailComposeViewControllerDelegate’.

      If that’s the error you’re getting, go up to the line where you’re declaring your ViewController class and make sure it looks something like this;

      class ViewController: UIViewController, MFMailComposeViewControllerDelegate { ... }

  • Pandurang Yachwad

    Thanks Andrew for sharing. It’s Useful.

  • Pingback: Day 020 – Swift Color Picker | AudreyLi.Me()

  • Athena

    Thanks for sharing.

    But i want to know..is sender information not required??

  • Frak N

    Hi Andrew.

    Thank you for the excellent tutorial. I was curious about one thing. I’ve seen email buttons that pre-populate the version of the app and iOS and even iPhone that you are running on into the text/body of the email. Can you please tell me how I could get at information like that? It would be very helpful for receiving feedback emails within an app.

    Cheers

  • J.SwiftNewbie

    Hi Andrew, thanks for this post…really helped me with my project! Also recently learned how to attach an image from camera/photolibrary to my email – but for some reason image orientation changes after sending. Composer displays correct orientation before sending. But after sending, image is displayed rotated 90degrees counter clockwise. It’s driving me nuts. Any tips?

    • Andrew Bancroft

      I’ve seen this happen before, but I’m not 100% sure of how to fix it. A quick search on StackOverflow produced a possible clue as to what’s going on. Specifically, it might pertain to the metadata associated with the image. Maybe. Here’s the post http://stackoverflow.com/questions/13971491/captured-photo-automatically-rotated-during-upload-in-ios-6-0-or-iphone . Maybe that’ll send you looking in a right(ish) direction. Sorry I don’t have much more advice on this!

      • J.SwiftNewbie

        Appreciate the tip! I’m brand new to coding so posts like yours are very helpful :) Thx again!

  • Tim

    Thank you for this post! Every time I run this app it closes and goes to the AppDelegate page with the error: Thread 1: SIGABRT. Any ideas?

    • Andrew Bancroft

      I think I’ve run into this before, but only on the simulator – have you tried to run it on your device at all? Just curious to see if theres a difference in behavior.

      • Tim

        Thanks for the reply. I got it working by re-creating my ViewController.. I think I messed something up in there. And yes you are right – it works properly on a phone versus the simulator.

        I do have another question: Is it possible to not only check to see if email is active, but to check for a particular domain, and then force it to use that email even if it is not set as the default?

  • Michele

    Hey! I want add UIDevice information to my mails. How to do?

  • Carolina

    Hey!

    I would like to export some stuff from my app using an UIActivityViewController. I have a NSURL among other things (which works perfectly when I choose to export to my reading list), but when I opt to send an email, the link that is printed in the email’s body is messed up by the encoding.

    Part of the link says: “…&origem=TODOS&regiao=QUALQUER…” which is converted to: “…&origem=TODOS®iao=QUALQUER…”. This action makes my link a non-clickable one, and I must guarantee this action to the user.

    How can I avoid this? Is there a way to not print the NSURL in the email body ?

    Also, when I print the link to the console this problem does not occur.

    Thank you in advance!

    Part of my code:

    @IBAction func exportWebButton(sender: AnyObject) {

    // some code here > generating testData.pdf

    let activityController = UIActivityViewController(activityItems: [testData,NSURL(string:self.url)!], applicationActivities: nil)

    print(NSURL(string:self.url)!) //> “…&origem=TODOS&regiao=QUALQUER…”

    let presentationController = activityController.popoverPresentationController

    presentationController?.barButtonItem = self.exportWebButtonO

    self.presentViewController(activityController, animated: true, completion: nil)

    }

  • Prabhakar Patil

    i want the user to attach multiple images with the mail, so how can i do it.

  • utsha guha

    Hi, The code mentioned above works perfectly to send single email. Could you please advise how to send multiple emails.

  • Coração Tricolor

    Any changes for swift 3 ? I tried it but only after simulator email singing in with Apple Id it worked although send buttons remains unabled.. is that normal?