2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【SwiftUI】ObservedObjectの中に入れたクラスの変更でViewを更新したい時の注意

Last updated at Posted at 2021-07-06

ObservedObjectの中にデータクラスを入れてその更新を取得しようとしたらハマったので共有
どうやらこのケースはこのままだとViewが更新されない模様
(もし間違っていたりこんなやり方もあるよーというのがあれば対応いたしますので教えていただければ幸いです!)

#実験
1秒に1ずつ増えていくsubCountを持つDataClassを作成

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も作ります

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
View側
struct ContentView: View {
    
    @ObservedObject var publishTest: PublishTest
    
    var body: some View {
        VStack{
            Text("rootCountは\(publishTest.rootCount)")
            Text("DataClassのsubCountは\(publishTest.dataclass.subCount)")
        }
    }
}

#結果
スクリーンショット 2021-07-06 16.23.16.png

しばらく更新されません
尚、printでログ表示するとsubCountが増えているのを確認できます

スクリーンショット 2021-07-06 16.23.00.png
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側に追加してみます

DataClassを持つ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
    }
}

これで更新されている事を確認
スクリーンショット 2021-07-06 16.52.24.png

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?