Swift 5 - 电子邮件类助手/管理器
Posted
技术标签:
【中文标题】Swift 5 - 电子邮件类助手/管理器【英文标题】:Swift 5 - Email Class Helper / Manager 【发布时间】:2020-06-28 01:03:16 【问题描述】:编辑:
非常感谢 Paulw11 帮助我解决了这个问题。我在这里添加了完整的代码以便于重用:
类:
import UIKit
import MessageUI
struct Feedback
let recipients: [String]
let subject: String
let body: String
let footer: String
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init()
fatalError("Use FeedbackManager(feedback:)")
init?(feedback: Feedback)
guard MFMailComposeViewController.canSendMail() else
return nil
self.feedback = feedback
func send(on viewController: UIViewController, completion:(@escaping(Result<MFMailComposeResult,Error>)->Void))
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients(feedback.recipients)
mailVC.setSubject(feedback.subject)
mailVC.setMessageBody("<p>\(feedback.body)<br><br><br><br><br>\(feedback.footer)</p>", ishtml: true)
viewController.present(mailVC, animated:true)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
if let error = error
completion?(.failure(error))
controller.dismiss(animated: true)
else
completion?(.success(result))
controller.dismiss(animated: true)
在视图控制器中:
添加变量:
var feedbackManager: FeedbackManager?
用途:
let feedback = Feedback(recipients: "String", subject: "String", body: "Body", footer: "String")
if let feedManager = FeedbackManager(feedback: feedback)
self.feedbackManager = feedManager
self.feedbackManager?.send(on: self) [weak self] result in
switch result
case .failure(let error):
print("error: ", error.localizedDescription)
// Do something with the error
case .success(let mailResult):
print("Success")
// Do something with the result
self?.feedbackManager = nil
else // Cant Send Email: // Added UI Alert:
let failedMenu = UIAlertController(title: "String", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "String", style: .default)
failedMenu.addAction(okAlert)
present(failedMenu, animated: true)
我正在尝试创建一个类来处理初始化 MFMailComposeViewController 以在应用程序内部发送电子邮件。
我在使它工作时遇到了问题。好吧,如果它不起作用工作,不如让它不会崩溃。
类:
import UIKit
import MessageUI
struct Feedback
let recipients = "String"
let subject: String
let body: String
class FeedbackManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate
func sendEmail(feedback: Feedback)
if MFMailComposeViewController.canSendMail()
self.mailComposeDelegate = self
self.setToRecipients([feedback.recipients])
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
else
print("else:")
mailFailed()
func mailFailed()
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
self.present(failedMenu, animated: true, completion: nil)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
controller.dismiss(animated: true)
然后从不同的视图控制器调用它:
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(feedback: Feedback(subject: "String", body: "String"))
self.present(feedbackManager, animated: true, completion: nil)
tableView.deselectRow(at: indexPath, animated: true)
如果 MFMailComposeViewController.canSendMail() == true,上面的工作就很好。我面临的问题是,如果 canSendMail() 不正确,那么该类显然无法初始化并崩溃。这是有道理的。
错误:
Unable to initialize due to + [MFMailComposeViewController canSendMail] returns NO.
我不知道从这里开始如何让它发挥作用。我尝试将 FeedbackManager 从 MFMailComposeViewController 更改为 UIViewController。这似乎可行,但因为它在堆栈上添加了一个视图,所以会导致奇怪的图形显示。
我可以做的另一件事是导入 MessageUI,并为我希望能够从中发送电子邮件的每个控制器符合 MFMailComposeViewController。这样我就可以在尝试初始化 FeedbackManager() 之前检查 canSendMail()。但这似乎也不是最好的答案。
我还能如何让它工作?
编辑: 我已经得到了代码来处理这个问题,但是在它呈现 MFMailComposeViewController 之前,将视图添加到堆栈上会有一个丑陋的过渡。
class FeedbackManager: UIViewController, MFMailComposeViewControllerDelegate
func sendEmail(feedback: Feedback, presentingViewController: UIViewController) -> UIViewController
if MFMailComposeViewController.canSendMail()
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
present(mail, animated: true)
return self
else
print("else:")
return mailFailed(presentingViewController: presentingViewController)
func mailFailed(presentingViewController: UIViewController) -> UIViewController
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
controller.dismiss(animated: true)
self.dismiss(animated: false)
【问题讨论】:
您没有为您的班级展示初始化程序,我认为这是消息来自的地方,但我建议您将其设为可失败的初始化程序并根据需要处理失败,或者你可以让sendEmail
返回false
。无论哪种方式,试图从FeedbackManager
内部发出警报都不是正确的方法
嘿保罗,我想我初始化了它?在不同的视图控件中使用 let feedbackManager = FeedbackManager()。您是正确的,在此类中显示警报是错误的方法。我已经尝试了各种方法,包括将 UIViewControllers 返回到呈现控制器。但无论我尝试什么,我都可以让一个工作正常。但不是两者兼而有之。
@Paulw11 - 重新阅读您的评论后,错误是由于该类是 MFMailComposeViewController 的子类而不是 UIViewController。我的问题是,如果它是 MFMailComposeViewController,则该类可以发送电子邮件。但是这样做,如果 canSendMail = false,它就会崩溃。 - 如果我改用 UIViewController,它会失败而不会崩溃,它会显示 MFMailComposeViewController 视图,但代理不会注册,因此发送/取消不会关闭视图。
你的问题是你在继承MFMailViewComposeViewController
;你不应该继承那个类。它旨在按原样使用。
【参考方案1】:
子类化MFMailComposeViewController
是错误的方法。此类旨在“按原样”使用。如果你愿意,你可以构建一个包装类:
struct Feedback
let recipients = "String"
let subject: String
let body: String
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init()
fatalError("Use FeedbackManager(feedback:)")
init?(feedback: Feedback)
guard MFMailComposeViewController.canSendMail() else
return nil
self.feedback = feedback
func send(on viewController: UIViewController, completion:(@escaping(Result<MFMailComposeResult,Error>)->Void))
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients([feedback.recipients])
mailVC.setSubject("Feedback: \(feedback.subject)")
mailVC.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
viewController.present(mailVC, animated:true)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
if let error = error
completion?(.failure(error))
else
completion?(.success(result))
然后从视图控制器中使用它:
let feedback = Feedback(subject: "String", body: "Body")
if let feedbackMgr = FeedbackManager(feedback: feedback)
self.feedbackManager = feedbackMgr
feedback.send(on: self) [weak self], result in
switch result
case .failure(let error):
// Do something with the error
case .success(let mailResult):
// Do something with the result
self.feedbackManager = nil
else
// Can't send email
您需要在属性中保持对FeedbackManager
的强引用,否则它将在包含函数退出时立即释放。我上面的代码引用了一个属性
var feedbackManager: FeedbackManager?
虽然这可行,但更好的用户体验是直接检查 canSendMail
并禁用/隐藏允许他们发送反馈的 UI 组件
【讨论】:
嗨,保罗,谢谢您的回复。我试了一下。除了 mailComposeController 委托之外的所有东西似乎都在工作。 - 我不明白包装器是什么。 (实际上,今天早些时候才第一次读到它们。所以它在明天的学习清单上)但是代表为你工作吗? 我已经通过将打印语句放在每个方法的顶部进行了一些测试,并且似乎没有调用委托方法。 - 这是我一直遇到的同样问题。这就是为什么我在另一个答案中将 MFMailComposeViewController 子类化。因为设置 self.mailComposeDelegate = self 然后呈现 self 是我唯一能够弄清楚如何通过委托获得两个视图的方法。我不知道如何让委托函数正确触发。 嗨,保罗,感谢您的更新。我让它工作了!谢谢! :) 嗨,保罗,感谢您将这些放在一起。当我从 UIAlertViewController 调用代理 didFinish 时,它似乎对我不起作用,有什么想法吗?我也传递了alertvc的父vc。【参考方案2】:通过首先添加一个检查 .canSendMail 是否为真的类来解决这个问题。如果是,则它会进入邮政发送类以呈现 MFMailComposeViewController。
这是我想出的唯一解决方法,它允许 MFMailComposeViewController 成为它自己的 MFMailComposeViewControllerDelegate。如果 .canSendMail = false 也可以防止崩溃。
import UIKit
import MessageUI
struct Feedback
let recipients = ["Strings"]
let subject: String
let body: String
class FeedbackManager
func tryMail() -> Bool
if MFMailComposeViewController.canSendMail()
return true
else
return false
func mailFailed() -> UIViewController
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
class PostalManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate
func sendEmail(feedback: Feedback) -> MFMailComposeViewController
if MFMailComposeViewController.canSendMail()
self.mailComposeDelegate = self
self.setToRecipients(feedback.recipients)
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
return self
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
controller.dismiss(animated: true)
调用:
let feedbackManager = FeedbackManager()
let feedback = Feedback(subject: "String", body: "Body")
switch feedbackManager.tryMail()
case true:
let postalManager = PostalManager()
present(postalManager.sendEmail(feedback: feedback), animated: true)
case false:
present(feedbackManager.mailFailed(), animated: true)
【讨论】:
【参考方案3】:您可以按如下方式更改代码。
struct Feedback
let recipients = "String"
let subject: String
let body: String
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate
func sendEmail(presentingViewController: UIViewController))
if MFMailComposeViewController.canSendMail()
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
presentingViewController.present(mail, animated: true)
else
print("else:")
mailFailed(presentingViewController: presentingViewController)
func mailFailed(presentingViewController: UIViewController)
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
presentingViewController.present(failedMenu, animated: true, completion: nil)
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?)
controller.dismiss(animated: true)
现在,mailComposer 可以从另一个UIViewController
类打开,如下所示。
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(presentingViewController: self)
希望对你有帮助
【讨论】:
这就是我想要实现的目标。但是我遇到的问题是,如果按上述方式设置类,那么如果 canSendMail = false,它就会崩溃。但是将子类设置为 UIViewController 允许 UIAlertController 正确返回。并提出 MFMailComposeViewController。但是没有设置委托。所以虽然它会发送一封电子邮件,但视图不会关闭。 我在我的项目中使用了上面的代码。它可以在没有任何崩溃的情况下工作。对于解雇不起作用,请参阅反馈管理器实例实际上是一个实例变量而不是局部变量。 上面的确切代码对我不起作用。 - 我有它直接代码,它提出了一个错误。 不能在 Swift 中声明符合“NSObjectProtocol”; “FeedbackManager”应该继承“NSObject”。 - UIViewController 的子类化修复它。但是,如果 canSendMail = false 并且委托协议不起作用,它仍然会崩溃。以上是关于Swift 5 - 电子邮件类助手/管理器的主要内容,如果未能解决你的问题,请参考以下文章
csharp 用C#编写的简单SMTP邮件客户端助手类,用于异步发送电子邮件。注意:使用Log4Net进行日志记录。
如何在控制器或视图之外使用 Zend Framework 部分视图助手?
使用Laravel发送电子邮件错误“类Swift_Transport_Esmtp_AuthHandler不存在”