Composing Emails in SwiftUI Using A View Modifier

Posted in SwiftUI

Updated on July 23rd, 2021

⏱ Reading Time: 10 mins

Just a couple of weeks ago, I had discussed in this previous post about the MFMailComposeViewController; a class that allows to present and use a system provided view controller in order to compose and send emails through our own applications. The discussion on that post was around a UIKit based application explaining how that class works.

In this post today, I’m going to talk about how to integrate MFMailComposeViewController in SwiftUI projects, since there is no native SwiftUI alternative. But that’s going to be just the half of the content; the other half is a step by step guidance how to hide everything behind a custom view modifier, turning that way email composing into a deeply SwiftUI-looking like and straightforward task.

To get a taste, this is what we are going to end up with:

Note that I will not focus on the details of MFMailComposeViewController in this post. I’ve covered that in the first post, so all I’ll do here is to demonstrate how to integrate it in SwiftUI.

The EmailData type

When presenting an MFMailComposeViewController instance, we can optionally provide default content to it. That content may include the subject, one or more recipients, email’s body, attachments, and a few more.

In order to handle all that easily, we’re going to start off by creating a custom type that will be holding any of the data that we can possibly set before presenting the email composer. It’s a simple struct with a few properties and an inner custom type to represent attachment data:

Note that the data type of each property is in accordance to the data type of the respective property in the MFMailComposeViewController class. EmailData is going to be quite handy, and we will put it in action starting right in the next part.

The EmailComposerView

MFMailComposeViewController is a UIKit view controller, therefore we can’t use it just like that in SwiftUI. In order to bring it to SwiftUI, it’s necessary to deal with it in a custom type conforming to UIViewControllerRepresentable protocol. An instance of that type is what we will be using in SwiftUI views then.

We’ll name that custom type EmailComposerView. According to the UIViewControllerRepresentable protocol requirements, it’s mandatory to implement the following two methods:

Note: Import the MessageUI framework to access the MFMailComposeViewController class.

The first method is the place to initialize, configure, and return an MFMailComposeViewController instance. The second is where changes coming from SwiftUI trigger updates to the view controller. We are not going to need it here, so we’ll leave it empty.

Before we proceed to the code of makeUIViewController(context:), let’s declare the following properties to EmailComposerView:

Here’s the purpose of them:

  • An EmailComposerView instance will be presented in modal sheet, and the presentationMode environment property will let us dismiss it easily.
  • The emailData will be containing any predefined values and data to feed the MFMailComposeViewController instance with.
  • The result property is a closure that will be indicating either the email sending result, or any error that was potentially occurred. Note that MFMailComposeResult is an enum with the following cases regarding an email: sent, saved, cancelled, failed. They are all Int values.

With all that available, let’s focus on the first method now. We’ll begin by initializing a MFMailComposeViewController object:

Next, we’ll set the delegate object through which we’ll be receiving messages from MFMailComposeViewController. This cannot be the self instance; EmailComposerView is a value type, and the delegate object must be a reference type, or in other words, a class instance.

To manage that in UIViewControllerRepresentable types, it’s necessary to define a Coordinator class that conforms to delegate protocols and implements the various delegate methods. We’ll do that in a moment.

Accessing an object of that class in the makeUIViewController(context:) method is done through the context parameter value, and we’ll use it right now in order to set the mailComposeDelegate in the emailComposer instance:

Before we return the emailComposer object from the method, we’ll provide it with all potential predefined content using the emailData object like so:

Finally, we will return the emailComposer object. The entire method is this:

Before moving on, let’s also define a static method that will be indicating whether a device can actually send emails or not. That’s an optional step that will help keep everything around our custom type:

The Coordinator class

The next step is to define the Coordinator class inside the EmailComposerView that will be dealing with delegate messages coming from the UIKit part.

Notice that Coordinator class inherits from NSObject and adopts the MFMailComposeViewControllerDelegate. The parent property is the EmailComposerView instance that is provided to the class upon initialization.

There is just one delegate method to implement. In it we’ll check whether an error occurred or not. If that’s the case, then we’ll call the result closure of parent indicating a failure and passing the error. In the opposite case, we’ll call the result again, but indicating success and passing the compose result this time. In any case, we’ll dismiss the sheet that contains the mail composer instance using the presentationMode environment property.

Here is all that:

The Coordinator class is now complete. There is one last thing missing in the EmailComposerView; to create a Coordinator instance. We do so into another method specific to that purpose:

The EmailComposerView custom type is now complete! Let’s go ahead to make a first use of it, and then see how to create a custom view modifier in order to make things shorter, simpler, and more elegant.

Note: Please read about the UIViewControllerRepresentable protocol if you are not familiar with any of the above steps.

Using the EmailComposerView

Suppose that we have the following SwiftUI view with some initial implementation:

