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
Cycle for Android
Search
Jaewe Heo
August 03, 2016
Programming
3
1.9k
Cycle for Android
functional and reactive programming in Kotlin
Jaewe Heo
August 03, 2016
Tweet
Share
More Decks by Jaewe Heo
See All by Jaewe Heo
Kotlin, Gradle and AWS SAM
importre
1
430
Kotlin-Flavored BuildScripts
importre
1
220
A tour of Standard Library
importre
0
78
Compose everything with rx & kotlin
importre
0
1.1k
Ready for Production
importre
2
290
Other Decks in Programming
See All in Programming
offers_20241022_imakiire.pdf
imakurusu
2
360
OpenTelemetryでRailsのパフォーマンス分析を始めてみよう(KoR2024)
ymtdzzz
4
1.6k
CSC305 Lecture 13
javiergs
PRO
0
130
Content Security Policy入門 セキュリティ設定と 違反レポートのはじめ方 / Introduction to Content Security Policy Getting Started with Security Configuration and Violation Reporting
uskey512
1
430
Importmapを使ったJavaScriptの 読み込みとブラウザアドオンの影響
swamp09
4
1.2k
ピラミッド、アイスクリームコーン、SMURF: 自動テストの最適バランスを求めて / Pyramid Ice-Cream-Cone and SMURF
twada
PRO
9
1k
色々なIaCツールを実際に触って比較してみる
iriikeita
0
270
go.mod、DockerfileやCI設定に分散しがちなGoのバージョンをまとめて管理する / Go Connect #3
arthur1
10
2.4k
Progressive Web Apps für Desktop und Mobile mit Angular (Hands-on)
christianliebel
PRO
0
110
開発効率向上のためのリファクタリングの一歩目の選択肢 ~コード分割~ / JJUG CCC 2024 Fall
ryounasso
0
370
シールドクラスをはじめよう / Getting Started with Sealed Classes
mackey0225
3
400
Vitest Browser Mode への期待 / Vitest Browser Mode
odanado
PRO
2
1.7k
Featured
See All Featured
XXLCSS - How to scale CSS and keep your sanity
sugarenia
246
1.3M
Writing Fast Ruby
sferik
626
61k
Navigating Team Friction
lara
183
14k
Adopting Sorbet at Scale
ufuk
73
9k
Making the Leap to Tech Lead
cromwellryan
132
8.9k
Thoughts on Productivity
jonyablonski
67
4.3k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
3
370
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
41
2.1k
How GitHub (no longer) Works
holman
311
140k
RailsConf 2023
tenderlove
29
880
Transcript
Cycle for Android Jaewe Heo · !iiid! · @importre Aug.
3, 2016 functional and reactive programming in Kotlin
import importre
About me • Android • Rx • Kotlin - Kotlin
Korea • Electron - Electron Korea • React - react-photonkit • Alfred workflows
alfred-mdi
alfred-pg
alfred-gi
alfred-hl
alfred-awe
alfred-tidy
Cycle for Android functional and reactive programming in Kotlin
Cycle.js http://cycle.js.org/
HCI (Human-Computer Interaction) two-way process
None
None
None
Mutual observation Reactive Programming
Two-way process val y = human(x)
Two-way process val y = human(x) val x = computer(y)
Two-way process val y = human(x) val x = computer(y)
?!
val x = computer(human(x))
Streams
Programming Foo Bar
Programming Foo Bar
Programming Foo Bar
Imperative Programming Foo Bar Proactive Passive Fooо Bar ࢚కܳ ߸҃ೡ
ࣻ . Barח ־о ೱਸ ח ݽܲ.
// This is inside the `Foo` module fun onNetworkRequest() {
// ... bar.incrementCounter() // ... } Example of the Imperative Programming
Barח ৻ࠗ ߮ী ߈ೞৈ न ࢚కܳ ҙܻೠ. Reactive Programming Foo
Bar Listenable Reactive Fooח ־ҳীѱ ೱਸ ח ݽܲ.
// This is inside the `Bar` module foo.addOnNetworkRequestListener { //
`this` is Bar this.incrementCounter() } Example of the Reactive Programming
Solve the cyclic dependency val y = human(x) val x
= computer(y)
Solve the cyclic dependency val proxyX = ReplaySubject.create<Any>() val y
= human(proxyX) val x = computer(y)
Solve the cyclic dependency val proxyX = ReplaySubject.create<Any>() val y
= human(proxyX) val x = computer(y) x.imitate(proxyX)
None
kotlin-cycle Cycle for Android (written in Kotlin)
Hello Example • ܴਸ ੍ (read) • ݫद ߸҃ (model)
• ݫदܳ ചݶী ࠁৈષ (write)
None
HelloActivity.kt class HelloActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_hello) Cycle.run(main, DomSource()) } private val main = { sources: Sources -> val change = sources.dom().select(helloEdit).textChanges() val model = change.map(::greeting) val view = model.map { message -> onUpdateView(message) } Sinks(DomSink(view)) } private fun onUpdateView(message: CharSequence) = { helloText.text = message } }
HelloActivity.kt class HelloActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_hello) Cycle.run(main, DomSource()) } private val main = { sources: Sources -> val change = sources.dom().select(helloEdit).textChanges() val model = change.map(::greeting) val view = model.map { message -> onUpdateView(message) } Sinks(DomSink(view)) } private fun onUpdateView(message: CharSequence) = { helloText.text = message } }
HelloActivity.kt class HelloActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_hello) Cycle.run(main, DomSource()) } private val main = { sources: Sources -> val change = sources.dom().select(helloEdit).textChanges() val model = change.map(::greeting) val view = model.map { message -> onUpdateView(message) } Sinks(DomSink(view)) } private fun onUpdateView(message: CharSequence) = { helloText.text = message } }
Kotlin-flavored Examples
HelloCycleActivity.kt class HelloCycleActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_hello) cycle { val change = dom.select(helloEdit).textChanges() val model = change.map(::greeting) model.map { message -> { helloText.text = message } } } } }
HelloCycleActivity.kt class HelloCycleActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_hello) cycle { val change = dom.select(helloEdit).textChanges() val model = change.map(::greeting) model.map { message -> { helloText.text = message } } } } }
Hello.kt fun greeting(name: CharSequence) = "Hello, $name!"
HelloTest.kt class HelloTest { @Test fun testGreeting() { val actual
= greeting("cycle") val expected = "Hello, cycle!" assertEquals(expected, actual) } }
BMI Example • ރޖѱ, ఃী ೠ ୡӝ ч ࢸ (properties)
• SeekBarܳ (read) • BMI ҅ • Ѿҗܳ ചݶী ࠁৈષ (write)
BmiActivity.kt cycle { val weightProps = Observable.just(Props(min = 40, max
= 140, value = 70)) val heightProps = Observable.just(Props(min = 140, max = 210, value = 170)) // Intent val (weightStream, heightStream) = intent(dom, heightProps, weightProps) // Model val stateStream = model(weightStream, heightStream, weightProps, heightProps) // View stateStream.map { state -> onUpdateView(state) } }
None
BmiActivity.kt private val intent = { dom: DomSource, heightProps: Observable<Props>,
weightProps: Observable<Props> -> val weightChangeStream = dom .select(weightSeekBar.apply { val props = weightProps.toBlocking().first() max = props.max - props.min progress = props.value - props.min }) .userChanges() val heightChangeStream = dom .select(heightSeekBar.apply { val props = heightProps.toBlocking().first() max = props.max - props.min progress = props.value - props.min }) .userChanges() Pair(weightChangeStream, heightChangeStream) }
BmiActivity.kt private val model = { weightChangeStream: Observable<Int>, heightChangeStream: Observable<Int>,
weightProps: Observable<Props>, heightProps: Observable<Props> -> Observable.combineLatest( weightChangeStream, heightChangeStream, weightProps, heightProps, ::calculateBmi) }
BmiActivity.kt private fun onUpdateView(state: State) = { unwrap(weightText, heightText, bmiText)
{ w, h, b -> w.text = "Weight: ${state.weight} kg" h.text = "Height: ${state.height} cm" b.text = "Bmi: ${state.bmi}" } nah { toast(R.string.error_undefined_views) } } kotlin-unwrap
Bmi.kt data class State(val weight: Int, val height: Int, val
bmi: Int) data class Props(val min: Int, val max: Int, val value: Int) fun calculateBmi(weight: Int, height: Int, weightProps: Props, heightProps: Props): State { val realHeight = height + heightProps.min val realWeight = weight + weightProps.min val heightMeters = realHeight * 0.01F val bmi = Math.round(realWeight / (heightMeters * heightMeters)) return State(realWeight, realHeight, bmi) }
REST Example • ࢜۽Ҋஜ (read) • ୡӝীח ࢜۽Ҋஜ হ ࢲߡ۽
ࠗఠ ؘఠܳ ੍যঠ ೣ • request(write) data -> response(read) data • Ѿҗܳ ചݶী ࠁৈષ (write) • ߡౡਸ ־ܴ (read) • షझܳ ڸ (write) https://jsonplaceholder.typicode.com/
UsersActivity.kt cycle { error = onError // Intent val usersStream
= api.getUsers() val refreshStream = dom .select(refreshView) .refreshes() .startWith(null as Void?) // Model val modelStream = Observable .combineLatest(usersStream, refreshStream) { users, refresh -> users } // View modelStream.map { users -> onUpdateView(users) } }
Extensions fun ImageView.loadUrl(imageUrl: String) = Picasso.with(context).load(imageUrl).into(this) fun Activity.toast(@StringRes id: Int)
= Toast.makeText(this, id, Toast.LENGTH_SHORT).show() fun Activity.toast(message: String) = Toast.makeText(this, message, Toast.LENGTH_SHORT).show() fun RecyclerView.ViewHolder.toast(message: String) = Toast.makeText(itemView.context, message, Toast.LENGTH_SHORT).show()
UserViewHolder.kt itemView.locationImage.loadUrl(url) itemView.nameText.text = user.name cycle { val emailChange =
dom.select(itemView.emailButton).clicks().map { ButtonType.EMAIL } val callChange = dom.select(itemView.callButton).clicks().map { ButtonType.CALL } val change = Observable.merge(emailChange, callChange) change.map { type -> show(user, type) } }
UserViewHolder.kt private fun show(user: User, type: ButtonType) = { toast(when
(type) { ButtonType.EMAIL -> user.email ButtonType.CALL -> user.phone }) }
Conclusion
Conclusion • ӭՔೞפ જӟೠؘ…
Conclusion • functional ೠо? • reactive ೠо? • productionী ਊ
оמೠо?
Conclusion • functional ೠо? • reactive ೠо? • productionী ਊ
оמೠо?
References
References - importre • https://github.com/importre/kotlin-cycle • https://github.com/importre/kotlin-unwrap • https://github.com/importre/ready-for-production •
Data class and Equality • Function Literals with Receiver • let, with, run, apply, use(using)
References - cycle.js • http://cycle.js.org/ • https://github.com/cyclejs-community/cycle-android
References - video • Unidirectional data flow architectures • What
if the user was a function?
val coWorker = true cycle { val change = dom
.select(toggle.apply { isChecked = true }) .checkedChanges() val model = change .map { onOff -> "উ٘۽٘ ѐߊܳ ݽभפ: $onOff" } .map { recruit -> "Riiid!\n$recruit" } model.map { message -> { checkText.text = message } } }
[email protected]
Thank you