0

你可以給出一些建議,在哪裏可以放置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個不同的地方可能會發生錯誤,我真的不認爲導航器應該處理這些。

謝謝!

回答

1

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

+0

我不認爲簽名會起作用。我不知道如何。然而!關於MFMailComposeViewController的事情是它必須從現有的視圖控制器啓動。只有現有的視圖控制器和我的路由器/交互器瞭解視圖控制器。如果'sendMail'函數被用作一個用例,在我的域中定義爲一個接口/協議,我不知道我該如何呈現它。現在去海灘吧!當我回來時我會再次選擇你的答案。看起來很有希望。謝謝丹尼爾。 – nmdias

+0

我添加到我的答案來解釋如何實現該功能。 –

+0

謝謝@Daniel。在此期間我試圖在RxSwift中添加對MFMailComposeViewController的支持。我將把它留在這裏作爲參考。 https://github.com/ReactiveX/RxSwift/issues/1378 – nmdias

0

這取決於你決定管理你的項目的方式。

最終郵件撰寫元素是一個UI元素,所以提出應該在UI處理類進行 - 比如你的VC,某種擴展名的喜歡你發等。

在我看來,是什麼你可以做的是子類化你的郵件編輯器,並在它完成時創建一個完成塊響應,然後在UI中相應地處理錯誤,這樣它將自己管理(由於它是一個全局控制器,具有一個普通的VM因爲這是浪費代碼)。

然後,當您提交郵件編輯器時,您讓用戶添加完成和失敗塊/使用來自Rx的信令返回結果。