Sending Email in SwiftUI

In SwiftUI, enhance user communication by integrating email through the mailto URL scheme or using UIKit’s MFMailComposeViewController within SwiftUI, each offering unique benefits.
Written by

Joash Tubaga

Updated on

Oct 23 2024

Table of contents

    Overview

    In SwiftUI, seamlessly integrating email functionality can significantly enhance user interaction and communication. There are two primary methods for incorporating email capabilities within SwiftUI apps: utilizing a mailto URL scheme to open the default mail application and employing the MFMailComposeViewController from UIKit, which is wrapped for use within SwiftUI. In this article, we will explore both methods in detail.

    The pink line in the gif above is just for redacting the email address.

    Method 1: Redirecting Users to the Mail App

    This method efficiently utilizes SwiftUI’s environment-specific capabilities to handle external URLs, aligning well with the modern SwiftUI framework’s design philosophy. It provides a clean and direct way to integrate external link handling within the SwiftUI view structure, maintaining a high level of abstraction and minimizing direct interaction with lower-level APIs.

    Code Snippet

    Swift
    import SwiftUI
    
    struct ContentView: View {
        @Environment(\.openURL) private var openUrl
        
        var body: some View {
            Button("Send email using the default mail app") {
                sendEmail(openUrl: openUrl)
            }
        }
        
        func sendEmail(openUrl: OpenURLAction) {
            let urlString = "mailto:sample@email.com?subject=Feedback&body=Hi%20there!"
            guard let url = URL(string: urlString) else { return }
            
            openUrl(url) { accepted in
                if !accepted {
                    // Handle the error, e.g., show an alert
                }
            }
        }
    }

    Code Explanation

    • Environment Property (openUrl): The @Environment(\.openURL) property wrapper provides access to the environment’s openURL action, which is used to open external links. By leveraging this SwiftUI feature, you can programmatically control how URLs are handled within the app context.

    • Button with Action: A button titled “Send email using the default mail app” is displayed. Upon tapping, it triggers the sendEmail function, passing the openUrl action.

    • Constructing and Opening the URL:

      • URL Construction: The function constructs a mailto URL containing a predefined email address, subject, and body. This is constructed as a string and then converted into a URL object.
      • URL Opening: The openUrl action attempts to open the constructed URL. A closure is provided to handle the result of this action:
        • If the URL opening action is accepted (accepted is true), the mail application is opened with the email draft pre-filled.
        • If the URL is not opened (accepted is false), error handling logic can be implemented, such as showing an alert to inform the user that the operation failed.

    Pros and Cons of Method 1

    Redirecting to the Mail App:

    • Pros: Simpler implementation; no need to manage email logic within the app. Universally compatible with all devices that have a mail app.
    • Cons: Less control over the user interface and user experience. Reliance on external applications.

    Method 2: Using UIKit’s MFMailComposeViewController

    This method provides a highly integrated email composition experience within the app, leveraging SwiftUI’s ability to incorporate UIKit components seamlessly. This approach is beneficial for maintaining consistency in the app’s user interface and can be particularly advantageous when you want to offer a more controlled and refined email composition experience.

    Code Snippet

    Swift
    import SwiftUI
    import MessageUI
    
    struct ContentView: View {
        @State private var isShowingMailView = false
    
        var body: some View {
            Button("Send email in-app") {
                self.isShowingMailView = true
            }
            .sheet(isPresented: $isShowingMailView) {
                MailComposerViewController(recipients: ["sample@email.com"], subject: "Feedback", messageBody: "Hi there!")
            }
        }
    }
    
    struct MailComposerViewController: UIViewControllerRepresentable {
        @Environment(\.dismiss) var dismiss
        var recipients: [String]
        var subject: String
        var messageBody: String
    
        func makeUIViewController(context: Context) -> MFMailComposeViewController {
            let mailComposer = MFMailComposeViewController()
            mailComposer.mailComposeDelegate = context.coordinator
            mailComposer.setToRecipients(recipients)
            mailComposer.setSubject(subject)
            mailComposer.setMessageBody(messageBody, isHTML: false)
            return mailComposer
        }
    
        func updateUIViewController(_ uiViewController: MFMailComposeViewController, context: Context) {}
    
        func makeCoordinator() -> Coordinator {
            return Coordinator(self)
        }
    
        class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
            var parent: MailComposerViewController
    
            init(_ parent: MailComposerViewController) {
                self.parent = parent
            }
    
            func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
                parent.dismiss()
            }
        }
    }

    Code Explanation

    • ContentView Structure:

      • Button for Email: A button labeled “Send email in-app” is implemented. When pressed, it sets isShowingMailView to true.
      • Sheet Presentation: The .sheet modifier listens to the isShowingMailView state variable and presents a mail composer interface when triggered.
    • MailComposerViewController Integration:

      • UIViewControllerRepresentable Protocol: This struct makes MailComposerViewController conform to UIViewControllerRepresentable, allowing UIKit components to be used within SwiftUI.
      • Creating the Mail Composer: The makeUIViewController method initializes an instance of MFMailComposeViewController, configuring it with the necessary details like recipients, subject, and message body.
      • Delegate Setup: It also sets up a coordinator as the mail compose delegate to handle the response from the mail composer, such as user actions (sent, canceled).
    • Coordinator Class:

      • Role of Coordinator: Acts as a bridge between the UIKit MFMailComposeViewController and the SwiftUI environment. It handles the closure of the mail composer interface based on the user’s action by calling the dismiss environment method.
      • Delegate Methods: Implements the mailComposeController(didFinishWith:error:) delegate method to dismiss the mail composer when the email interaction is completed.

    Pros and Cons of Method 2

    UIKit Way:

    • Pros: Provides a seamless in-app experience, maintaining UI consistency of the app. Gives more control over the email composition process.
    • Cons: More complex implementation; requires integrating and wrapping UIKit components. May not function on devices without configured email accounts or in simulators.

    Conclusion

    Both methods provide robust solutions for adding email functionality to SwiftUI apps. The choice between these methods should be based on specific project requirements, such as the desired level of user experience and control over the email composition process.



    Get started for free

    Join over 2,000+ students actively learning with CodeWithChris