Jak otworzyć aplikację pocztową z Swift

119

Pracuję nad prostą szybką aplikacją, w której użytkownik wprowadza adres e-mail i naciska przycisk, który otwiera aplikację pocztową, z wpisanym adresem w pasku adresu. Wiem, jak to zrobić w Objective-C, ale mam problem z uruchomieniem go w Swift.

Jesse.H
źródło

Odpowiedzi:

241

Możesz użyć prostych linków mailto: w iOS, aby otworzyć aplikację pocztową.

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)
  }    
}
Stephen Groom
źródło
77
Może warto dodać, że to nie działa w symulatorze, tylko na urządzeniu ... Zobacz stackoverflow.com/questions/26052815/…
Pieter
4
teraz musisz dodać „!” w drugim wierszu dla NSURL NSURL (string: "mailto: (email)")!
anthonyqz
4
dlaczego jest napisane, że jest to dostępne tylko na iOS 10 lub nowszym, gdy odpowiedź jest wyraźnie sprzed 3 lat
pete
1
Przykład języka Swift 4 / iOS 10+: UIApplication.shared.open (url, options: [:], completeHandler: nil) Przekazanie pustego słownika opcji daje taki sam wynik, jak wywołanie openURL.
Luca Ventura
Dzięki ... To bardzo pomaga :) :)
Anjali jariwala
62

Podczas gdy inne odpowiedzi są poprawne, nigdy nie możesz wiedzieć, czy iPhone / iPad, na którym działa Twoja aplikacja, ma zainstalowaną aplikację Apple Mail, czy nie, ponieważ może ją usunąć użytkownik.

Lepiej jest obsługiwać wielu klientów poczty e-mail. Poniższy kod obsługuje wysyłanie wiadomości e-mail w bardziej elegancki sposób. Przepływ kodu to:

  • Jeśli zainstalowano aplikację Mail, otwórz edytor Mail, który jest wstępnie wypełniony podanymi danymi
  • W przeciwnym razie spróbuj otworzyć aplikację Gmail, następnie Outlook, następnie Yahoo Mail i Spark, w tej kolejności
  • Jeśli żaden z tych klientów nie jest zainstalowany, powróć do wartości domyślnej, mailto:..która monituje użytkownika o zainstalowanie aplikacji Apple Mail.

Kod jest napisany w języku 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: .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)
        }
    }

Pamiętaj, że celowo przegapiłem treść aplikacji Outlook, ponieważ nie jest ona w stanie jej przeanalizować.

Musisz również dodać następujący kod do Info.plistpliku, który umieszcza na białej liście schematy zapytań URl, które są używane.

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>googlegmail</string>
    <string>ms-outlook</string>
    <string>readdle-spark</string>
    <string>ymail</string>
</array>
WebMajstr
źródło
4
Dobra robota. To jest najbardziej kompletna odpowiedź i można ją łatwo rozszerzyć na inne aplikacje klientów poczty e-mail. IMHO, nie sądzę, aby pod koniec 2019 roku po prostu powiedzieć osobie „przepraszam, nie masz szczęścia”, jeśli nie używa domyślnej aplikacji Apple Mail, jak sugeruje większość innych rozwiązań. To naprawia ten brak.
wildcat12
Czy ta metoda działa z HTML? Nie mogę wyświetlić go poprawnie.
Matthew Bradshaw
@MatthewBradshaw możesz obsługiwać HTML dla domyślnego kompozytora poczty, ustawiając isHTMLw powyższym kodzie na true. W przypadku innych klientów wydaje się, że nie jest to możliwe, więcej informacji można znaleźć na stronie stackoverflow.com/questions/5620324/mailto-link-with-html-body
WebMajstr
1
Dzięki, to działa świetnie. Zmodyfikowałem go nieznacznie, aby umożliwić użytkownikowi wybranie klienta według jego preferencji (filtruję je z wyprzedzeniem za pomocą canOpenUrl). Btw body dla Microsoft Outlooka działa dobrze :-)
Filip
To jest genialne! Czy ktoś zrobił to dla SwiftUI?
Averett
55

