Last active
June 2, 2021 13:28
-
-
Save stleamist/6baaa33cdfeecbcb2ba449dfa29abd35 to your computer and use it in GitHub Desktop.
MFMailComposeViewController for SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import MessageUI | |
import SwiftUI | |
struct MailComposer { | |
struct MessageBody { | |
var body: String | |
var isHTML: Bool | |
} | |
struct AttachmentData { | |
var data: Data | |
var mimeType: String | |
var fileName: String | |
} | |
var subject: String = "" | |
var toRecipients: [String]? = nil | |
var ccRecipients: [String]? = nil | |
var bccRecipients: [String]? = nil | |
var messageBody: MessageBody = .init(body: "", isHTML: false) | |
var attachmentData: [AttachmentData] = [] | |
var preferredSendingEmailAddress: String = "" | |
var _onFinish: ((MFMailComposeResult, Error?) -> Void)? = nil | |
func onFinish(perform action: @escaping (MFMailComposeResult, Error?) -> Void) -> Self { | |
var modified = self | |
modified._onFinish = action | |
return modified | |
} | |
} | |
extension View { | |
func mailComposer(isPresented: Binding<Bool>, content: () -> MailComposer) -> some View { | |
self.modifier(MailComposeViewPresenter(isPresented: isPresented, mailComposer: content())) | |
} | |
} | |
private struct MailComposeViewPresenter: ViewModifier { | |
@Binding var isPresented: Bool | |
var mailComposer: MailComposer | |
func body(content: Content) -> some View { | |
if MFMailComposeViewController.canSendMail() { | |
content | |
.sheet(isPresented: $isPresented) { | |
MailComposeView(mailComposer: mailComposer) | |
.edgesIgnoringSafeArea(.all) | |
} | |
} else { | |
content | |
.alert(isPresented: $isPresented) { | |
Alert( | |
title: Text("Cannot Send Email"), | |
message: Text("Your device is not configured to send email.") | |
) | |
} | |
} | |
} | |
} | |
private struct MailComposeView: UIViewControllerRepresentable { | |
var mailComposer: MailComposer | |
@Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode> | |
func makeCoordinator() -> Coordinator { | |
return .init(representable: self) | |
} | |
func makeUIViewController(context: Context) -> UIViewController { | |
guard MFMailComposeViewController.canSendMail() else { | |
assertionFailure("Don't use `MailComposeView` directly and use `mailComposer(isPresented:content:)` instead.") | |
presentationMode.wrappedValue.dismiss() | |
return UIViewController() | |
} | |
let mailComposeViewController = MFMailComposeViewController() | |
mailComposeViewController.mailComposeDelegate = context.coordinator | |
// After presenting a mail compose view controller, the system ignores any attempts to modify the email using the methods of this class. | |
// The user can still edit the content of the email, but your app cannot. | |
// Therefore, always configure the fields of your email before presenting the view controller. | |
mailComposeViewController.setSubject(mailComposer.subject) | |
mailComposeViewController.setToRecipients(mailComposer.toRecipients) | |
mailComposeViewController.setCcRecipients(mailComposer.ccRecipients) | |
mailComposeViewController.setBccRecipients(mailComposer.bccRecipients) | |
mailComposeViewController.setMessageBody(mailComposer.messageBody.body, isHTML: mailComposer.messageBody.isHTML) | |
mailComposer.attachmentData.forEach { mailComposeViewController.addAttachmentData($0.data, mimeType: $0.mimeType, fileName: $0.fileName) } | |
mailComposeViewController.setPreferredSendingEmailAddress(mailComposer.preferredSendingEmailAddress) | |
return mailComposeViewController | |
} | |
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { | |
context.coordinator.representable = self | |
} | |
class Coordinator: NSObject, MFMailComposeViewControllerDelegate { | |
var representable: MailComposeView | |
init(representable: MailComposeView) { | |
self.representable = representable | |
} | |
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { | |
representable.mailComposer._onFinish?(result, error) | |
representable.presentationMode.wrappedValue.dismiss() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment