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
480
Kotlin-Flavored BuildScripts
importre
1
270
A tour of Standard Library
importre
0
90
Compose everything with rx & kotlin
importre
0
1.2k
Ready for Production
importre
2
310
Other Decks in Programming
See All in Programming
七輪ライブラリー: Claude AI で作る Next.js アプリ
suneo3476
1
170
UMAPをざっくりと理解 / Overview of UMAP
kaityo256
PRO
3
1.4k
Носок на сок
bo0om
0
1.1k
スモールスタートで始めるためのLambda×モノリス(Lambdalith)
akihisaikeda
2
340
ASP.NETアプリケーションのモダナイゼーションについて
tomokusaba
0
240
Road to RubyKaigi: Making Tinny Chiptunes with Ruby
makicamel
4
540
ComposeでのPicture in Picture
takathemax
0
130
読書シェア会 vol.4 『ダイナミックリチーミング 第2版』
kotaro666
0
110
エンジニア向けCursor勉強会 @ SmartHR
yukisnow1823
3
12k
By the way Google Cloud Next 2025に行ってみてどうだった
ymd65536
0
110
SwiftDataのカスタムデータストアを試してみた
1mash0
0
140
Browser and UI #2 HTML/ARIA
ken7253
2
170
Featured
See All Featured
Code Reviewing Like a Champion
maltzj
523
40k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
52
2.4k
It's Worth the Effort
3n
184
28k
How to Ace a Technical Interview
jacobian
276
23k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.8k
What's in a price? How to price your products and services
michaelherold
245
12k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Being A Developer After 40
akosma
91
590k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
60k
Optimizing for Happiness
mojombo
378
70k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
8
690
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
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