ObservedObjectの中にデータクラスを入れてその更新を取得しようとしたらハマったので共有
どうやらこのケースはこのままだとViewが更新されない模様
(もし間違っていたりこんなやり方もあるよーというのがあれば対応いたしますので教えていただければ幸いです!)
#実験
1秒に1ずつ増えていくsubCountを持つDataClassを作成
class DataClass: ObservableObject {
@Published var subCount = 0
var timer = Timer()
init(){
//生成と共にタイマー開始
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(classLoop), userInfo: nil, repeats: true)
}
// タイマーで呼び出されるメソッド
@objc func classLoop() {
subCount = subCount + 1
}
}
5秒に1ずつ増えていくrootCountと先程のDataClassを持つPublishTestも作ります
class PublishTest: ObservableObject {
@Published var rootCount: Int = 0
@Published var dataclass = DataClass()
var timer = Timer()
init(){
//生成と共にタイマー開始
timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(classLoop), userInfo: nil, repeats: true)
}
}
// タイマーで呼び出されるメソッド
@objc func classLoop() {
rootCount = rootCount + 1
}
}
最後にView側は下記を表示させます
- publishTest自身のrootCount
- publishTestが持つDataClassのsubCount
struct ContentView: View {
@ObservedObject var publishTest: PublishTest
var body: some View {
VStack{
Text("rootCountは\(publishTest.rootCount)")
Text("DataClassのsubCountは\(publishTest.dataclass.subCount)")
}
}
}
しばらく更新されません
尚、printでログ表示するとsubCountが増えているのを確認できます
5秒経ち、rootCountが更新されるとrootCountもsubCountもその時点のものに更新されます
何もしない場合、下記の挙動になるようです
- ObservedObjectの中に入れ子にしたサブクラス側の変更はView側に伝わない
- ObservedObject直下の何かしらが更新された時にViewが更新されサブクラスのデータも反映
#対応策
### 1.ObservedObject直下のデータを利用する構造にする
身も蓋もないですが、View側で更新させたいものは全てObservedObject直下のものだけを使う構造にするのが1つ。
もしくは更新させたいタイミングでObservedObjectのデータを強制的に更新する事でも実現は可能かもしれません(上記の実験のケース)
### 2. CombineのobjectWillChangeを利用する
CombineのobjectWillChangeを利用する事でデータの更新が反映されるみたいです。
こちらの記事が参考になりました
https://www.it-swarm-ja.com/ja/ios/%E5%85%A5%E3%82%8C%E5%AD%90%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%9Fobservableobject%E3%81%AB%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%ABswiftui%E3%83%93%E3%83%A5%E3%83%BC%E3%81%AB%E6%8C%87%E7%A4%BA%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/813692063/
PublishTest側に追加してみます
import Combine
class PublishTest: ObservableObject {
@Published var rootCount: Int = 0
@Published var dataclass = DataClass()
var timer = Timer()
var anyCancellable: AnyCancellable? = nil //追加
init(){
timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(classLoop), userInfo: nil, repeats: true)
//追加ここから
anyCancellable = dataclass.objectWillChange.sink { (_) in
self.objectWillChange.send()
}
//追加ここまで
}
// タイマーで呼び出されるメソッド
@objc func classLoop() {
rootCount = rootCount + 1
}
}