sending email with SwiftUI
You can use UIViewControllerRepresentable
MailComposeViewController
struct MailComposeViewController: UIViewControllerRepresentable {
var toRecipients: [String]
var mailBody: String
var didFinish: ()->()
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MailComposeViewController>) -> MFMailComposeViewController {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = context.coordinator
mail.setToRecipients(self.toRecipients)
mail.setMessageBody(self.mailBody, isHTML: true)
return mail
}
final class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
var parent: MailComposeViewController
init(_ mailController: MailComposeViewController) {
self.parent = mailController
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
parent.didFinish()
controller.dismiss(animated: true)
}
}
func updateUIViewController(_ uiViewController: MFMailComposeViewController, context: UIViewControllerRepresentableContext<MailComposeViewController>) {
}
}
Usage:
struct MailView: View {
@State private var showingMail = false
var body: some View {
VStack {
Button("Open Mail") {
self.showingMail.toggle()
}
}
.sheet(isPresented: $showingMail) {
MailComposeViewController(toRecipients: ["test@gmail.com"], mailBody: "Here is mail body") {
// Did finish action
}
}
}
}
Possible another solution. You can create one singleton class and present MFMailComposeViewController
on the root controller. You can modify function as per your requirement. Like this
class MailComposeViewController: UIViewController, MFMailComposeViewControllerDelegate {
static let shared = MailComposeViewController()
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["you@yoursite.com"])
mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)
UIApplication.shared.windows.first?.rootViewController?.present(mail, animated: true)
} else {
// show failure alert
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
Usage:
Button(action: {
MailComposeViewController.shared.sendEmail()
}, label: {
Text("Send")
})
SwiftUI: Send email
As you mentioned, you need to port the component to SwiftUI
via UIViewControllerRepresentable
.
Here's a simple implementation:
struct MailView: UIViewControllerRepresentable {
@Binding var isShowing: Bool
@Binding var result: Result<MFMailComposeResult, Error>?
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
@Binding var isShowing: Bool
@Binding var result: Result<MFMailComposeResult, Error>?
init(isShowing: Binding<Bool>,
result: Binding<Result<MFMailComposeResult, Error>?>) {
_isShowing = isShowing
_result = result
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
defer {
isShowing = false
}
guard error == nil else {
self.result = .failure(error!)
return
}
self.result = .success(result)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing,
result: $result)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
let vc = MFMailComposeViewController()
vc.mailComposeDelegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: MFMailComposeViewController,
context: UIViewControllerRepresentableContext<MailView>) {
}
}
Usage:
struct ContentView: View {
@State var result: Result<MFMailComposeResult, Error>? = nil
@State var isShowingMailView = false
var body: some View {
VStack {
if MFMailComposeViewController.canSendMail() {
Button("Show mail view") {
self.isShowingMailView.toggle()
}
} else {
Text("Can't send emails from this device")
}
if result != nil {
Text("Result: \(String(describing: result))")
.lineLimit(nil)
}
}
.sheet(isPresented: $isShowingMailView) {
MailView(isShowing: self.$isShowingMailView, result: self.$result)
}
}
}
(Tested on iPhone 7 Plus running iOS 13 - works like a charm)
Updated for Xcode 11.4
SwiftUI: Send email using MFMailComposeViewController
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.thisIsTest() {
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.
Send email using Default email provider - SwiftUI
iOS 14.0+ | Swift 5.3
On iOS 14.0; Apple allows iOS users to choose a default mail app in Settings
Users can choose their default email provider in settings, and all you have to do to compose an email would be:
let mailTo = "mailto:nemecek@support.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let mailtoUrl = URL(string: mailto!)!
if UIApplication.shared.canOpenURL(mailtoUrl) {
UIApplication.shared.open(mailtoUrl, options: [:])
}
It needs to be percent-encoded with the addingPercentEncoding
and then we would create an URL from this and open it with UIApplication.shared.open
.
We can also use standard URL parameters to customize the mailto
url. First parameter is denoted with ?
and then others are chained with &
.
We can add a subject and body like this:
let mailTo = "mailto:nemecek@support.com?subject=Cool app feedback&body=Hello I have an issue...".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
Related Topics
Load a Uiview from Nib in Swift
Overriding Superclass Property With Different Type in Swift
Swift Optional Escaping Closure Parameter
How to Change the Textual Representation Displayed For a Type in Swift
How to Declare an Array of Weak References in Swift
Dynamically Hiding View in Swiftui
Using Decodable in Swift 4 With Inheritance
Accessing an Enumeration Association Value in Swift
All Dates Between Two Date Objects (Swift)
Strange Swift Numbers Type Casting
Swift 3 For Loop With Increment
How to Encode Enum Using Nscoder in Swift
Go to a New View Using Swiftui
Firestore: How to Get Random Documents in a Collection