Slide 1

Slide 1 text

Reactive approach to delegation in Kotlin Aida Issayeva

Slide 2

Slide 2 text

Inheritance party No more!

Slide 3

Slide 3 text

@aida_isay Delegation in Java interface DoorActionListener { void closeDoor(); void openDoor(); } interface WindowActionListener { void openWindow(); void closeWindow(); } @aida_isay

Slide 4

Slide 4 text

@aida_isay Delegation in Java class Window implements WindowActionListener { @Override public void openWindow() { } @Override public void closeWindow() { } } class Door implements DoorActionListener { @Override public void closeDoor() { } @Override public void openDoor() { } }

Slide 5

Slide 5 text

@aida_isay Delegation in Java class Room implements WindowActionListener, DoorActionListener { private Window window = new Window(); private Door door = new Door(); @Override public void closeDoor() { door.closeDoor(); } @Override public void openDoor() { door.openDoor(); } @Override public void openWindow() { window.openWindow(); } @Override public void closeWindow() { window.closeWindow(); } }

Slide 6

Slide 6 text

@aida_isay Delegation in Java class LivingRoom implements WindowActionListener { private Window window = new Window(); @Override public void openWindow() { window.openWindow(); } @Override public void closeWindow() { window.closeWindow(); } }

Slide 7

Slide 7 text

@aida_isay Delegation in Kotlin interface DoorActionListener { fun closeDoor() fun openDoor() } interface WindowActionListener { fun openWindow() fun closeWindow() } open class Window : WindowActionListener { override fun openWindow() {} override fun closeWindow() {} } open class Door : DoorActionListener { override fun closeDoor() {} override fun openDoor() {} } class Room : WindowActionListener by Window(), DoorActionListener by Door() class LivingRoom : WindowActionListener by Window()

Slide 8

Slide 8 text

@aida_isay Delegation in Kotlin interface DoorActionListener { fun closeDoor() fun openDoor() } interface WindowActionListener { fun openWindow() fun closeWindow() } open class Window : WindowActionListener { override fun openWindow() {} override fun closeWindow() {} } open class Door : DoorActionListener { override fun closeDoor() {} override fun openDoor() {} } class Room : WindowActionListener by Window(), DoorActionListener by Door() class LivingRoom : WindowActionListener by Window()

Slide 9

Slide 9 text

