Slide 1

Slide 1 text

Immutable data holder 2019/02/28 potatotips #59

Slide 2

Slide 2 text

よくある話 タップしてユーザのプロフィールを表示 λοϓ

Slide 3

Slide 3 text

MVVMによる実装 ● ユーザIDを引数としてViewModelがRepository からfetch ● pro leはDataBindingを通じてViewに表示 fun load(userId: String) = profileRepository.fetchProfile(userId) { profile.value = it } ProfileViewModel.kt

Slide 4

Slide 4 text

MVVMによる実装 ● ユーザIDを引数としてViewModelがRepository からfetch ● pro leはDataBindingを通じてViewに表示 fun load(userId: String) = profileRepository.fetchProfile(userId) { profile.value = it } ProfileViewModel.kt

Slide 5

Slide 5 text

MVVMによる実装 ● ユーザIDを引数としてViewModelがRepository からfetch ● pro leはDataBindingを通じてViewに表示 fun load(userId: String) = profileRepository.fetchProfile(userId) { profile.value = it } ProfileViewModel.kt var profile = MutableLiveData()

Slide 6

Slide 6 text

MVVMによる実装 ● ユーザIDを引数としてViewModelがRepository からfetch ● pro leはDataBindingを通じてViewに表示 fun load(userId: String) = profileRepository.fetchProfile(userId) { profile.value = it } ProfileViewModel.kt var profile = MutableLiveData()

Slide 7

Slide 7 text

MVVMによる実装 ● ユーザIDを引数としてViewModelがRepository からfetch ● pro leはDataBindingを通じてViewに表示 fun load(userId: String) = profileRepository.fetchProfile(userId) { profile.value = it } ProfileViewModel.kt var profile = MutableLiveData() ͳΜͱ͔͠ͳ͍ͱɾɾɾɾ

Slide 8

Slide 8 text

Lv1: mutableな変数宣言をやめる 知らないうちにobserveできなくなってしまう val に書き換えでimmutableに var data = MutableLiveData() data.observe(this, Observer { print(it) }) data.value = "hello" data = MutableLiveData() data.value = "world" // ͜͜͸observe͞Εͳ͍ var profile = MutableLiveData() val profile = MutableLiveData()

Slide 9

Slide 9 text

Lv2: mutableなデータを公開しない 変数をvalにしてもオブジェクト自体はまだmutable   ViewがViewModelのデータを変更できてしまう
 外からはread onlyなデータだけが見える形にする val data = MutableLiveData() data.value = "hello" data.value = "world" // いくらでも更新できる private val _profile: MutableLiveData() val profile: LiveData get() = _profile val profile = MutableLiveData()

Slide 10

Slide 10 text

良さそう? まあよさそう。だが、、
 
 
 
 
 privateとはいえViewModel内なら書き換え可能 ● ViewModelが肥大化したら多分事故る ● そもそも2つも変数を宣言するのはくどい private val _profile: MutableLiveData() val profile: LiveData get() = _profile ProfileViewModel.kt

Slide 11

Slide 11 text

Lv3: そもそもmutableにしない switchMapを使いimmutableな定義に 
private val _userId = MutableLiveData() … val profile: LiveData = 
 Transformations.switchMap(_userId){ profileRepository.getProfile(it) }

Slide 12

Slide 12 text

Lv3: そもそもmutableにしない switchMapを使いimmutableな定義に 
private val _userId = MutableLiveData() … val profile: LiveData = 
 Transformations.switchMap(_userId){ profileRepository.getProfile(it) } userIdの変更を監視

Slide 13

Slide 13 text

Lv3: そもそもmutableにしない switchMapを使いimmutableな定義に 
private val _userId = MutableLiveData() … val profile: LiveData = 
 Transformations.switchMap(_userId){ profileRepository.getProfile(it) } IDに変更があればfetch

Slide 14

Slide 14 text

Lv3: そもそもmutableにしない switchMapを使いimmutableな定義に 
private val _userId = MutableLiveData() … val profile: LiveData = 
 Transformations.switchMap(_userId){ profileRepository.getProfile(it) }

Slide 15

Slide 15 text

Lv3: そもそもmutableにしない switchMapを使いimmutableな定義に 
 ● 「pro leはIDの更新以外を理由に変更できない」
 という制約をコード上で表現することができる ● 「pro leはIDをトリガーとしてfetchした値」
 というデータのフローが明確になる private val _userId = MutableLiveData() … val profile: LiveData = 
 Transformations.switchMap(_userId){ profileRepository.getProfile(it) }

Slide 16

Slide 16 text

まとめ mutable、やめよう ● 変数宣言 ● オブジェクトが抱えるデータ 
 LiveDataならswitchMap(Transformations)
 を使うといい感じに

Slide 17

Slide 17 text

Bonus: より簡潔に 拡張関数を活用すると良さそう(ktxにある?)
 
 
 Transformations.switchMap(_userId) { profileRepository.getProfile(it) } _userId.switchMap { profileRepository.getProfile(it) } fun LiveData.switchMap(body: (X) -> LiveData): 
 LiveData { return Transformations.switchMap(this, body) } 長い シンプル https://github.com/google/iosched ͷ Extensions.kt ΑΓ