Nie jestem pewien, czy chcesz przełączyć się na samą aplikację pocztową, czy po prostu otworzyć i wysłać wiadomość e-mail. W przypadku drugiej opcji połączonej z przyciskiem 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)
    }

    }
Steve Rosenberg
źródło
1
Mam problemy, gdy funkcja delegata mailComposeController nie jest wywoływana.
AustinT,
3
Dodaj „import MessageUI” do swoich importów i pamiętaj o dodaniu opcji „MFMailComposeViewControllerDelegate” do deklaracji klasy, np .: class myClass: UIViewController, MFMailComposeViewControllerDelegate {
Jalakoo,
MFMailComposeViewController () return nil for me
ilan
2
Mając również kwestie: 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target. Awarie aplikacji na niektórych urządzeniach (iPhone 5, iPhone 6 i iPad Mini)
Spacemonkey
23

W Swift 3 upewnij się, że dodajesz import MessageUIi musisz być zgodny z MFMailComposeViewControllerDelegateprotokołem.

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
  }
}

Protokół:

func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
  controller.dismiss(animated: true)
}
Ved Rauniyar
źródło
17

Dla Swift 4.2+ i 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)
}

Zastąp [email protected] żądanym adresem e-mail.

Mehdico
źródło
16

Swift 2, ze sprawdzeniem dostępności :

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)
}
Ixx
źródło
15

Oto jak to wygląda dla 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)
    }
Yuval
źródło
12

Zaktualizowana odpowiedź od Stephena Grooma dla Swift 3

let email = "[email protected]"
let url = URL(string: "mailto:\(email)")
UIApplication.shared.openURL(url!)
Ben W.
źródło
10

Oto aktualizacja dla Swift 4, jeśli po prostu chcesz otworzyć klienta poczty za pomocą URL:

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

To działało idealnie dla mnie :)

Nii Mantse
źródło
9

To proste rozwiązanie 3 kroków w Swift.

import MessageUI

Dodaj, aby dostosować delegata

MFMailComposeViewControllerDelegate

I po prostu stwórz swoją metodę:

    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)
}
Maria Ortega
źródło
4

Powinieneś spróbować wysłać za pomocą wbudowanego edytora poczty, a jeśli to się nie powiedzie, spróbuj z 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)
            }
        }
    }
}
lenooh
źródło
błąd: „Ta aplikacja nie może
wysyłać
3
@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)
}

Zwróć uwagę, że nie wszyscy użytkownicy mają skonfigurowane urządzenie do wysyłania e-maili, dlatego przed próbą wysłania musimy sprawdzić wynik canSendMail (). Zauważ również, że musisz złapać wywołanie zwrotne didFinishWith, aby zamknąć okno poczty.

Nishal Solanki
źródło
1

W kontrolerze widoku, z którego chcesz, aby aplikacja pocztowa otwierała się po dotknięciu.

  • W górnej części pliku zaimportuj MessageUI .
  • Umieść tę funkcję w swoim kontrolerze.

    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)
     }
  • Rozszerz swój kontroler widoku i dostosuj się do MFMailComposeViewControllerDelegate .

  • Skorzystaj z tej metody i poradzić sobie z niepowodzeniem, wysyłaniem wiadomości.

    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)
    }
Shiv Prakash
źródło
0

Dla tych z nas, którzy wciąż pozostają w tyle w Swift 2.3, oto odpowiedź Gordona w naszej składni:

let email = "[email protected]"
if let url = NSURL(string: "mailto:\(email)") {
   UIApplication.sharedApplication().openURL(url)
}
Paul Lehn
źródło
0

Dla Swift 4.2 i nowszych

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

Daj użytkownikowi możliwość wyboru wielu opcji poczty (takich jak iCloud, google, yahoo, Outlook.com - jeśli żadna poczta nie jest wstępnie skonfigurowana w jego telefonie) do wysyłania wiadomości e-mail.

iHarshil
źródło
1
W moim przypadku w iOS 13 podczas wywoływania UIApplication.shared.open system operacyjny zawsze pokazywał okno dialogowe z ofertą instalacji Mail.app (och, i canOpenURL dla „mailto” też jest zawsze prawdziwe), nawet jeśli są inne aplikacje pocztowe. Więc to zdecydowanie nie działa.
NeverwinterMoon