Sending Email with Image Attachments in Swift

Sending content from an iOS application to external sources like email is a common requirement. This quick post summarizes the steps to implement this feature. Aa an added twist, the project used for this technique post includes capturing an image from one of the app's UIViews.

Demonstration of iOS App Sending Email using Swift

Sending Email from within an app

The crux of the technique is using the iOS provided MFMailComposeViewController.  This handy class has a perfectly descriptive name.

To use this component, make sure to include the following import at the top of the file where it's referenced:

import MessageUI

Once the namespace is imported, create and configure a Compose ViewController by setting the properties of the email.

@IBAction func sendEmailTapped(_ sender: UIButton) {
    if let addr = emailAddress.text, !addr.isEmpty,
        let jpegData = designView.asJpeg,
        MFMailComposeViewController.canSendMail() {
        
        let mail = MFMailComposeViewController()
        
        mail.setToRecipients(["\(addr)"])
        mail.setSubject("Design attached")
        mail.setMessageBody("Body goes here", isHTML: true)
        mail.mailComposeDelegate = self
        mail.addAttachmentData(jpegData, 
                                mimeType: "image/jpeg", 
                                fileName: "mydesign.jpeg")
        
        present(mail, animated: true)
    }
    else {
        print("Email cannot be sent")
    }
}

Code Snippet that Sends an Email from Swift Code
Once the inputs for the email are set, call the present() function to call up the iOS mail sheet (you can see it in the demo after the "email" button is tapped in the app.

In this sample app, an attachment is included with the email. Almost any type of data can be attached--just make sure to set the correct MIME type for the Data, so the sending and receiving e-mail client applications will know how to present a preview to the user, and decide what actions can be taken with the attachment once it's received.  In this demo, the attachment is a JPEG image file.

mail.addAttachmentData(jpegData, 
                        mimeType: "image/jpeg", 
                        fileName: "mydesign.jpeg")

A caveat to using the Compose ViewController is that the device must be able to send email. Typically this just means the user has configured at least one e-mail account, but make sure to trap the error and provide a useful error to a user. This example only uses the print() statement to send output to the debug log--but you should use an Alert() or other user-visible error report.

Note the delegate assignment in the above code snippet:

    mail.mailComposeDelegate = self

Implement the MFMailComposeViewControllerDelegate to get feedback from iOS whether the email send operation succeeded or not.  This delegate method is a good place to provide feedback to the user.

Errors are provided in an error implementing the Error protocol, so be helpful to users and provide them feedback and a way to recover.

extension ViewController: 
      MFMailComposeViewControllerDelegate {
    func mailComposeController(_ controller:   
            MFMailComposeViewController, 
            didFinishWith result: MFMailComposeResult, 
            error: Error?) {
            
        if let _ = error {
            self.dismiss(animated: true, completion: nil)
        }
        switch result {
            case .cancelled:
                print("Cancelled")
                break
            case .sent:
                print("Mail sent successfully")
                break
            case .failed:
                print("Sending mail failed")
                break
            default:
                break
        }
        controller.dismiss(animated: true, completion: nil)
    }
}

Code Snippet of MFMailComposeViewControllerDelegate

extension ViewController: MFMailComposeViewControllerDelegate {
    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        if let _ = error {
            self.dismiss(animated: true, completion: nil)
        }
        switch result {
            case .cancelled:
                print("Cancelled")
                break
            case .sent:
                print("Mail sent successfully")
                break
            case .failed:
                print("Sending mail failed")
                break
            default:
                break
        }
        controller.dismiss(animated: true, completion: nil)
    }
}

The demo video above actually illustrates two other techniques, covered in related posts:

Related Post: Drawing with Core Graphics using Swift

Related Post: Capture a UIView as a a JPEG image using Swift

Full Project Source Code

The source for this project is available in my GitHub repository:

Link to GitHub Repository