首页 > 解决方案 > 2 个 ObservableObjects 之间的数据通信

问题描述

我有 2 个独立ObservableObject的 sViewModel1ViewModel2

ViewModel2有一个字符串数组:

@Published var strings: [String] = [].

每当修改该数组时,我都想ViewModel1被告知。

实现这一目标的推荐方法是什么?

标签: swiftswiftuicombineobservableobjectdata-communication

解决方案


显然,有很多潜在的解决方案,比如前面提到的NotificationCenter和单例的想法。

对我来说,这似乎是一个组合相当有用的场景:

import SwiftUI
import Combine

class ViewModel1 : ObservableObject {
    var cancellable : AnyCancellable?
    
    func connect(_ publisher: AnyPublisher<[String],Never>) {
        cancellable = publisher.sink(receiveValue: { (newStrings) in
            print(newStrings)
        })
    }
}

class ViewModel2 : ObservableObject {
    @Published var strings: [String] = []
}


struct ContentView : View {
    @ObservedObject private var vm1 = ViewModel1()
    @ObservedObject private var vm2 = ViewModel2()
    
    var body: some View {
        VStack {
            Button("add item") {
                vm2.strings.append("\(UUID().uuidString)")
            }
            
            ChildView(connect: vm1.connect)
            
        }.onAppear {
            vm1.connect(vm2.$strings.eraseToAnyPublisher())
        }
    }
}

struct ChildView : View {
    var connect : (AnyPublisher<[String],Never>) -> Void
    @ObservedObject private var vm2 = ViewModel2()
    
    var body: some View {
        Button("Connect child publisher") {
            connect(vm2.$strings.eraseToAnyPublisher())
            vm2.strings = ["Other strings","From child view"]
        }
    }
}

要对此进行测试,首先尝试按下“添加项目”按钮——您将在控制台中看到ViewModel1接收新值。

然后,试试这个Connect child publisher按钮——现在,初始连接被取消,并为孩子的ViewModel2.

为了使这个场景起作用,您总是必须引用ViewModel1and ViewModel2,或者至少引用该connect方法,正如我在 中演示的那样ChildView。您可以通过依赖注入甚至通过EnvironmentObject

ViewModel1如果您需要一个 -> 多个场景,也可以更改为而不是拥有 1 个连接,而是通过创建一个连接并每次添加一个连接来拥有多个cancellable连接Set<AnyCancellable>

使用AnyPublisher解耦了等式两边都有特定类型的想法,因此连接ViewModel4ViewModel1等同样容易。


推荐阅读