从哪里可以在Clean Architecture中展示MFMailComposeViewController?
你可以给出一些建议,在哪里可以放置MFMailComposeViewController
?从哪里可以在Clean Architecture中展示MFMailComposeViewController?
在非RxSwift和非清洁建筑项目,我会实现它在某些视图控制器,这样的:
extension ViewController: MFMailComposeViewControllerDelegate {
func presentMailComposer() {
if !MFMailComposeViewController.canSendMail() {
// TODO: - Handle error here
return
}
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.mailComposeDelegate = self
mailComposeViewController.setToRecipients(["[email protected]"])
mailComposeViewController.setMessageBody("Message body", isHTML: false)
DispatchQueue.main.async(execute: {
self.present(mailComposeViewController, animated: true, completion: nil)
})
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if result == MFMailComposeResult.failed {
// TODO: - Handle error here
}
}
}
在清洁建筑,在那里你会放在邮件作曲家?
你会从导航器/路由器中提出这个吗?毕竟它是一个“场景”,即使我们不一定有专用于MailComposer的导航器/路由器和ViewModel。
有2个不同的地方可能会发生错误,我真的不认为导航器应该处理这些。
谢谢!
Clean Architecture的基本前提是“业务规则”即应用程序的逻辑不依赖于UI,或由UI执行。相反,您的应用程序的逻辑是在控制之下。
这意味着应用程序的逻辑的某些部分知道当用户可以发送电子邮件,但不知道究竟如何该发生。
如果您使用RxSwift,您可以将用户交互视为模型转换。因此,您的示例将变为:
func sendMail(recipients: [String], tile: String, message: String, isHTML: Bool) -> Observable<Bool>
上面的代码可以作为闭包传递给您的逻辑,也可以嵌入您的逻辑使用的协议中。
如果你想使用罗伯特·马丁的具体结构,那么事情就有点不一样,因为你不会使用RX在你的模型对象都没有。 (他建议您的Interactors &等不依赖于外部库。)
在这种情况下,Interactor将向主讲者发送消息以通过响应模型对象显示电子邮件视图控制器,而Controller将会将成功/失败结果发送回Interactor,或更可能发送给不同的Interactor。
下面是Bob叔叔说他是如何构造的东西:https://camo.githubusercontent.com/c34f4ed0203238af6e43b44544b864dffac6bc08/687474703a2f2f692e696d6775722e636f6d2f576b42414154792e706e67但是,在他公开展示的一个iOS Swift应用中,他没有使用这种结构。https://github.com/unclebob/MACS_GOMOKU
为了详细说明您的评论后,签名的工作,但它需要一些支撑结构...
首先,一个不错的,但不是绝对必要的一块,我们做的视图控制器演示功:
extension Reactive where Base: UIViewController {
func present(_ viewControllerToPresent: UIViewController, animated: Bool) -> Observable<Void> {
return Observable.create { observer in
self.base.present(viewControllerToPresent, animated: animated, completion: {
observer.onNext()
observer.onCompleted()
})
return Disposables.create()
}
}
}
这不只是一个视图控制器只能由另一视图控制器呈现,而且它必须是当前未呈现任何系统中的一个视图控制器。我们可以通过从根开始,并走上了演讲堆栈发现视图控制器:
extension UIViewController {
static func top() -> UIViewController? {
var result = UIApplication.shared.delegate.flatMap { $0.window??.rootViewController }
while let child = result?.presentedViewController {
result = child
}
return result
}
}
现在,而不是有一些视图控制器符合MFMailComposeViewControllerDelegate
协议,我们做了专门的无类。
class MailComposeViewControllerDelegate: NSObject, UINavigationControllerDelegate, MFMailComposeViewControllerDelegate {
let subject = PublishSubject<MFMailComposeResult>()
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let error = error {
subject.onError(error)
}
else {
subject.onNext(result)
}
}
}
一旦所有这些作品都到位,写sendmail的功能很简单:
func sendMail(recipients: [String], tile: String, message: String, isHTML: Bool) -> Observable<MFMailComposeResult> {
let delegate = MailComposeViewControllerDelegate()
let controller = MFMailComposeViewController()
controller.delegate = delegate
return UIViewController.top()!.rx.present(controller, animated: true)
.flatMap { delegate.subject }
}
就像我说的,你应该不调用这个函数直接。相反,您应该将其注入到将调用它的对象中,以便您可以将其嘲笑为测试。
这种模式适用于UIImagePickerController,甚至UIAlertController!
你可能会发现这篇文章我写了一篇有趣的阅读。它使用承诺而不是Rx,但哲学是相同的:https://medium.com/@danielt1263/encapsulating-the-user-in-a-function-ec5e5c02045f
这取决于你决定管理你的项目的方式。
最终邮件撰写元素是一个UI元素,所以提出应该在UI处理类进行 - 比如你的VC,某种扩展名的喜欢你发等。
在我看来,是什么你可以做的是子类化你的邮件编辑器,并在它完成时创建一个完成块响应,然后在UI中相应地处理错误,这样它将自己管理(由于它是一个全局控制器,具有一个普通的VM因为这是浪费代码)。
然后,当您提交邮件编辑器时,您让用户添加完成和失败块/使用来自Rx的信令返回结果。
我不认为签名会起作用。我不知道如何。然而!关于MFMailComposeViewController的事情是它必须从现有的视图控制器启动。只有现有的视图控制器和我的路由器/交互器了解视图控制器。如果'sendMail'函数被用作一个用例,在我的域中定义为一个接口/协议,我不知道我该如何呈现它。现在去海滩吧!当我回来时我会再次选择你的答案。看起来很有希望。谢谢丹尼尔。 – nmdias
我添加到我的答案来解释如何实现该功能。 –
谢谢@Daniel。在此期间我试图在RxSwift中添加对MFMailComposeViewController的支持。我将把它留在这里作为参考。 https://github.com/ReactiveX/RxSwift/issues/1378 – nmdias