The purpose of the view is to present the email composer when tapping on the Send Email button. The sheet(isPresented:onDismiss:content:) view modifier exists in the above snippet because it’s going to contain an EmailComposerView instance as its content.

See also that there is an alert; we need it to display a message to the user if the device cannot send an email. In this particular demo, alert has an additional role. We’ll use it to report back a successful or failed email sending.

There are three state properties you can see in the above snippet. In order of appearance, they indicate and define the presented state of both the sheet and alert, and keep the message that the alert will display. We also create an EmailData instance with a couple of default values; the subject and the recipient.

Now, we need to do two things in order to present the email composer. The first is to trigger the sheet’s presentation in the button’s action closure. However, we’ll do so once we make sure that the device can actually send emails. If it can’t, then we’ll show the alert instead:

The second thing is to initialize an EmailComposerView instance in the sheet’s content:

See that we supply the emailData property as argument to the EmailComposerView. Besides that, handleEmailComposeResult(_:) is a custom method that handles the email compose result in a way specific to this example. Most probably, a different handling would be more appropriate in a real application. The handleEmailComposerResult(_:) method is this:

After all the above, the entire implementation of the view is now this:

The above is the shortest implementation we can do in order to present the MFMailComposeViewController through the EmailComposerView in SwiftUI. And even though it’s simple, it’s not easily reusable.

So, let’s keep going to find out how to make things simpler and easier to reuse with a custom view modifier.

Creating a custom view modifier

You can think of the view modifier that we’ll create here as a special kind of a sheet. That’s because we are going to actually contain a sheet in the modifier, and have the EmailComposerView instance as the sheet’s content.

Having that information in mind, we’ll start implementing a new custom type that will be conforming to the ViewModifier protocol. We’ll declare a few properties first that we can group in two categories; those necessary to the sheet, and those necessary to the EmailComposerView:

Notice that there is an onDismiss closure declared among the others. We have it there because we may initialize a sheet with such a closure and perform potential additional actions when we dismiss it.

Next, we’ll implement the required body(content:) method by the ViewModifier protocol. In the content parameter value we’ll apply the sheet view modifier. However, note something important here; we will not just contain an EmailComposerView instance into the sheet. We’ll check if the device can send an email, and if not, then we’ll display a Text view with a relevant message and a dismiss button instead!

There is a reason for doing that. We’ll manage that way to get rid of the alert in the SwiftUI view if we don’t need it for any other purpose. So, as you understand, the sheet is going to have a double role. To display either the EmailComposerView, or a Text view along with a button.

Here is the implementation:

See that in case that the device can send emails, we’re passing the EmailComposerView’s result as argument to the result of the EmailComposer instance. Otherwise, we’re setting false to the isPresented binding property that controls the presented state of the sheet inside the dismiss button’s action closure.

We can now use the above as shown right next:

In the button’s action we don’t check anymore whether the device can send emails or not; we handle that in the EmailComposer view modifier. All we need is to make the showEmailComposer property true.

But the most important part is how we use the EmailComposer modifier. The only way to put it in action is by passing it as argument to the modifier() view modifier.

Even though the above is undoubtedly simpler and shorter compared to the initial implementation, it is still not so elegant. And that’s because we have to call modifier() whenever we want to use the EmailComposer view modifier.

As a last step in this post, let’s change that and provide a more natural approach.

The emailComposer method

In order to create a view modifier that looks like built-in modifiers in SwiftUI, defining a ViewModifier type (such as the EmailComposer) is not enough. It’s also necessary to extend the View protocol and define a new method. The name of that method should be exactly what we want to use and see as the final outcome.

To showcase that, here is a View protocol’s extension with a new method defined in it:

The method’s parameter values are exactly the same to the properties of the EmailComposer type. That’s because in its body we are going to call the the modifier() view modifier and pass an EmailComposer instance:

We can now call the above method like any other view modifier in SwiftUI:

In fact, we can even omit the onDismiss argument at all if we don’t want to perform any actions upon the sheet’s dismissal. Remember that it’s nil by default:

That’s definitely a much better implementation than what we started with!

Summary

Composing and sending emails is a common feature in applications. Doing so is not difficult with the use of the MFMailComposeViewController class. However, that’s a UIKit view controller, and it takes some additional steps in order to make it available as a view in SwiftUI. In this post I went through those steps, and I showed how to implement a UIViewControllerRepresentable type and end up with a SwiftUI view. After that, I demonstrated how to take that view and hide it behind a custom view modifier, and eventually have a neat, handy and SwiftUI-like way to present the email composer. I hope that you’ve found all that interesting, useful and educational. And I want to believe that I motivated you enough to start implementing your custom view modifiers; they often make things simpler. And we can reuse them too! Thank you for reading!

This post has been published on Medium too!

EmailComposer is available as a Swift package on Github.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.