Как открыть почтовое приложение из Swift

119

Я работаю над простым быстрым приложением, в котором пользователь вводит адрес электронной почты и нажимает кнопку, которая открывает почтовое приложение с введенным адресом в адресной строке. Я знаю, как это сделать в Objective-C, но мне не удается заставить его работать в Swift.

Jesse.H
источник

Ответы:

241

Вы можете использовать простые ссылки mailto: в iOS, чтобы открыть почтовое приложение.

let email = "foo@bar.com"
if let url = URL(string: "mailto:\(email)") {
  if #available(iOS 10.0, *) {
    UIApplication.shared.open(url)
  } else {
    UIApplication.shared.openURL(url)
  }    
}
Стивен Грум
источник
77
Возможно, стоит добавить, что это не работает в симуляторе, только на устройстве ... См. Stackoverflow.com/questions/26052815/…
Питер
4
теперь вам нужно добавить "!" во второй строке для NSURL NSURL (строка: "mailto: (email)")!
anthonyqz
4
почему он сказал , что это доступно только на КСН 10 или новее , когда ответ ясно 3 лет старого
Pete
1
Пример Swift 4 / iOS 10+: UIApplication.shared.open (url, options: [:], completedHandler: nil). Передача пустого словаря для параметров дает тот же результат, что и вызов openURL.
Лука Вентура
Спасибо ... Это очень помогает :) :)
Анджали Джаривала
62

Хотя все остальные ответы верны, вы никогда не узнаете, установлено ли на iPhone / iPad, на котором запущено ваше приложение, приложение Apple Mail или нет, поскольку оно может быть удалено пользователем.

Лучше поддерживать несколько почтовых клиентов. Следующий код обрабатывает отправку электронной почты более изящным способом. Поток кода:

  • Если установлено приложение Mail, откройте редактор Mail, предварительно заполненный предоставленными данными.
  • В противном случае попробуйте открыть приложение Gmail, затем Outlook, затем почту Yahoo, а затем Spark в этом порядке.
  • Если ни один из этих клиентов не установлен, выберите вариант по умолчанию mailto:.., при котором пользователю будет предложено установить приложение Apple Mail.

Код написан на Swift 5 :

    import MessageUI
    import UIKit

    class SendEmailViewController: UIViewController, MFMailComposeViewControllerDelegate {

        @IBAction func sendEmail(_ sender: UIButton) {
            // Modify following variables with your text / recipient
            let recipientEmail = "test@email.com"
            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: .urlHostAllowed)!
            let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!

            let gmailUrl = URL(string: "googlegmail://co?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
            let outlookUrl = URL(string: "ms-outlook://compose?to=\(to)&subject=\(subjectEncoded)")
            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)
        }
    }

Обратите внимание, что я намеренно пропустил тело приложения Outlook, поскольку оно не может его проанализировать.

Вы также должны добавить в Info.plistфайл следующий код, который заносит в белый список используемые схемы запросов URl.

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>googlegmail</string>
    <string>ms-outlook</string>
    <string>readdle-spark</string>
    <string>ymail</string>
</array>
WebMajstr
источник
4
Отлично сработано. Это наиболее полный ответ, который легко расширяется для других клиентских приложений электронной почты. ИМХО, я не думаю, что в конце 2019 года приемлемо просто сказать человеку «извините, вам не повезло», если он не использует приложение Apple Mail по умолчанию, как предлагают большинство других решений. Это устраняет этот недостаток.
wildcat12
Этот метод работает с HTML? Я не могу правильно отобразить.
Мэтью Брэдшоу,
@MatthewBradshaw, вы можете поддерживать HTML для составителя почты по умолчанию, установив isHTMLв приведенном выше коде значение true. Для других клиентов, это не представляется возможным, для дальнейшего чтения см stackoverflow.com/questions/5620324/mailto-link-with-html-body
WebMajstr
1
Спасибо, все отлично. Я немного изменил его, чтобы пользователь мог выбирать клиента по своему вкусу (я заранее фильтрую их с помощью canOpenUrl). Кстати, тело для Microsoft Outlook работает нормально :-)
Филип
Это великолепно! Кто-нибудь делал это для SwiftUI?
Averett
55

Я не уверен, хотите ли вы переключиться на само почтовое приложение или просто открыть и отправить электронное письмо. Для последнего варианта, связанного с кнопкой 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 = ["friend@stackoverflow.com"]
    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)
    }

    }
Стив Розенберг
источник
1
У меня возникают проблемы, когда функция делегата mailComposeController не вызывается.
AustinT
3
Добавьте «import MessageUI» к своему импорту и обязательно добавьте параметр «MFMailComposeViewControllerDelegate» в объявление своего класса, например: class myClass: UIViewController, MFMailComposeViewControllerDelegate {
Jalakoo
MFMailComposeViewController () возвращает мне ноль
Илан
2
Также возникают вопросы: 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target. Приложение вылетает на некоторых устройствах (iPhone 5, iPhone 6 и iPad Mini)
Spacemonkey
23

В Swift 3 вы обязательно добавляете import MessageUIи должны соответствовать MFMailComposeViewControllerDelegateпротоколу.

func sendEmail() {
  if MFMailComposeViewController.canSendMail() {
    let mail = MFMailComposeViewController()
    mail.mailComposeDelegate = self
    mail.setToRecipients(["ved.ios@yopmail.com"])
    mail.setMessageBody("<p>You're so awesome!</p>", isHTML: true)

    present(mail, animated: true)
  } else {
    // show failure alert
  }
}

Протокол:

func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
  controller.dismiss(animated: true)
}
Вед Раунияр
источник
17