@aida_isay Implicit delegation Translation public final class Room implements WindowActionListener, DoorActionListener { // $FF: synthetic field private final Window $$delegate_0 = new Window(); // $FF: synthetic field private final Door $$delegate_1 = new Door(); public void closeWindow() { this.$$delegate_0.closeWindow(); } public void openWindow() { this.$$delegate_0.openWindow(); } public void closeDoor() { this.$$delegate_1.closeDoor(); } public void openDoor() { this.$$delegate_1.openDoor(); } }

Slide 10

Slide 10 text

@aida_isay Implicit delegation Translation public final class LivingRoom implements WindowActionListener { // $FF: synthetic field private final Window $$delegate_0 = new Window(); public void closeWindow() { this.$$delegate_0.closeWindow(); } public void openWindow() { this.$$delegate_0.openWindow(); } } Tools->Kotlin->Show Kotlin Bytecode -> “Decompile” button

Slide 11

Slide 11 text

@aida_isay Delegated properties class User(val name: String, val lastName: String) // var user: User by UserDelegate() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println(user.name) user = User("Android", “Summit") println(user.name) }

Slide 12

Slide 12 text

@aida_isay Delegated properties //use operator //for var class UserDelegate { operator fun getValue(thisRef: Any?, property: KProperty<*>){} operator fun setValue(thisRef: Any?, property: KProperty<*>, value: User){} } //for val class UserDelegate { operator fun getValue(thisRef: Any?, property: KProperty<*>){} } //or implement interfaces //for val class UserDelegate : ReadOnlyProperty { override fun getValue(thisRef: R, property: KProperty<*>): User {} } //for var class UserDelegate : ReadWriteProperty { override fun setValue(thisRef: R, property: KProperty<*>, value: User) {} override fun getValue(thisRef: R, property: KProperty<*>): User {} }

Slide 13

Slide 13 text

@aida_isay 1. lazy

Slide 14

Slide 14 text

@aida_isay Lazy delegate class AboutMeFragment : Fragment() { val component by lazy { val appComponent = DaggerHelper.getAppComponent(this) AboutMeComponent.builder() .appComponent(appComponent) .build() } }

Slide 15

Slide 15 text

@aida_isay Lazy delegate class AboutMeFragment : Fragment() { val component by lazy { val appComponent = DaggerHelper.getAppComponent(this) AboutMeComponent.builder() .appComponent(appComponent) .build() } }

Slide 16

Slide 16 text

@aida_isay Lazy delegate class AboutMeFragment : Fragment() { val component by lazy { val appComponent = DaggerHelper.getAppComponent(this) AboutMeComponent.builder() .appComponent(appComponent) .build() } }

Slide 17

Slide 17 text

@aida_isay Lazy delegate class AboutMeFragment : Fragment() { val component by lazy { val appComponent = DaggerHelper.getAppComponent(this) AboutMeComponent.builder() .appComponent(appComponent) .build() } val viewModel by lazy { val factory = AboutMeViewModel.Factory(component) ViewModelProviders.of(this, factory) .get(AboutMeViewModel::class.java) } }

Slide 18

Slide 18 text

@aida_isay init val init val init val ? LazyThreadSafetyMode SYNCHRONIZED PUBLICATION NONE

Slide 19

Slide 19 text

@aida_isay lazy •Faster class init •No more useless null checks •Smart cast and thread sync •Not used property marked by compiler •Declared and init in single place

Slide 20

Slide 20 text

@aida_isay 2. notNull

Slide 21

Slide 21 text

@aida_isay notNull delegate class AboutMeFragment : Fragment() { lateinit var age: Int }

Slide 22

Slide 22 text

@aida_isay notNull delegate class AboutMeFragment : Fragment() { var age: Int by Delegates.notNull() } import kotlin.properties.Delegates.notNull class AboutMeFragment : Fragment() { var age: Int by notNull() }

Slide 23

Slide 23 text

@aida_isay notNull •Later init •Primitive && top-level properties •No null checks

Slide 24

Slide 24 text

@aida_isay 3. Map

Slide 25

Slide 25 text

@aida_isay map delegate class Profile(map: Map) { val firstName: String by map val lastName: String by map val phoneNumber: String by map val income: Double by map }

Slide 26

Slide 26 text

@aida_isay map delegate class Profile(map: Map) { val firstName: String by map val lastName: String by map val phoneNumber: String by map val income: Double by map }

Slide 27

Slide 27 text

@aida_isay map delegate override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val map = mapOf( "firstName" to "Android", "middleName" to null, "lastName" to "Summit", "phoneNumber" to "212-212-2121", "income" to 100.00 ) val person = Profile(map) println(person.firstName) }

Slide 28

Slide 28 text

@aida_isay map •Simplify access •Define expected structure •But have a power to access unexpected fields •Map key == property name

Slide 29

Slide 29 text

@aida_isay 4. Observable

Slide 30

Slide 30 text

@aida_isay Observable delegate class AboutMeFragment : Fragment() { val disposables = CompositeDisposable() var isLoading by Delegates.observable(false) { p, old, new -> buttonDelete.isEnabled = !new buttonSubmit.isEnabled = !new progressBar.setVisibleOrGone(new) } . . . super.onViewCreated(view, savedInstanceState) buttonSubmit.setOnClickListener { isLoading = true viewModel.updateProfile("Android", "Summit", "[email protected]") .subscribe { isLoading = false } .addTo(disposables) } } }

Slide 31

Slide 31 text

@aida_isay Observable delegate class AboutMeFragment : Fragment() { val disposables = CompositeDisposable() var isLoading by Delegates.observable(false) { p, old, new -> buttonDelete.isEnabled = !new buttonSubmit.isEnabled = !new progressBar.setVisibleOrGone(new) } . . . super.onViewCreated(view, savedInstanceState) buttonSubmit.setOnClickListener { isLoading = true viewModel.updateProfile("Android", "Summit", "[email protected]") .subscribe { isLoading = false } .addTo(disposables) } } }

