Skip to main content
March 19, 2015

Email attachments in Swift

I began Udacity’s “iOS App Development with Swift” nanodegree last week. As part of our course we were asked to create a tutorial on iOS development.

The first app we are working on for the course is an audio recorder that allows you to apply effects. I figured it would be nice to be able to send the recordings to someone when you were done so here is my write up on sending emails with attachements from your Swift app.

In this tutorial we will:

  1. Create a single view controller app with a button to send email
  2. Add an asset (image, audio, etc.) to our app to test attachments
  3. Create a MFMailComposeViewController instance within our main view controller
  4. Turn our main view controller into a MFMailComposeViewControllerDelegate so we can return to it when the user is done.

WARNING This code is best tested on an actual device.

###1. Setting up the application

XCode start screen

Create a new project in Xcode and choose the template: iOS Single View Application.

Name the app and be sure the language is set to Swift.

Open Main.storyboard and drag a button from the library pane onto the view. Change the text to something like “Email sound file.” I added horizontal and vertical contraints so it will stay centered on any sized screen.

XCode start screen

Next show the Assistant editor and make sure that it is set to automatic. You should now see the file ViewController.swift, in the right side of the editor.

Control drag from the button to inside the top of the ViewController class to create a outlet called “emailButton”. Control drag again and this time to inside the bottom of the ViewController. Create an action called “sendEmail”.

Your ViewController should now look something like this:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var emailButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    @IBAction func sendEmail(sender: UIButton) {

    }
}

###2. Adding a file to attach

For this example we will add a file to the application that we want to send in an email. In a real app you would probably want to add a file that was created by the app or chosen by the user, but this will give an idea of how it works.

I used this recording of swifts(the bird) from http://freesound.org.

Find the file in the Finder and drag it into the Supporting Files group in the navigator. Make sure “Copy items if needed” is checked.

###3. Creating the mail compose controller

Finding Objective-C code on the web is a great starting point for working in Swift. Coding Explorer Blog and AppCoda have nice tutorials on creating an email composition view in Objective-C using the MFMailComposeViewController.

Once you have read over some Objective-C is a good idea to check over Apple’s documentation. There are some aspects that differ from Objective-C particularly when it comes to instantiating variables. I have linked to the relavent frameworks and protocols below.

MFMailComposeViewController creates a view that looks like the Mail apps composition window. You can prepopulate the subject, body, recipients and any attachments with this object. It is part of the MessageUI framework, so the first thing you need to do is add the following line to the top of your ViewController.swift file.

import MessageUI

Next we will start working on our sendEmail method by checking to see if the device can send email. MFMailComposeViewController contains a method do just that: canSendEmail(). Since everything we are going to do relies on sending email, we will wrap the rest of our code in an if statement that checks for this.

@IBAction func sendEmail(sender: UIButton) {
    //Check to see the device can send email.
    if( MFMailComposeViewController.canSendMail() ) {
        println("Can send email.")
    }
}

This will prevent any unwanted errors but if you wanted to make your code more user friendly it might make sense to check for this ability before even displaying your email button.

Unlike our main view which is generated from the Main.storyboard file, our email composition view will be created in code. We start by creating an instance of MFMailComposeViewController inside our if statement.

let mailComposer = MFMailComposeViewController()

We can now populate some of the fields of the email.

//Set the subject and message of the email
mailComposer.setSubject("Have you heard a swift?")
mailComposer.setMessageBody("This is what they sound like.", isHTML: false)

Next we can attach our file. Again before trying to attach the file its a good idea to check that it exists. Since I included audio file I am sending in my application I will find it using the NSBundle.mainBundle. Here is the code:

if let filePath = NSBundle.mainBundle().pathForResource("swifts", ofType: "wav") {
    println("File path loaded.")
}

Now that we are sure we have the filePath we can prepare to attach the file to our email. Attaching files to the email is done by using the *addAttachmentData() method, which requires three arguments:

Before we get to the file data lets talk about MIME type. For our simple application we will no exactly what type of file we are sending because I have included it from the start so I will just hardcode the type into my code. You may want a more sophisticated solution for your app. I suggest looking at the solutions used by AppCoda tutorial (which relies on the extension). You can find a list of MIME types here.

The file data can be loaded by using a NSData object. Instantiating an NSData object in Swift is a bit different than doing so in Objective-C. Just like when we are loading the file path I enclosed loading the data in an if statement as well.

if let fileData = NSData(contentsOfFile: filePath) {
    println("File data loaded.")
}

Now that we have our data we can attach our data. Inside the if statement add the following code:

mailComposer.addAttachmentData(fileData, mimeType: "audio/wav", fileName: "swifts")

Now we just need to present our view:

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

I placed the presentViewController() method outside the last if statement so it will attempt to send the email regardless of whether the file is attached to the document.

You should now be able to test and send an email from the app.

###4. Returning to the main view

You may have noticed that although you can now send emails, there is no way to return to the the main view controller. Lets fix that.

We need to make our main view the delegate for the MFMailComposeViewController. This will allow it to receive the results the second view after a message is sent, canceled or saved as a draft.

To do this we need to add a protocol to our ViewController class. That protocol is included in MessageUI framework: MFMailComposeViewControllerDelegate. Change the class definition line to:

class ViewController: UIViewController, MFMailComposeViewControllerDelegate {

Next we need to declare our particular view controller as the delegate for the MFMailComposeViewController we created and presented in the sendMail() method.

//Add this line below
//let mailComposer = MFMailComposeViewController()
//inside the sendMail() method
mailComposer.mailComposeDelegate = self

We assign the composer’s deleage to self, which in this instance is ViewController

Now we need to define a method that will receive the results of the mail composition view. This name is defined in the MFMailComposeViewControllerDelegate protocol.

func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {

}

For our simple app we will simply dismiss the MFMailComposeViewController which will return us to our original view. Add this line inside mainComposeController():

self.dismissViewControllerAnimated(true, completion: nil)

If your are interested in knowing more about what the user did in the composition view checkout AppCoda tutorial. It should be fairly straight forward to convert to Swift.

That is it. Hope that helps you with your code. Find the complete source for ViewController below: