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
2k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Cycle for Android
functional and reactive programming in Kotlin
Jaewe Heo
August 03, 2016
More Decks by Jaewe Heo
See All by Jaewe Heo
Kotlin, Gradle and AWS SAM
importre
1
650
Kotlin-Flavored BuildScripts
importre
1
370
A tour of Standard Library
importre
0
150
Compose everything with rx & kotlin
importre
0
1.4k
Ready for Production
importre
2
330
Other Decks in Programming
See All in Programming
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
140
自動化したのに回らないテスト運用の壁ーAI時代の品質責任と生産性
mfunaki
0
120
Google Apps Script で Ruby を動かす
kawahara
0
130
Foundation Models frameworkで画像分析
ryodeveloper
1
510
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
1
270
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
220
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.5k
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
200
5分で問診!Composer セキュリティ健康診断
codmoninc
0
860
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
960
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.7k
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
210
Featured
See All Featured
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.7k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
510
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Navigating Weather and Climate Data
rabernat
0
440
Code Reviewing Like a Champion
maltzj
528
40k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
530
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
250
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Darren the Foodie - Storyboard
khoart
PRO
3
3.5k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
160
Claude Code のすすめ
schroneko
67
230k
How to Ace a Technical Interview
jacobian
281
24k
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