Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Implementation of API call state using sealed c...
Search
Hideyuki Kikuma
January 16, 2020
Programming
2.8k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Implementation of API call state using sealed class
Hideyuki Kikuma
January 16, 2020
More Decks by Hideyuki Kikuma
See All by Hideyuki Kikuma
Talk about still using minSdkVersion=7
hidey
1
580
AndroidとIPv6
hidey
1
1.2k
Other Decks in Programming
See All in Programming
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
430
yield再入門 #phpcon
o0h
PRO
0
830
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
440
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
120
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
910
OpenSpecのproposalにbrainstormingを持たせてみた
tigertora7571
1
150
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
120
FDEが実現するAI駆動経営の現在地
gonta
2
240
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
340
その節約、円になってますか?
isamumumu
0
550
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
420
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
560
Featured
See All Featured
エンジニアに許された特別な時間の終わり
watany
108
250k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
640
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
310
Statistics for Hackers
jakevdp
799
230k
Code Review Best Practice
trishagee
74
20k
Design in an AI World
tapps
1
270
Optimising Largest Contentful Paint
csswizardry
37
3.8k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
450
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
230
We Are The Robots
honzajavorek
0
290
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Transcript
API通信の状態を sealed classを使って表現する Bonfire Android #6
自己紹介 @hidey / 菊間 英行 株式会社メルペイ Android エンジニア DroidKaigi スタッフ
API通信中の状態とか 困ったりしてませんか?
よくある画面表示の要件 • 画面表示には API から取ってきた情報が必要 ◦ 初期表示はデフォルト表示の場合と何も表示したくないパターンどっちもありそう • ロード中は progress
表示にしたい • API のレスポンスが返ってきたらその内容を表示 • API がエラーだった場合はエラー画面表示にしたい • エラー画面に再読み込みボタンを付けたい
None
つまり画面の状態としてはざっくり4つ • 初期状態 • ロード中 • データ取得完了 • データ取得エラー
メルペイではRemoteDataKを使っている 内部で使っていたライブラリを OSS 化したもの • 状態を表現する sealed class • 便利関数
(map, mapError など ) メルペイの用途ではこれで満足できている https://github.com/mercari/RemoteDataK
コードで表すとこんな感じ sealed class RemoteData<out V : Any, out E :
Exception> { object Initial : RemoteData<Nothing, Nothing>() class Loading<V : Any>(progress: Int? = null, val total: Int = 100) : RemoteData<V, Nothing>() class Success<out V : Any>(val value: V) : RemoteData<V, Nothing>() class Failure<out E : Exception>(val error: E) : RemoteData<Nothing, E>() }
素朴なstate実装 data class SampleState( val entity: SampleEntity? = null, val
error: Exception? = null, val isLoading: Boolean = false ) data class SampleEntity( val title: String, val items: List<String> )
その場合のStateの変更箇所のコード class SampleReducer { fun reduce(action: Action, currentState: SampleState): SampleState
= when (action) { is ShowDataAction -> { if (action.result.isSuccess) { currentState.copy(entity = action.result.entity, isLoading = false) } else { currentState.copy(error = action.result.error, isLoading = false) } } is LoadData -> currentState.copy(isLoading = true, entity = null, error = null) else -> currentState } }
その場合のView周りのコード fun updateView(state: SampleState) { if (state.entity != null) {
titleView.text = state.entity.title adapter.items = state.entity.items } if (state.error != null) { errorMessage.text = state.error.localizedMessage } errorView.isVisible = state.error != null loadingView.isVisible = state.isLoading }
RemoteDataで書き直してみる data class SampleState( val entity: RemoteData<SampleEntity, Exception> = RemoteData.Initial
) data class SampleEntity( val title: String, val items: List<String> )
その場合のStateの変更箇所のコード class SampleReducer { fun reduce(action: Action, currentState: SampleState): SampleState
= when (action) { is ShowDataAction -> { if (action.result.isSuccess) { currentState.copy(entity = RemoteData.Success(action.result.entity)) } else { currentState.copy(entity = RemoteData.Failure(action.result.error)) } } is LoadData -> currentState.copy(entity = RemoteData.Loading()) else -> currentState } }
その場合のView周りのコード fun updateView(state: SampleState) { when (val entity = state.entity)
{ is RemoteData.Success -> { titleView.text = entity.value.title adapter.items = entity.value.items } is RemoteData.Failure -> errorMessage.text = entity.error.localizedMessage } errorView.isVisible = state.entity.isFailure loadingView.isVisible = state.entity.isLoading }
あれ? そんなに変わらなくない?
拡張関数を追加してみる fun <V : Any, E : RemoteError> Result<V, E>.toRemoteData():
RemoteData<V, E> = when (this) { is Result.Success -> RemoteData.Success(this.value) is Result.Failure -> RemoteData.Failure(this.error) }
Stateの変更箇所のコード class SampleReducer { fun reduce(action: Action, currentState: SampleState): SampleState
= when (action) { is ShowDataAction -> currentState.copy( entity = action.result.toRemoteData() ) is LoadData -> currentState.copy(entity = RemoteData.Loading()) else -> currentState } }
View周りでいいこととか statusObservable.map(SampleState::entity) .map { it.value.title } // title が nonnull
である必要がある .distinctUntilChanged() .subscribe { updateTitle(it) }
課題もある
例えばkotlinx.serializationが使えない • AAC の ViewModel で状態管理をしたい場合、 Bundle に入れられる型が必要 • kotlinx.serialization
で Serializable にしたい • RemoteDataK の Failure は java の Exception を持っている • これは kotlinx.serialization で Serializable に出来ない このため、アドホックなコードで対応してる
詳しくはこちら https://tech.mercari.com/entry/2019/12/04/100000 https://tech.mercari.com/entry/2019/12/18/100000
今日のサンプルコード https://github.com/hidey/remote_data_sample
まとめ • sealed class を使うと状態 + 値の組み合わせをうまく表現できる • 一つのプロパティにまとまるので関数で処理しやすい •
ロード中などの状態を全体で統一した表現に出来る • NullObject を作らずに nonnull に出来るので Rx と相性がいい
ご静聴ありがとうございました