Malburrito
Malburrito

Reputation: 1198

SwiftUI: Send email using MFMailComposeViewController

I am currently trying to implement a "send email" button in my SwiftUI app, using SwiftUI lifecycle and targeting iOS 14.

I know there are quite some solutions presented online - here on stack overflow and elsewhere. However, I have-not been able to make anything work so far in simulator/on device.

My current solution looks like this (based on this question on stackoverflow:

import SwiftUI
import MessageUI
import Foundation

struct ContentView: View {

    class MailComposeViewController: UIViewController, MFMailComposeViewControllerDelegate {
    
        static let shared = MailComposeViewController()
    
        func sendEmail() {
            if MFMailComposeViewController.canSendMail() {
                let mail = MFMailComposeViewController()
                mail.mailComposeDelegate = self
                mail.setToRecipients(["[email protected]"])

                UIApplication.shared.windows.last?.rootViewController?.present(mail, animated: true, completion: nil)
            } else {
                // Alert
            }
        }
    
        func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
            controller.dismiss(animated: true, completion: nil)
        }
    }
    
    var body: some View {
        Button(action: {
            MailComposeViewController.shared.sendEmail()
        }, label: {
            Text("Send")
        })
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The simulator does show the button and doesn't give me any errors. However, upon clicking the button, nothing happens at all - same thing when testing on device.

Any idea what might be wrong here?

Thanks!

Upvotes: 6

Views: 4385

Answers (2)

Malburrito
Malburrito

Reputation: 1198

Building up on the code snippet shared in my original question:

Based on the answer from @Arjun this is my current workaround to account for the edge case that someone might have deleted the Apple Mail app and is using another email app:

Button(action: {
    if MailComposeViewController.shared.canSendMail() {
        MailComposeViewController.shared.sendEmail()
    } else {
        openURL(URL(string: "mailto:[email protected]?subject=This%20is%20the%20subject")!)
    }
}, label: {
    Text("Send")
})

It opens the in-app sheet as long as the user has set up apple mail and otherwise switches to any other email app using a mailto: link.

Upvotes: 1

Arjun
Arjun

Reputation: 494

Your code works fine, the problem is that the iOS Simulator does not have the Mail app, thus MFMailComposeViewController.canSendMail() returns false. Try it on a physical device, it works. The reason you didn't see any errors is because of this block of code:

if MFMailComposeViewController.canSendMail() {
                let mail = MFMailComposeViewController()
                mail.mailComposeDelegate = self
                mail.setToRecipients(["[email protected]"])

                UIApplication.shared.windows.last?.rootViewController?.present(mail, animated: true, completion: nil)
            } else {
                // Alert
            }

Inside the else block, instead of temporarily commenting // Alert, you should print something instead while debugging, it'll make your life a lot easier.

Upvotes: 2

Related Questions