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
我不認爲簽名會起作用。我不知道如何。然而!關於MFMailComposeViewController的事情是它必須從現有的視圖控制器啓動。只有現有的視圖控制器和我的路由器/交互器瞭解視圖控制器。如果'sendMail'函數被用作一個用例,在我的域中定義爲一個接口/協議,我不知道我該如何呈現它。現在去海灘吧!當我回來時我會再次選擇你的答案。看起來很有希望。謝謝丹尼爾。 – nmdias
我添加到我的答案來解釋如何實現該功能。 –
謝謝@Daniel。在此期間我試圖在RxSwift中添加對MFMailComposeViewController的支持。我將把它留在這裏作爲參考。 https://github.com/ReactiveX/RxSwift/issues/1378 – nmdias