4

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(["test@test.com"])

                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!

Malburrito
  • 860
  • 7
  • 23

2 Answers2

3

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:someone@example.com?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.

Malburrito
  • 860
  • 7
  • 23
  • 1
    What does `thisIsTest()` do? – meaning-matters Nov 10 '22 at 22:19
  • That was an error ... I just replaced it with the correct code. – Malburrito Nov 19 '22 at 15:11
  • This shouldn't be the approved answer as it's very incorrect. MailComposeViewController should be MFMailComposeViewController, needs importing MessageUI, and sendEmail() is not even an existing method. Documentation: https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller – albertodebortoli Jan 07 '23 at 17:00
2

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(["test@test.com"])

                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.

Arjun
  • 376
  • 3
  • 13
  • I already figured that this might be the case and tested on my (physical) iPhone as well. However, I encountered the same behavior. – Malburrito Jan 15 '21 at 20:41
  • 1
    @Malburrito I just tested your code without changing anything and it worked. Make sure you have the iOS Mail app fully setup on your device so that ```MFMailComposeViewController.canSendMail()``` returns true. – Arjun Jan 15 '21 at 21:34
  • Thanks for testing on your end again! I made the mistake to believe that any email app (e.g. Outlook or Gmail) would be triggered. But it seems to only work for Apple's Mail app. When this is installed on the test device, it works just fine. Do you happen to know how the code must be changed to work with the "default" email app that the user can set in iOS 14? – Malburrito Jan 16 '21 at 09:21
  • 1
    Unfortunately that doesn't seem to be possible as of now: https://developer.apple.com/forums/thread/653146 Instead you could create a mailto link button. You won't be able to insert a subject or message body but it would open the default mail app: https://stackoverflow.com/questions/25981422/how-to-open-mail-app-from-swift – Arjun Jan 16 '21 at 21:44
  • 1
    Thanks @Arjun - that's exactly the solution I went for. Actually it's even possible to pass in a subject using a mailto: link (at least it worked well with Gmail). If interested, find my current solution in the answer I posted below. – Malburrito Jan 17 '21 at 10:04