Slide 32

Slide 32 text

@aida_isay Observable delegate class AboutMeFragment : Fragment() { val disposables = CompositeDisposable() var isLoading by Delegates.observable(false) { p, old, new -> buttonDelete.isEnabled = !new buttonSubmit.isEnabled = !new progressBar.setVisibleOrGone(new) } . . . super.onViewCreated(view, savedInstanceState) buttonSubmit.setOnClickListener { isLoading = true viewModel.updateProfile("Android", "Summit", "[email protected]") .subscribe { isLoading = false } .addTo(disposables) } } }

Slide 33

Slide 33 text

@aida_isay Observable delegate class AboutMeFragment : Fragment() { val disposables = CompositeDisposable() var isLoading by Delegates.observable(false) { p, old, new -> buttonDelete.isEnabled = !new buttonSubmit.isEnabled = !new progressBar.setVisibleOrGone(new) } . . . super.onViewCreated(view, savedInstanceState) buttonSubmit.setOnClickListener { isLoading = true viewModel.updateProfile("Android", "Summit", "[email protected]") .subscribe { isLoading = false } .addTo(disposables) } } }

Slide 34

Slide 34 text

@aida_isay Adapters in Java public class NameAdapter extends RecyclerView.Adapter { public void updateNames(ArrayList names){ this.list = names; notifyDataSetChanged(); } }

Slide 35

Slide 35 text

@aida_isay Adapters in Kotlin class NameAdapter(val context: Context) : RecyclerView.Adapter() { var list = emptyList() fun updateNames(names: ArrayList) { list = names notifyDataSetChanged() } }

Slide 36

Slide 36 text

@aida_isay Adapters in Kotlin class NameAdapter(val context: Context) : RecyclerView.Adapter() { var list by observable(mutableListOf()) { _, old, new -> if (old != new) notifyDataSetChanged() } }

Slide 37

Slide 37 text

@aida_isay Observable •Lambda called after new value is set •Have an access to old value •Notify ui immediately about changes in data •Advantage of partial ui update

Slide 38

Slide 38 text

@aida_isay 5. Vetoable

Slide 39

Slide 39 text

@aida_isay Adapters in Java public class Adapter extends RecyclerView.Adapter { public void updateNames(ArrayList names){ if(names.size() <= 5) { this.list = names; notifyDataSetChanged(); } } }

Slide 40

Slide 40 text

@aida_isay Adapters in Kotlin class NameAdapter(val context: Context) : RecyclerView.Adapter() { var list by Delegates.vetoable>(mutableListOf()) { _, old, new -> new.size <= 5 } }

Slide 41

Slide 41 text

@aida_isay Vetoable delegate class NameAdapter(val context: Context) : RecyclerView.Adapter() { var list by Delegates.vetoable>(mutableListOf()) { _, old, new -> if (new.size <= 5) { notifyDataSetChanged() return@vetoable true } return@vetoable false } }

Slide 42

Slide 42 text

@aida_isay Vetoable delegate class MainActivity : Activity() { var firstName: String by Delegates.vetoable("") { prop, old, new -> when (new.length) { 0 -> hideValidation() in 2..20 -> showValidData(new) else -> showError() } new.startsWith("a") } }

Slide 43

Slide 43 text

@aida_isay Vetoable •Lambda is called before new value set •To set or not to set •Have an access to old value •Notify ui immediately about changes in data •Advantage of partial ui update

Slide 44

Slide 44 text

@aida_isay Custom Views

Slide 45

Slide 45 text

@aida_isay override fun onCreate(savedInstanceState: Bundle?) { . . . buttonLike.setOnClickListener { likePost() } buttonSend.setOnClickListener { sendComment() } editText.addTextChangedListener(object : TextWatcher { . . . override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { when (count) { 0 -> { buttonLike.visibility = View.VISIBLE buttonSend.visibility = View.GONE } 1 -> { buttonLike.visibility = View.GONE buttonSend.visibility = View.VISIBLE } } } }) }

Slide 46

Slide 46 text

