Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Implementation of API call state using sealed c...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
35
17k
HTML-in-Canvasで作る未来のWeb UI表現
clockmaker
5
5.1k
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
コーディング自動化の次へ。人の介入をゼロにするファクトリーとサポートAIによる実践
timakin
5
3.7k
AWSに止められる覚悟してますか?
morizo_1984
2
540
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
190
Workers Cache を知る
syumai
0
370
sbt 2.0: takeoff
eed3si9n
0
160
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
500
気持ちぃ〜角丸 Squircle
ryu1013
0
310
AmebaブログはKMPから卒業します
kmars0605
0
190
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
Featured
See All Featured
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
590
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.5k
Designing Experiences People Love
moore
143
24k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
450
How to make the Groovebox
asonas
2
2.5k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.2k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
630
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
380
Practical Orchestrator
shlominoach
192
12k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3.1k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.9k
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 と相性がいい
ご静聴ありがとうございました