当我需要从两个不同的 Web 服务获取控制器数据时,应该如何使用 MVVM 和依赖注入?

Has*_*esh 5 dependency-injection mvvm repository-pattern ios swift

我在我的 swift 项目中使用 MVVM 设计模式。在视图控制器中,我需要从两个不同的 Web 服务获取数据。所以我需要在控制器中创建一个视图模型,如下所示:

class MyViewController: UIViewController {

var viewModel: MyViewModel = MyViewModel(repository: NetworkLayer(service1: WebService1(), service2: WebService2()))

    override func viewDidLoad() {
         super.viewDidLoad()
         viewModel.getData(catUrl, categoryId: "\(catId)")
    }
} 
Run Code Online (Sandbox Code Playgroud)

视图模型负责从服务器获取数据并通过控制器更新视图。我找到了存储库设计模式和依赖注入来解决我的问题:

class MyViewModel {
    var webService1: WebService1?
    var webService2: WebService2?

    init(repository: NetworkLayer) {
        self.webService1 = repository.service1
        self.webService2 = repository.service2
    }

    func getData(_ url: String, categoryId: String) {
        webService1?.delegate = self
        webService1?.getData(urlCode: url)

        webService2?.delegate = self
        webService2?.getProducts(queryString: "sortby=14&pageno=0&status=2&pagesize=20&category=c\(categoryId)")
    }
}

class NetworkLayer: WebService1Delegate, WebService2Delegate {  
    var webService1: WebService1?
    var webService2: WebService2?

    init(service1: WebService1, service2: WebService2) {
        self.service1 = service1
        self.service2 = service2
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以告诉我处理这种情况的正确方法吗?如果不是,最好的应对方法是什么?

Ada*_*amM 5

如果您想正确实现存储库模式,我建议您应该使用协议。例如,您将创建一个像这样的协议

protocol RepositoryProtocol {
    func fetchInformation() -> String
}
Run Code Online (Sandbox Code Playgroud)

所以你的服务类必须实现这个。这是一个例子

class MyService: RepositoryProtocol {
    func fetchInformation() -> String {

        return ""
    }
}
Run Code Online (Sandbox Code Playgroud)

为了安全起见,您还应该创建一个视图模型协议。

protocol ViewModel {
    associatedtype Repo
    init (withRepo repo: Repo)
}
Run Code Online (Sandbox Code Playgroud)

所以这就是说,符合此 ViewModel 协议的类将在视图模型中定义一些类型,并且必须将其注入到 init 方法中。还有一个好处,它不再是一个可选对象。

这是视图模型的示例

struct MyViewModel : ViewModel {
    typealias Repo = RepositoryProtocol

    let repo : RepositoryProtocol
    init(withRepo repo: Repo) {
        self.repo = repo
    }
}
Run Code Online (Sandbox Code Playgroud)

所以在这里您可以看到,您的视图模型必须注入一个符合您在视图模型类中定义的协议的对象。正如您所看到的,typealias 用于表示该对象的类型为 RepositoryProtocol。您可以更新视图模型协议,以便它需要使用您需要的尽可能多的服务来初始化视图模型

这是处理存储库模式和依赖项注入的更好方法。