【问题标题】:Proper way of setting delegates in MVVM在 MVVM 中设置委托的正确方法
【发布时间】:2018-10-21 05:06:40
【问题描述】:

我想知道在 Swift 的 MVVM 模式中的 ViewModel 中设置委托的正确方法是什么。

我正在从另一个类实例化 ViewController:

let viewModel = DashboardViewModel()
let viewController = DashboardViewController(viewModel: viewModel)

我的视图模型:

protocol DashboardViewModelType {
    var items: [Item] { get }
    var reloadDelegate: DashboardDataReloadDelegate? { get set }
}

protocol DashboardDataReloadDelegate: class {
    func reloadData()
}

class DashboardViewModel: DashboardViewModelType {

    var items: [Item] = []

    weak var reloadDelegate: DashboardDataReloadDelegate?

    init() {
        loadItems()
    }

    func loadItems() {
        let databaseFetcher = DatabaseDaysFetcher()
        databaseFetcher.getDays(onData: { (items) in
            self.items = items
            reloadDelegate?.reloadData() //delegate is nil here
        }) { (error) in
            print(error)
        }
    }
}

和视图控制器:

class DashboardViewController: UIViewController {

    var viewModel: DashboardViewModelType?

    init(viewModel: DashboardViewModelType) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
        self.viewModel!.reloadDelegate = self // it is executed after 
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad() 
    }

}

extension DashboardViewController: DashboardDataReloadDelegate {
    func reloadData() {
        print("data reloaded")
    }
}

所以主要问题是,如果我想在另一个类中注入 viewModel,我会在委托尚未设置时实例化 viewModel。在DashboardViewModelType 协议中声明loadItems,然后从ViewController 中的init 或viewDidLoad 调用这个函数会更好吗?

【问题讨论】:

    标签: ios swift mvvm delegates


    【解决方案1】:

    是的,您可以在init 中为DashboardViewModel 注入DatabaseDaysFetcher,然后如您所说,将loadItems 移动到DashboardViewModelType 协议中。

    然后当你调用loadItems 时,它应该回调给调用者。 然后在loadItems回调中使用[weak self]。

    这样就不需要delegate

    protocol DashboardViewModelType {
        init(databaseFetcher: DatabaseDaysFetcher)
        func loadItems(completion: ([Item]) -> Void, error: (Error) -> Void)
    }
    
    final class DashboardViewModel: DashboardViewModelType {
    
        private var databaseFetcher: DatabaseDaysFetcher
    
        init(databaseFetcher: DatabaseDaysFetcher) {
            self.databaseFetcher = databaseFetcher
        }
    
        func loadItems(completion: ([Item]) -> Void, onError: (Error) -> Void) {
            self.databaseFetcher.getDays(onData: { (items) in
                completion(items)
            }) { (error) in
                onError(error)
            }
        }
    }
    

    【讨论】:

    • 顺便说一句,可以把所有方法都放在这个协议中吗?据我所知,在注入依赖项时最好使用协议,对吧?所以我应该将所有必要的方法放在协议中并使用协议类型而不是向下转换为确切的 ViewModel?
    • 正确 - 你想要code to an interface 的原因是它会帮助你编写可测试的代码,例如您可以编写一个使用字典而不是 API 或数据库调用的假 DatabaseDayFetcher,并专注于测试那个单元。此外,它有助于创建可重用的代码和易于更改的代码。
    猜你喜欢
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 2018-06-25
    • 1970-01-01
    • 2016-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多