MFMailComposeViewController in SwiftUI


In iOS development, sending emails directly from your app can enhance user interaction by providing a seamless experience for sharing information or contacting support. In this tutorial, we’ll explore how to integrate MFMailComposeViewController with SwiftUI to allow users to compose and send emails from within your app.

SwiftUI View

We will create a SwiftUI view that conforms to UIViewControllerRepresentable to wrap around MFMailComposeViewController. After sending an email, we will use an @Environment(.dismiss) action to dismiss the controller.

import SwiftUI
import MessageUI

public struct MailComposeView: UIViewControllerRepresentable {
    
    let subject: String
    let toRecipients: [String]
    let body: String
    
    @Environment(\.dismiss) private var dismiss
    
    public static var canSendMail: Bool {
        MFMailComposeViewController.canSendMail()
    }
    
    public init(subject: String, toRecipients: [String], body: String) {
        self.subject = subject
        self.toRecipients = toRecipients
        self.body = body
    }
    
    public func makeUIViewController(context: Context) -> MFMailComposeViewController {
        let controller = MFMailComposeViewController()
        controller.setSubject(subject)
        controller.setToRecipients(toRecipients)
        controller.setMessageBody(body, isHTML: false)
        controller.mailComposeDelegate = context.coordinator
        return controller
    }
    
    public func updateUIViewController(_ uiViewController: MFMailComposeViewController, context: Context) {}
    
    public func makeCoordinator() -> Coordinator {
        Coordinator {
            dismiss()
        }
    }
    
    public class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
        private var dismiss: () -> Void
        
        init(dismiss: @escaping () -> Void) {
            self.dismiss = dismiss
        }
        
        public func mailComposeController(
            _ controller: MFMailComposeViewController,
            didFinishWith result: MFMailComposeResult,
            error: Error?
        ) {
            dismiss()
        }
    }
}

Example of usage

You can present this view modally within your app.

import SwiftUI

public struct FeedbackDemoView: View {
            
    @State private var isFeedbackPresented = false
    
    public var body: some View {
        Button("Feedback") {
            isFeedbackPresented.toggle()
        }
        .fullScreenCover(isPresented: $isFeedbackPresented) {
            feedbackView
        }
    }
    
    @ViewBuilder
    private var feedbackView: some View {
        if MailComposeView.canSendMail {
            MailComposeView(
                subject: "Feedback",
                toRecipients: ["livsy@livsycode.com"],
                body: ""
            )
        } else {
            NavigationStack {
                ContentUnavailableView(
                    "The Mail application is not configured.",
                    systemImage: "exclamationmark.bubble",
                    description: Text("")
                )
                .toolbar {
                    ToolbarItem(placement: .cancellationAction) {
                        Button("Cancel") {
                            isFeedbackPresented = false
                        }
                    }
                }
            }
        }
    }
    
}

Conclusion

By using MFMailComposeViewController within SwiftUI, you’ve added a powerful feature to your app with minimal code.