@aida_isay editText.addTextChangedListener(object : TextWatcher { . . . override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { when (count) { 0 -> { isInputEmpty = true buttonLike.setBackgroundResource(R.drawable.ic_favorite_gray_24dp) } 1 -> { isInputEmpty = false buttonLike.setBackgroundResource(R.drawable.ic_favorite_gray_24dp) } } } }) buttonLike.setOnClickListener { if (isInputEmpty) likePost() else sendComment() }

Slide 47

Slide 47 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private var sendToLike: AnimatedVectorDrawableCompat? = null private var likeToSend: AnimatedVectorDrawableCompat? = null private var showLike: Boolean = false init { showLike = true likeToSend = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_favorite_to_send) sendToLike = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_send_to_favorite) background = likeToSend } . . . }

Slide 48

Slide 48 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { . . . private var showLike: Boolean = false . . . fun showLike() { if (!showLike) { morph() } } fun showSend() { if (showLike) { morph() } } . . . }

Slide 49

Slide 49 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private var sendToLike: AnimatedVectorDrawableCompat? = null private var likeToSend: AnimatedVectorDrawableCompat? = null private var showLike: Boolean = false . . . fun showLike() { if (!showLike) { morph() } } fun showSend() { if (showLike) { morph() } } private fun morph() { val drawable = if (showLike) likeToSend else sendToLike background = drawable drawable?.start() showLike = !showLike } }

Slide 50

Slide 50 text

@aida_isay editText.addTextChangedListener(object : TextWatcher { . . . override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { when (count) { 0 -> buttonLike.showLike() 1 -> buttonLike.showSend() } } })

Slide 51

Slide 51 text

@aida_isay buttonLike.setOnClickListener { TODO("????") } editText.addTextChangedListener(object : TextWatcher { . . . override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { when (count) { 0 -> button_like.showLike() 1 -> button_like.showSend() } } })

Slide 52

Slide 52 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private var sendToLike: AnimatedVectorDrawableCompat? = null private var likeToSend: AnimatedVectorDrawableCompat? = null private var showLike: Boolean = false init { showLike = true . . . } . . . }

Slide 53

Slide 53 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private var sendToLike: AnimatedVectorDrawableCompat? = null private var likeToSend: AnimatedVectorDrawableCompat? = null var showLike: Boolean = false init { showLike = true . . . } . . . }

Slide 54

Slide 54 text

@aida_isay buttonLike.setOnClickListener { if (buttonLike.showLike) likePost() else sendComment() } editText.addTextChangedListener(object : TextWatcher { . . . override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { when (count) { 0 -> buttonLike.showLike() 1 -> buttonLike.showSend() } } })

Slide 55

Slide 55 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private var sendToLike: AnimatedVectorDrawableCompat? = null private var likeToSend: AnimatedVectorDrawableCompat? = null var showLike: Boolean = false var isLiked: Boolean = false fun setLikedView(isLiked: Boolean) { this.isLiked = isLiked if (isLiked) { likeToSend = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_gray_favorite_to_send) sendToLike = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_send_to_gray_favorite) } } }

Slide 56

Slide 56 text

@aida_isay override fun onCreate(savedInstanceState: Bundle?) { . . . getPost() .subscribe({ buttonLike.setLikedView(it) }, {}).addTo(disposable) }

Slide 57

Slide 57 text

@aida_isay override fun onCreate(savedInstanceState: Bundle?) { . . . buttonLike.setOnClickListener { when (buttonLike.isLiked && buttonLike.showLike) { true -> unlikePost() else -> if (buttonLike.showLike) likePost() else sendComment() } } }

Slide 58

Slide 58 text

@aida_isay One View

Slide 59

Slide 59 text

@aida_isay sealed class ButtonState { object ShowLiked : ButtonState() object ShowUnLiked : ButtonState() object SendComment : ButtonState() }

Slide 60

Slide 60 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private val sendToLike by bindAnimation(R.drawable.avd_send_to_favorite) private val likeToSend by bindAnimation(R.drawable.avd_favorite_to_send) private val unlikeToSend by bindAnimation(R.drawable.avd_gray_favorite_to_send) private val sendToUnlike by bindAnimation(R.drawable.avd_send_to_gray_favorite) }

