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
570
AndroidとIPv6
hidey
1
1.2k
Other Decks in Programming
See All in Programming
Inside Stream API
skrb
1
660
メソッドのジェネリクスでGoの夢は広がるか? / Kyoto.go #65
utgwkk
3
610
Datadog × OpenTelemetry 入門と実践のあいだ
kn_to_maxpno
1
150
今さら聞けないCancellationToken
htkym
0
220
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.3k
Claspは野良GASの夢をみるか
takter00
0
170
AI駆動開発で崩れていくコードベースを立て直す
kyoko_nr_nr
1
440
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
3.3k
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
510
CSC307 Lecture 17
javiergs
PRO
0
320
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
110
LLM Plugin for Node-REDの利用方法と開発について
404background
0
160
Featured
See All Featured
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
0
1.6k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
170
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
160
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Chasing Engaging Ingredients in Design
codingconduct
0
210
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
480
How to train your dragon (web standard)
notwaldorf
97
6.7k
KATA
mclloyd
PRO
35
15k
GitHub's CSS Performance
jonrohan
1033
470k
The untapped power of vector embeddings
frankvandijk
2
1.7k
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 と相性がいい
ご静聴ありがとうございました