Для Swift 4.2+ и iOS 9+

let appURL = URL(string: "mailto:TEST@EXAMPLE.COM")!

if #available(iOS 10.0, *) {
    UIApplication.shared.open(appURL, options: [:], completionHandler: nil)
} else {
    UIApplication.shared.openURL(appURL)
}

Замените TEST@EXAMPLE.COM желаемым адресом электронной почты.

Mehdico
источник
16

Swift 2, с проверкой доступности :

import MessageUI

if MFMailComposeViewController.canSendMail() {
    let mail = MFMailComposeViewController()
    mail.mailComposeDelegate = self
    mail.setToRecipients(["test@test.test"])
    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)
}
Ixx
источник
15

Вот как это выглядит для Swift 4:

import MessageUI

if MFMailComposeViewController.canSendMail() {
    let mail = MFMailComposeViewController()
    mail.mailComposeDelegate = self
    mail.setToRecipients(["test@test.test"])
    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)
    }
Юваль
источник
12

Обновленный ответ Стивена Грума для Swift 3

let email = "email@email.com"
let url = URL(string: "mailto:\(email)")
UIApplication.shared.openURL(url!)
Бен В
источник
10

Вот обновление для Swift 4, если вы просто хотите открыть почтовый клиент через URL:

let email = "foo@bar.com"
if let url = URL(string: "mailto:\(email)") {
   UIApplication.shared.open(url, options: [:], completionHandler: nil)
}

Это отлично сработало для меня :)

Нии Манце
источник
9

Это простое решение из трех шагов в Swift.

import MessageUI

Добавить в соответствие с делегатом

MFMailComposeViewControllerDelegate

И просто создайте свой метод:

    func sendEmail() {
    if MFMailComposeViewController.canSendMail() {
        let mail = MFMailComposeViewController()
        mail.mailComposeDelegate = self
        mail.setToRecipients(["support@mail.com"])
        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)
}
Мария Ортега
источник
4

Вы должны попробовать отправить с помощью встроенного почтового композитора, а если это не удастся, попробуйте поделиться:

func contactUs() {

    let email = "info@example.com" // 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)
            }
        }
    }
}
lenooh
источник
ошибка: «Этому приложению не разрешено запрашивать схему mailto»
Хушал iOS
3
@IBAction func launchEmail(sender: AnyObject) {
 if if MFMailComposeViewController.canSendMail() {
   var emailTitle = "Feedback"
   var messageBody = "Feature request or bug report?"
   var toRecipents = ["friend@stackoverflow.com"]
   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)
}

Обратите внимание, что не все пользователи настроили свое устройство для отправки электронных писем, поэтому нам нужно проверить результат canSendMail () перед попыткой отправки. Также обратите внимание, что вам нужно поймать обратный вызов didFinishWith, чтобы закрыть почтовое окно.

Нишал Соланки
источник
1

В контроллере представления, откуда вы хотите, чтобы ваше почтовое приложение открывалось при нажатии.

  • В верхней части файла импортируйте MessageUI .
  • Поместите эту функцию в свой контроллер.

    func showMailComposer(){
    
      guard MFMailComposeViewController.canSendMail() else {
           return
      }
      let composer = MFMailComposeViewController()
      composer.mailComposeDelegate = self
      composer.setToRecipients(["abc@gmail.com"]) // email id of the recipient
      composer.setSubject("testing!!!")
      composer.setMessageBody("this is a test mail.", isHTML: false)
      present(composer, animated: true, completion: nil)
     }
  • Расширьте свой контроллер представления и выполните MFMailComposeViewControllerDelegate .

  • Поставьте этот метод и обработайте сбой, отправив вашу почту.

    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)
    }
Шив Пракаш
источник
0

Для тех из нас, кто все еще отстает от Swift 2.3, вот ответ Гордона в нашем синтаксисе:

let email = "foo@bar.com"
if let url = NSURL(string: "mailto:\(email)") {
   UIApplication.sharedApplication().openURL(url)
}
Пол Лен
источник
0

Для Swift 4.2 и выше

let supportEmail = "abc@xyz.com"
if let emailURL = URL(string: "mailto:\(supportEmail)"), UIApplication.shared.canOpenURL(emailURL)
{
    UIApplication.shared.open(emailURL, options: [:], completionHandler: nil)
}

Предоставьте пользователю возможность выбрать множество вариантов электронной почты (например, iCloud, google, yahoo, Outlook.com - если в его телефоне предварительно не настроена почта) для отправки электронной почты.

iHarshil
источник
1
В моем случае с iOS 13 при вызове UIApplication.shared.open ОС всегда показывала диалоговое окно с предложением установить Mail.app (о, и canOpenURL для «mailto» тоже всегда верно), даже если есть другие почтовые приложения. Так что это точно не сработает.
NeverwinterMoon