Im working on a simple swift app where the user inputs an email address and presses a button which opens the mail app, with the entered address in the address bar. I know how to do this in Objective-C, but I'm having trouble getting it to work in Swift.
You can use simple mailto: links in iOS to open the mail app.
let email = "[email protected]"
if let url = URL(string: "mailto:\(email)") {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
}
While other answers are all correct, you can never know if the iPhone/iPad that is running your application has the Apple's Mail app installed or not as it can be deleted by the user.
It is better to support multiple email clients. Following code handles the email sending in a more graceful way. The flow of the code is:
- If Mail app is installed, open Mail's composer pre-filled with provided data
- Otherwise, try opening the Gmail app, then Outlook, then Yahoo mail, then Spark, in this order
- If none of those clients are installed, fallback to default
mailto:..
that prompts the user to install Apple's Mail app.
Code is written in Swift 5:
import MessageUI
import UIKit
class SendEmailViewController: UIViewController, MFMailComposeViewControllerDelegate {
@IBAction func sendEmail(_ sender: UIButton) {
// Modify following variables with your text / recipient
let recipientEmail = "[email protected]"
let subject = "Multi client email support"
let body = "This code supports sending email via multiple different email apps on iOS! :)"
// Show default mail composer
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([recipientEmail])
mail.setSubject(subject)
mail.setMessageBody(body, isHTML: false)
present(mail, animated: true)
// Show third party email composer if default Mail app is not present
} else if let emailUrl = createEmailUrl(to: recipientEmail, subject: subject, body: body) {
UIApplication.shared.open(emailUrl)
}
}
private func createEmailUrl(to: String, subject: String, body: String) -> URL? {
let subjectEncoded = subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let gmailUrl = URL(string: "googlegmail://co?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
let outlookUrl = URL(string: "ms-outlook://compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
let yahooMail = URL(string: "ymail://mail/compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
let sparkUrl = URL(string: "readdle-spark://compose?recipient=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
let defaultUrl = URL(string: "mailto:\(to)?subject=\(subjectEncoded)&body=\(bodyEncoded)")
if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
return gmailUrl
} else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
return outlookUrl
} else if let yahooMail = yahooMail, UIApplication.shared.canOpenURL(yahooMail) {
return yahooMail
} else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
return sparkUrl
}
return defaultUrl
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
You also have to add following code to Info.plist
file that whitelists the URl query schemes that are used.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlegmail</string>
<string>ms-outlook</string>
<string>readdle-spark</string>
<string>ymail</string>
</array>
isHTML
in the above code to true. For other clients, it doesn't appear to be possible, for further reading see https://mcmap.net/q/54185/-mailto-link-with-html-body –
Morse I'm not sure if you want to switch to the mail app itself or just open and send an email. For the latter option linked to a button IBAction:
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
@IBAction func launchEmail(sender: AnyObject) {
var emailTitle = "Feedback"
var messageBody = "Feature request or bug report?"
var toRecipents = ["[email protected]"]
var mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.presentViewController(mc, animated: true, completion: nil)
}
func mailComposeController(controller:MFMailComposeViewController, didFinishWithResult result:MFMailComposeResult, error:NSError) {
switch result {
case MFMailComposeResultCancelled:
print("Mail cancelled")
case MFMailComposeResultSaved:
print("Mail saved")
case MFMailComposeResultSent:
print("Mail sent")
case MFMailComposeResultFailed:
print("Mail sent failure: \(error?.localizedDescription)")
default:
break
}
self.dismissViewControllerAnimated(true, completion: nil)
}
}
class myClass: UIViewController, MFMailComposeViewControllerDelegate {
–
Pulsation 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target
. App crashes in some devices (iPhone 5, iPhone 6 and iPad Mini) –
Jacquerie For Swift 4.2+ and iOS 9+
let appURL = URL(string: "mailto:[email protected]")!
if #available(iOS 10.0, *) {
UIApplication.shared.open(appURL, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(appURL)
}
Replace [email protected] with your desired email address.
You can also include a subject field, a message, and multiple recipients in the To, Cc, and Bcc fields:
mailto:[email protected][email protected]&subject=Greetings%20from%20Cupertino!&body=Wish%20you%20were%20here!
In Swift 3 you make sure to add import MessageUI
and needs conform to the MFMailComposeViewControllerDelegate
protocol.
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["[email protected]"])
mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)
present(mail, animated: true)
} else {
// show failure alert
}
}
Protocol:
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
Swift 2, with availability check:
import MessageUI
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["[email protected]"])
mail.setSubject("Bla")
mail.setMessageBody("<b>Blabla</b>", isHTML: true)
presentViewController(mail, animated: true, completion: nil)
} else {
print("Cannot send mail")
// give feedback to the user
}
// MARK: - MFMailComposeViewControllerDelegate
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Cancelled")
case MFMailComposeResultSaved.rawValue:
print("Saved")
case MFMailComposeResultSent.rawValue:
print("Sent")
case MFMailComposeResultFailed.rawValue:
print("Error: \(error?.localizedDescription)")
default:
break
}
controller.dismissViewControllerAnimated(true, completion: nil)
}
Here how it looks for Swift 4:
import MessageUI
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["[email protected]"])
mail.setSubject("Bla")
mail.setMessageBody("<b>Blabla</b>", isHTML: true)
present(mail, animated: true, completion: nil)
} else {
print("Cannot send mail")
// give feedback to the user
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
print("Cancelled")
case MFMailComposeResult.saved.rawValue:
print("Saved")
case MFMailComposeResult.sent.rawValue:
print("Sent")
case MFMailComposeResult.failed.rawValue:
print("Error: \(String(describing: error?.localizedDescription))")
default:
break
}
controller.dismiss(animated: true, completion: nil)
}
Here's an update for Swift 4 if you're simply looking to open up the mail client via a URL
:
let email = "[email protected]"
if let url = URL(string: "mailto:\(email)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
This worked perfectly fine for me :)
Updated answer from Stephen Groom for Swift 3
let email = "[email protected]"
let url = URL(string: "mailto:\(email)")
UIApplication.shared.openURL(url!)
This is a straight forward solution of 3 steps in Swift.
import MessageUI
Add to conform the Delegate
MFMailComposeViewControllerDelegate
And just create your method:
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["[email protected]"])
mail.setSubject("Support App")
mail.setMessageBody("<p>Send us your issue!</p>", isHTML: true)
presentViewController(mail, animated: true, completion: nil)
} else {
// show failure alert
}
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
You should try sending with built-in mail composer, and if that fails, try with share:
func contactUs() {
let email = "[email protected]" // insert your email here
let subject = "your subject goes here"
let bodyText = "your body text goes here"
// https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller
if MFMailComposeViewController.canSendMail() {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self as? MFMailComposeViewControllerDelegate
mailComposerVC.setToRecipients([email])
mailComposerVC.setSubject(subject)
mailComposerVC.setMessageBody(bodyText, isHTML: false)
self.present(mailComposerVC, animated: true, completion: nil)
} else {
print("Device not configured to send emails, trying with share ...")
let coded = "mailto:\(email)?subject=\(subject)&body=\(bodyText)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let emailURL = URL(string: coded!) {
if #available(iOS 10.0, *) {
if UIApplication.shared.canOpenURL(emailURL) {
UIApplication.shared.open(emailURL, options: [:], completionHandler: { (result) in
if !result {
print("Unable to send email.")
}
})
}
}
else {
UIApplication.shared.openURL(emailURL as URL)
}
}
}
}
For Swift 4.2 and above
let supportEmail = "[email protected]"
if let emailURL = URL(string: "mailto:\(supportEmail)"), UIApplication.shared.canOpenURL(emailURL)
{
UIApplication.shared.open(emailURL, options: [:], completionHandler: nil)
}
Give the user to choose many mail options(like iCloud, google, yahoo, Outlook.com - if no mail is pre-configured in his phone) to send email.
In the view controller from where you want your mail-app to open on the tap.
- At the top of the file do, import MessageUI.
Put this function inside your Controller.
func showMailComposer(){ guard MFMailComposeViewController.canSendMail() else { return } let composer = MFMailComposeViewController() composer.mailComposeDelegate = self composer.setToRecipients(["[email protected]"]) // email id of the recipient composer.setSubject("testing!!!") composer.setMessageBody("this is a test mail.", isHTML: false) present(composer, animated: true, completion: nil) }
Extend your View Controller and conform to the MFMailComposeViewControllerDelegate.
Put this method and handle the failure, sending of your mails.
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { if let _ = error { controller.dismiss(animated: true, completion: nil) return } controller.dismiss(animated: true, completion: nil) }
@IBAction func launchEmail(sender: AnyObject) {
if if MFMailComposeViewController.canSendMail() {
var emailTitle = "Feedback"
var messageBody = "Feature request or bug report?"
var toRecipents = ["[email protected]"]
var mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.present(mc, animated: true, completion: nil)
} else {
// show failure alert
}
}
func mailComposeController(controller:MFMailComposeViewController, didFinishWithResult result:MFMailComposeResult, error:NSError) {
switch result {
case .cancelled:
print("Mail cancelled")
case .saved:
print("Mail saved")
case .sent:
print("Mail sent")
case .failed:
print("Mail sent failure: \(error?.localizedDescription)")
default:
break
}
self.dismiss(animated: true, completion: nil)
}
Note that not all users have their device configure to send emails, which is why we need to check the result of canSendMail() before trying to send. Note also that you need to catch the didFinishWith callback in order to dismiss the mail window.
In my case I was just trying to open the mail app, without creating an email draft.
In case someone is stumbling upon this question and is actually trying to do the same thing here is the code I used:
private var openMailAppButton: some View {
Button {
if let emailUrl = mailAppUrl() {
UIApplication.shared.open(emailUrl)
}
} label: {
Text("OPEN MAIL APP")
}
}
private func mailAppUrl() -> URL? {
let gmailUrl = URL(string: "googlegmail://")
let outlookUrl = URL(string: "ms-outlook://")
let yahooMail = URL(string: "ymail://")
let sparkUrl = URL(string: "readdle-spark://")
let defaultUrl = URL(string: "message://")
if let defaultUrl = defaultUrl, UIApplication.shared.canOpenURL(defaultUrl) {
return defaultUrl
} else if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
return gmailUrl
} else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
return outlookUrl
} else if let yahooMail = yahooMail, UIApplication.shared.canOpenURL(yahooMail) {
return yahooMail
} else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
return sparkUrl
}
return defaultUrl
}
For those of us still lagging behind on Swift 2.3 here is Gordon's answer in our syntax:
let email = "[email protected]"
if let url = NSURL(string: "mailto:\(email)") {
UIApplication.sharedApplication().openURL(url)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult,error: Swift.Error?) {
controller.dismiss(animated: true, completion: nil)
}
all answer is great but for me i like this more
extension ContentUsVC : MFMailComposeViewControllerDelegate {
func sendEmail(emile:String) {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([emile])
present(mail, animated: true)
} else {
UIPasteboard.general.string = emile
self.view.makeToast(StaticString.copiedToClipboard[languageString],position: .top)
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
© 2022 - 2024 — McMap. All rights reserved.