Slide 61

Slide 61 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { private val sendToLike by bindAnimation(R.drawable.avd_send_to_favorite) private val likeToSend by bindAnimation(R.drawable.avd_favorite_to_send) private val unlikeToSend by bindAnimation(R.drawable.avd_gray_favorite_to_send) private val sendToUnlike by bindAnimation(R.drawable.avd_send_to_gray_favorite) } fun View.bindAnimation(animateRes: Int) = lazy { AnimatedVectorDrawableCompat.create(context, animateRes) }

Slide 62

Slide 62 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { var state by Delegates.observable(ButtonState.ShowUnLiked) { p, oldValue, newValue -> when (newValue) { ButtonState.SendComment -> { when (oldValue) { ButtonState.ShowUnLiked -> morph(unlikeToSend) ButtonState.ShowLiked -> morph(likeToSend) } } . . . } } }

Slide 63

Slide 63 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { var state by Delegates.observable(ButtonState.ShowUnLiked) { p, oldValue, newValue -> when (newValue) { ButtonState.SendComment -> { when (oldValue) { ButtonState.ShowUnLiked -> morph(unlikeToSend) ButtonState.ShowLiked -> morph(likeToSend) } } ButtonState.ShowLiked -> { when (oldValue) { ButtonState.ShowUnLiked -> setBackgroundResource(redLike) ButtonState.SendComment -> morph(sendToLike) } } ButtonState.ShowUnLiked -> { when (oldValue) { ButtonState.SendComment -> morph(sendToUnlike) ButtonState.ShowLiked, ButtonState.ShowUnLiked -> setBackgroundResource(grayLike) } } } }

Slide 64

Slide 64 text

@aida_isay override fun onCreate(savedInstanceState: Bundle?) { . . . RxView.clicks(buttonLike) .subscribe({ when (buttonLike.state) { ButtonState.SendComment -> sendComment() ButtonState.ShowLiked -> unlikePost() ButtonState.ShowUnLiked -> likePost() } }, {}).addTo(disposable) val textChanged = RxTextView.textChanges(editText) Observable.combineLatest(apiResponseLikeStatus, textChanged, BiFunction { t1, t2 -> return@BiFunction when (t2.count() > 0) { true -> ButtonState.SendComment else -> if (t1) ButtonState.ShowLiked else ButtonState.ShowUnLiked } }) .subscribe({ buttonLike.state = it }, {}).addTo(disposable) }

Slide 65

Slide 65 text

@aida_isay override fun onCreate(savedInstanceState: Bundle?) { . . . RxView.clicks(buttonLike) .subscribe({ when (buttonLike.state) { ButtonState.SendComment -> sendComment() ButtonState.ShowLiked -> unlikePost() ButtonState.ShowUnLiked -> likePost() } }, {}).addTo(disposable) val textChanged = RxTextView.textChanges(editText) Observable.combineLatest(apiResponseLikeStatus, textChanged, BiFunction { t1, t2 -> return@BiFunction when (t2.count() > 0) { true -> ButtonState.SendComment else -> if (t1) ButtonState.ShowLiked else ButtonState.ShowUnLiked } }) .subscribe({ buttonLike.state = it }, {}).addTo(disposable) }

Slide 66

Slide 66 text

@aida_isay class SendLikeButton(context: Context) : Button(context) { var state by Delegates.observable(ButtonState.ShowUnLiked) { p, oldValue, newValue -> when (newValue) { ButtonState.SendComment -> { when (oldValue) { ButtonState.ShowUnLiked -> morph(unlikeToSend) ButtonState.ShowLiked -> morph(likeToSend) } } ButtonState.ShowLiked -> { when (oldValue) { ButtonState.ShowUnLiked -> setBackgroundResource(redLike) ButtonState.SendComment -> morph(sendToLike) } } ButtonState.ShowUnLiked -> { when (oldValue) { ButtonState.SendComment -> morph(sendToUnlike) ButtonState.ShowLiked, ButtonState.ShowUnLiked -> setBackgroundResource(grayLike) } } } } private val sendToLike by bindAnimation(R.drawable.avd_send_to_favorite) private val likeToSend by bindAnimation(R.drawable.avd_favorite_to_send) private val unlikeToSend by bindAnimation(R.drawable.avd_gray_favorite_to_send) private val sendToUnlike by bindAnimation(R.drawable.avd_send_to_gray_favorite) private val redLike by lazy { R.drawable.ic_favorite_red_24dp } private val grayLike by lazy { R.drawable.ic_favorite_gray_24dp } private fun morph(avd: AnimatedVectorDrawableCompat?) { background = avd avd?.start() } } fun View.bindAnimation(animateRes: Int) = lazy { AnimatedVectorDrawableCompat.create(context, animateRes) } sealed class ButtonState { object ShowLiked : ButtonState() object ShowUnLiked : ButtonState() object SendComment : ButtonState() }

Slide 67

Slide 67 text

@aida_isay Multiple Views

Slide 68

Slide 68 text

@aida_isay data class UiModel( val loading: Boolean, val firstName: String, val employmentStatus: String, . . . val shouldAnimateView1: Boolean val shouldAnimateView2: Boolean )

Slide 69

Slide 69 text

@aida_isay private val reducer = BiFunction { previous, intent -> when (intent) { is ContinueClicked -> { previous.copy( ... shouldAnimateView1 = false, ) } is AddressChosen -> { previous.copy( ... shouldAnimateView1 = false ) } is ProfileInfo -> { previous.copy( ... shouldAnimateView1 = true, ) } is OccupationChosen -> { previous.copy( ... shouldAnimateView2 = true, shouldAnimateView1 = false ) } is EmploymentStatusChosen -> { previous.copy( ... shouldAnimateView1 = true, ) } is DateOfBirthClicked, is StateClicked -> previous.copy( ... shouldAnimateView1 = false ) is DialogDismissed -> previous.copy( ... shouldAnimateView1 = false ) is ButtonsState -> previous.copy( ... shouldAnimateView1 = false, shouldAnimateView2 = false, ) else -> previous.copy() } }

Slide 70

Slide 70 text

@aida_isay override fun onViewCreated(view: View,savedInstanceState: Bundle?) { . . . val firstName = RxTextView.textChanges(et_firstName.editText) val middleName = RxTextView.textChanges(et_middleName.editText) val lastName = RxTextView.textChanges(et_lastName.editText) val phoneNumber = RxTextView.textChanges(et_phoneNumber.editText) val employmentStatus = RxTextView.textChanges(et_es.editText) val combinedEvents = Observable.combineLatest( firstName, middleName, lastName, phoneNumber, employmentStatus, Function5 { t1, t2, t3, t4, t5 -> TextChanged(t1, t2, t3, t4, t5) }) .skip(1) . . . }

Slide 71

Slide 71 text

@aida_isay override fun onViewCreated(view: View,savedInstanceState: Bundle?) { . . . viewModel.apply { bind(Observable.merge(combinedEvents, intents.hide())) } viewModel.state.observe(this, Observer { if (it != null) render(it) }) } private fun render(viewState: UiModel) { et_firstName.text = viewState.firstName et_middleName.text = viewState.middleName et_lastName.text = viewState.lastName et_email.text = viewState.email if (viewState.shouldAnimateView1) { et_es.editText = viewState.employmentStatus . . . } if (viewState.shouldAnimateView2) { addAnimatedViews(viewstate.occupation) . . . } . . . }

Slide 72

Slide 72 text

@aida_isay override fun onViewCreated(view: View,savedInstanceState: Bundle?) { . . . var uiModel by Delegates.observable(viewModel.initialStateCallable.call()) {_, old, new -> if(new.firstName != old.firstName){ et_firstName.text = new.firstName } if(old.employmentStatus.isEmpty() && new.employmentStatus.isNotEmpty()){ addAnimatedViews(new.occupation) } . . . } viewModel.state.observe(this, Observer { if (it != null) uiModel = it }) }

Slide 73

Slide 73 text

Just use it!

Slide 74

Slide 74 text

Thank you Aida Issayeva @aida_isay