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
450
Kotlin-Flavored BuildScripts
importre
1
240
A tour of Standard Library
importre
0
87
Compose everything with rx & kotlin
importre
0
1.1k
Ready for Production
importre
2
300
Other Decks in Programming
See All in Programming
チームリードになって変わったこと
isaka1022
0
190
JavaScriptツール群「UnJS」を5分で一気に駆け巡る!
k1tikurisu
10
1.8k
SwiftUI Viewの責務分離
elmetal
PRO
0
150
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
5
730
Honoのおもしろいミドルウェアをみてみよう
yusukebe
1
200
Bedrock Agentsレスポンス解析によるAgentのOps
licux
2
720
自分ひとりから始められる生産性向上の取り組み #でぃーぷらすオオサカ
irof
8
2.6k
Linux && Docker 研修/Linux && Docker training
forrep
23
4.5k
第3回 Snowflake 中部ユーザ会- dbt × Snowflake ハンズオン
hoto17296
4
360
Conform を推す - Advocating for Conform
mizoguchicoji
3
680
Unity Android XR入門
sakutama_11
0
140
[JAWS-UG横浜 #79] re:Invent 2024 の DB アップデートは Multi-Region!
maroon1st
1
140
Featured
See All Featured
Site-Speed That Sticks
csswizardry
3
370
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
GraphQLの誤解/rethinking-graphql
sonatard
68
10k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
8
270
A designer walks into a library…
pauljervisheath
205
24k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
Into the Great Unknown - MozCon
thekraken
35
1.6k
Statistics for Hackers
jakevdp
797
220k
The World Runs on Bad Software
bkeepers
PRO
67
11k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.1k
Building an army of robots
kneath
302
45k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7.1k
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