struct Content: Hashable {
let name: String
}
extension Content: Identifiable {
var id: Content { self }
}
class ContentViewModel: ObservableObject {
@Published var contents: [Content] = [.init(name: "piyo")]
}
上記のようなViewModelを実装した際にcontents
の更新を監視したい場合がある
解決策
viewModel.$contents
.sink { contents in
print(contents)
}
もしくは
viewModel.objectWillChange
.sink {
print("willChange")
}
これで値を購読することができる
注意するべきポイントとして、どちらも値更新前に呼ばれるのでsinkが呼ばれたタイミングでは値は更新されていない。
プロパティを直接購読する場合は更新後の値が取れるが、
objectWillChangeを購読する場合はObservableObjectに生えているプロパティそれぞれの更新タイミングを取得することができるが更新後の値は取得できない。
余談
ちなみに、ViewのinitializerなどでViewModelにあるPublishedObjectの更新前の値を参照したい場合に直接クロージャーで扱おうとするとEscaping closure captures mutating 'self' parameter
と出てしまうので、値をコピーする必要がある。
コピーをするだけなのでクロージャー内で値を書き換えてもViewの保持するViewModelには何も影響は出ない。
viewModel.$contents
.sink { [previousContents = viewModel.contents] _ in
print(previousContents)
}
typealias DisposeBag = Set<AnyCancellable>
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
private var disposeBag = DisposeBag()
var body: some View {
NavigationView {
List {
ForEach(viewModel.contents) { content in
NavigationLink(
destination: Text(content.name),
label: {
Text(content.name)
})
}
}
}
}
init() {
viewModel.$contents
.sink { [previousContents = viewModel.contents] contents in
print(contents)
print(previousContents)
}
.store(in: &disposeBag)
}
}