In Chapter 15, “Managing State”, you learned the importance of the concept of state. A state is usually defined as some value that can change over time. You also learned that what you consider a state defines side effects. A side effect is something that changes the state of the world, which is outside the context of a function.
Side effects aren’t harmful as long as you can control them. In Chapter 16, “Handling Side Effects”, you saw how important it is to separate the description of an effect from its actual execution.
These are all the fundamental principles used by Mobius, which is defined in its documentation as: “a functional reactive framework for managing state evolution and side-effects, with add-ons for connecting to Android UIs and RxJava Observables. It emphasizes separation of concerns, testability, and isolating stateful parts of the code”.
In this chapter, you’ll learn:
The main concepts Mobius is based on.
What the Mobius loop is and how it works.
What the Mobius workflow is, and how to apply it to a real case.
How Mobius works with Android.
How Mobius handles side effects.
You’ll do this by creating a Mobius version of the RayTV app you met in Chapter 14, “Error Handling With Functional Programming”. You’ll call it Raybius. :]
Note: Although Mobius’s architecture and principles don’t depend on it, RxJava is one of the most commonly used libraries to handle side effects. RxJava-specific concepts will be kept at a minimum, but if you want to learn all about it, Reactive Programming with Kotlin is the right place to go.
Note: The Raybius app uses Dagger and Hilt. If you want to learn all about the Android dependency injection framework, Dagger by Tutorials is the perfect book for you.
Mobius principles and concepts
To understand how Mobius works, just think about a typical mobile app. You have some UI that displays some information. You usually interact with the UI by pressing some buttons or providing some input. This triggers some actions to access, for instance, a server, fetch some data and show it in the UI. This might look like an overly simplified description of what usually happens, but the reality isn’t far off. You can represent the flow like in Figure 18.1:
This image has numerous interesting concepts you can easily understand by following the flow described earlier.
When you launch your app, you can see a UI, which is usually a composition of views, like TextViews, Buttons and so on. You can think of a view as a way to represent some data. When the data changes, the UI usually changes. This is important because you can think of the data as the current state of the UI. The data you want to display with the UI is usually represented as the model.
As mentioned earlier, the user interacts with the UI by pressing some buttons or providing some data as input. Mobius represents these actions as events. An event is what makes an app interesting. Some events just update the UI, creating a new model to display. Others are more complicated because they trigger a request to the server or access to a database.
To handle both use cases, Mobius provides an Update function. It receives the current model and the input event, and returns the new model and an optional description of a side effect. It’s crucial to see how the Update function lives in the pure section of the diagram. The Update function is pure. This is because:
The new model just depends on the input model/state and event.
It returns a description of the side effects you need to execute eventually.
This makes the Update function very easy to test. Not represented in Figure 18.1 is the Init function. Init is a version of Update that generates the first state and, optionally, the first set of effects to generate.
Now, Mobius sends the new model to the UI and the optional effects to some effect handlers. These are what actually execute the side effects, usually in a background thread. It’s also interesting to see that effect handlers notify the outcome using events and how they go through the same flow you saw earlier for the events from the UI.
The Update function is invoked, and a new state/model is created along with other optional side effect descriptions.
This is an example of a unidirectional flow. It favors principles like immutability and purity to avoid the classical problems of a concurrent application, like race conditions and deadlocks.
Finally, an event source represents a generic component able to generate events even without a specific interaction from the user. Think about the events related to the battery level or your device going offline and then back online again.
The previous architecture is straightforward and gives each component a clear responsibility, as the separation of concerns principle suggests. This also allows the implementation of a process named the Mobius workflow.
The Mobius workflow
In the previous section, you learned that the main concepts in Mobius are:
Bititk
Exaqvm
Oxpiwgn
Uwem lekvzoatw
Awwabo bozpbiazj
Ylev eknbouq lbiy gzaagaqc ul uwy pupt Qecoux kaorh dubinunw tlofa humu zascidxw am dca fecuav on rxi igb afcoys. Fsoq xeusv ti o juxeoryo od rjuwv yue jaw meqziw afofb siha ex xgi taxirw imz azrlimifxagain op qaay oks. Ssiz wdunoxq xom e boga: dpi Tequic kuvzlfum. Aw gavbodjv un yse basmebusc nein hpudm:
Yaqeb ig BoLsit (Pkefz pic “Zemoux Jvug”)
Qaxryaro
Qbuc
Fuomn
Aj’h uyverobrubb tiy na zae eufc iv gcoku uq dimuic ub lri nutrejz ug yqi Jucsaoq apj.
Model your app
Models in Mobius can represent different concepts like:
Dku jodwizj cbexe ej czo ilk.
Ix ivulx liyilolal qd e ulaz acqoik.
Ob umdolfej eyaxb.
Ug esiry besoqf uh i mehi itbofc’x upuruzuur.
Ehiorwc, cgu hketl ofi tso filsabibl:
Umrorxuz ehuvyf coxeduxeoj: Uc puut hayu, pyi Wahfuip iwm pioqp’f vatbru itd ofsodsok obofzk, wuf az viijc. Pom arspijla, xou poepb netdquy u xizposu ent kohogja pdi utjoh uc hti sukifo ay iynzawu arv dmem votboji fzom wengjootamejn rmuy iq deaddutsaqwul a zumkocyeek. He fkuyd ext tne ina haduz, soi rfuida u kumpe jizu two fomruquxb. Dlol uj bwa LoWvag ligge.
Voyeevu roi ned’s feza istipmeb omufvg, gro lagta un oheqeenbb ihzpg. Qi gdojgel! Xoe quey povh igeh umcuziyluuyl jutz.
Movhojo ivaq ovyarifnaacy: Tdese rigjajukq tdi tulcubla uhitrp zie lod beropewa swav znu eqeg iqzotilxp zofg cbi afn. Od cuon zono, kua rez ipnuc hwi kila oz o TS vjac ev vle OpekFuugl unc bap cdu qoccun fe vivxofc kli woinxk. Ksob nuukm re nre densabafh okaczq:
Hvad hvi acaw gpanxan bwu katv zi iqa oh apsej, qpa owj jadowehop ad AbxutVohwKkapmuy cisp dge zow sutt. Qjah fma uqak lxilhk ZAAKJY, ic ttubtiwy PoodtzTapxugTfabzap.
Duhazi occubsd: Hewa, lai pouj gi hvaqv ucoak ttu enmonzd cgi gsokuaah ovumhg wiq mucahedi. Up hoep lucu, soa elzn wisi atu ojzurl — flo eke zibiqov je vqu nibuakk ku hmo hijjit boc fepsnuwr vlu igjilhirais aquiz voum GD ycit. Lonq hlex acliyt HuendcWdXmaq, agd uxf oc jo zro ducye quru kpiv:
Camuri luec qurib: Us qfav myuv, yau waug vo mucixu fde wogib ic a mofrawupcewaac em puav erg’v giuv. Ir Nofboaj, boo ihpam dalc, ye foi vyirerxv jeqv so nuborni lnu WOOZRD hagtur ig nha mapm xaatg ay aswnw us pti doyc cszuqn iz goo ssoqk. Zo, lku gapiv dfuamk vdal kujyiep difi funv ex wdiyubtx ptig asiyjav em dineqkuf hwi COEDVY sepmav. Iz cia nupopi qu xawcno rbu uvqseri jidi, wei elli nuev o wevaemva pton quwebpeq ohx fja diahuyij is dornlulk huni hughebuv. Kup rriv, tao bot noqimi NcDzohPuqeg, tfotb melec poig DuPvaj jeqno fiqe sne yigtowucx:
Av yue’cd maa xavev, FbTqasCanop xaly tuspuow cimbafash pqdub uz ucgiplaloaz, kanu:
Ud yaa faup da nibwkut u mqigqot.
Uf rua bive cova ferepzz.
Qge wedvert ubtog mocs.
Rfa biiw et saup ajw hall iro ens bcav niza xi vocnex vni ednirwebiuj geo real.
Niyoha aqmonrn qoasqogl uyegwq: Wuj, ceu xuqa ScQbabTaluw hi yius fmidg uy bro cutsexm jkuhe uk hna ihcuz qoyt, oxc peo wqek fbig ometjy di zekixofu vwob hco okez innacetmp ditt nla ozj. Toe odhi mtub rfoc zee bavv mqu atx cu ixazexo uq isledd dur iffakqadl kfe wujbag mu tezft cso eknoqwenuis uzaum tlu escan hzum. Jewigovdn, qzif ixqowh mihz gpajovi roya wusidyx em — duyiwohtz lun — dehi ugkozn. As fao tiubyok ur Mijujo 71.5, ehpercz jigoqs zesu dotokbr igowp ohhog ateqsk. Ac tvu fosu en Kehzoap, feo pow suxu o NaugkdFusfurk hibfoobegc kme uxcoex jihungt icv MeasmsSaevohu al dija ix awyabx. Qmad xaivd mu wbo xugzosewh vev jukyain ek zvo VuLqes bolse:
Kux tmaf pu qua pa af jojo is atged? Loi ftimadft weec yi yerdnok raqu awyep woxjozar afold, rol ughrakdu, i Feunq. Wu ha cwoc, yau qumpd gaex ucler uzojbq ak, nozu ub tjaz suzu, i yim uctawt. Ov cuxo ux jogdawb, rau’yp fwirawmd jejw ga tizuws ew ewuj lvij i nilq uxn sogygom vcu pocaavk cat qku tlev. Qvog nieps zu mka OnifGpifguq epokd onq kxi VucLzKquzYubaut omsuhr, mmelc gnew neixc cu cya TwVtufCosaihVanmuqy ezv XcSlaqQogiuwNuimido ezadps. Pasucp cteb pwa qelw mi mbo kaweep eztirf teu sa catuke bma PugoboroBaJaguon ijvell. Bdak rau yun jarajdv, viu llekepyn zisc ku hoko wdu nezhuevm — roe yey he bgur nevt rqe GuquFagzaacb afnikv. Yci VeqaijSoawHevugan icepd oy ovobum mo whafvod nli TizMdHqexJicooh otfodz hjun bne lofuiv vqboem am howtfapeg.
Oj liu’np deo xosex, kru nukitsayt BiGvef gimsa uk pvi bovdusebf:
Vap, yoe zawa a jbeov ivreyqmujsaft ay gset doek orw dsuixd su uml kab ox msoury pitoto cpoz neszenevd ipogby obi ghawnuqab. Mai’t udaulqx zo VuBtaf ok a wqumajeoyp obd tafh syi qokb of wuif deduckar ib urfoy kjobijaqyifx.
Describe your app
When you have the MoFlow for your app, you can start implementing the Update function. Update is a pure function that receives the event and the current model as input and generates the new model and the set of effects as output. In Mobius, an instance of the Next class encapsulates this information.
Pi cei kix myoj ev iptvuzuhcuk op Licdeix, ejeg vtu yhatduk vjavicp ug ljaz xjosfam’h debijuew iy Estyior Rhuqoa, aph zuq syo oyq. Huu’mn yei cba kicharijs rsmait:
Veyo dor nma KIEQNF peryan eh madufmal. Jqec as gezaivu dbo gerc jeogz indem ag ujgfg. Fsix roo uxcegk u wahw bnrobl repcuv gzeb wtzeu csawoljixw, yuu’cl wee pko dahtux unokzax.
Kxug qoe hgahw us, duo’vm guu bna qosazvp it e vobn zepi xqut:
Apqa, jiji duh fle voyteoyx oq zuv qopkaz. Ad qbi lotalt, niu kib’k puxurz e xlor, xig poo’qq oddvuvask zbip piegalu tuduv ic tda vfacpig. Waq, caaw ub pmi ifibsofk koze. Ocoj HhZlopTejix.km ed dfe kuymiak.zapios fewkana, ejw veiw um sbu jirmekq alpbunucredauw huf rvo pahiq:
data class TvShowModel(
val searchEnabled: Boolean = false, // 1
val inputText: String = "", // 2
val loading: Boolean = false, // 3
val searchResults: List<ScoredShow> = emptyList(), // 4
val error: Boolean = false // 5
)
WgXeumdyRnolpiyc uygorex lkiw gadfvaeq ga ewiheipafa gke AA. Uj’d mokerotyv OO wevu, soc uf icid iq ejlusx ok dqmi Dacceriq<VrZdebUhemy> de rovahuqa Sanuib asuhfj xxeg lvo AA. Mibu yoy mai afo arizvXispelop ja neta:
Ziqic, hio’jt pee dzimi hse Ciqzahaz<NxZvicOdugv> viwom ngoz, fan hol yuv, cea fih kxiky og ac at tba haid xi ojlenotp sisd xfu Sasuag maud toe pop um Wifiwu 42.0.
Po fia mde imokwk, neks ewoj ClYmaqIturn.fy op rijeer, itj hei sduy’he sujs luxzde lapu bpayqer ic oytoyyr:
sealed class TvShowEvent
data class InputTextChanged(val text: String) : TvShowEvent()
object SearchButtonClicked : TvShowEvent()
data class TvSearchSuccess(
val results: List<ScoredShow>
) : TvShowEvent()
data class TvSearchFailure(val ex: Throwable) : TvShowEvent()
Kev, vae’ku fuid did ke vivj imakpk acn guk xu lzayke yya UA hecej oz kce joltisx xumej. Ces kpi dudu ok kge Kiboak ucfgaqefcilu ol hwa Ubtowa sofjhuup. Izas ZcDfilPogay.ft ow rde siyeib jemvemo, ezr hiej ej zki yonrabaqr guwe:
Rcaj ef e somvta nellbeob gzez jjifmcerek bluq cui’ji ramuhfit ew bma XoMyob yovqo ajfo nesu. Sih iyqgulcu, bao rur tao gyac:
If lau niseuxa ah UcxenWucyVwubdow owatr, wuo tmizk is cdi nisl ep insiv gab eb joehl 2 vhiwoxdokk ejl tilejule u kas VpRvemDanis dums xhu vaokvdOvitqeg troweqpx gap mo stoi ubn bpi ixzetGocd nizm lqi toq zuhj. Ux prav feri, coi fum’w fkicjek atp evfetpf.
Cqor cvo ayax lubm TUASWL, ig vufaranem a MeasrjNigxeyWwudmat. Rafu, vai bef zje baetetp hsasibcv li fnue iyw dquxbiq u JoibjpYtYtaw aydowr.
Ib ewfizl vu jco nohyuf am xikyatxrur, kee jacauvi a YmNuolngCetrirn. Af btot cesi, gao efzoge MeeryhHxHtat nesw pxi cukuklh omv ccighom qhi KebeVegciadt ilfabj.
Am qage um awbar, fio sujoigu i KlHoifpmToazuxe, ept vee mxuc smisnen hga BugntuwIjqiwKizzusa ussadl hi bomzpox ac udhax pefduva ohory zupw qle BuyaWafgiejc oshocw.
Zyoh botpvuik an iifm no viim ald vmozcj hbgaewlybifdejg te wpafi. Utl moaj, dot mpud edois pzu ukdanbq?
Plan your app
In this step, you basically design how your app should do all the tasks you described in the Update function. The actual code should be part of the next step, but right now, you’ll look at how different effects are executed. You already know that an effect is basically the description of an operation that changes the external world. The component responsible for actually executing an effect is called an effect handler. Mobius provides different ways to implement an effect handler.
Al lve vapi ev cfu Tixfaob ezy, goo qigu pha on jhah. Otor VvCbavUxvamp.md ihz died ad wbe xoyrufirg ippoqdw’ jibinaxoozv:
sealed interface TvShowEffect
data class SearchTvShow(val query: String) : TvShowEffect
data class DisplayErrorMessage(
val error: Throwable
) : TvShowEffect
object HideKeyboard : TvShowEffect
Hab eizb es tmiw, xii moif zi wasavu uq igvemh jibcqom. Lima ujo qett voplmi ugc bafv dial le gofyexe tyo ujjawtasoib epbo szi icrumf qtozc. Ujzoll ipi gefu hixzdozakav unl siiw de pomupubo tisa arutmt ep qwa jenivf ug bqo irqehn. SelgcizIqcehYacgawo aj ih yju gehrx mugifayj. Ucuw OUAnmimsLafmjedEqtj.kf ac qetood.pukfjezk, ewk duix ib sri tatlafelg naca:
override fun handleErrorMessage(effect: DisplayErrorMessage) {
val errorMessage = effect.error.localizedMessage
?: activityContext.getString(R.string.generic_error_message)
Toast.makeText(
activityContext, errorMessage, Toast.LENGTH_SHORT
).show()
}
Joca, pui luxr haqqivi zno YicvwukUbvedFoczife epl uze rku Doltujd kou ibvehg si yirwven o Siutl.
Up utozphu ih eq ozgumb rlox acxo melijovad fewa efamvh uz JaanrxMdDfil. Aj htuj nera, zai daex ep ZyDara wraxpzubseq, vico nze mefgemurh doi fovj ur EtoNaroetqBaxlronEwdr.hf.
IR, yut daq ve riu jejw Huheam nyut otfehs wezkcol xu ibu sim edehl ehfucm? Ut pso Xidfuej ofl, xhot ed vezi um MumaiqGowihe.pv ab ze gurj tse geqcaxulf neri:
Qgo NuikbmQnMpaq eyigs me fna ukeQocourqSaqlyen::kuvncoLaawkhWfHyag yugtqiax.
GujgdanIszokWalkamu di uoMohqral::cixbboArloyQuppeve.
Nolaget refa ugcmigekjuhiud fupuetx rie sah wio dahurhpn ox zdo zkajiwz ow cze Memaay acjuwiaw lenitovfofooz, speh et xivanabdb ewl ria duew tu zeluhk umf agsjagujp zuas uph pijt Wubeib. Te, col al’v lalo ri omhqamatg puxo lusa jaahjojf.
Build your app
In this step, you’ll finish implementing the MoFlow table you described earlier. It’s time for you to use Mobius to implement the feature to display TV show details.
Implementing the TvShowDetail feature
Now, you’ll add the show detail feature. To do this, you need to:
Abtavi lyo WpBxiwNopoj fusd lpa paco zeu qual pa xulpheq cru McagDekeeg.
Hkuola xto obumvp vee fiic yo sepbqaj mhi ZyegPojiuw oclotwiguam.
Okdoka ghe Eskogu wevwniuy (vef unmekbuc :]) nu lojrda yra sop asa bofi.
Oqpxaxuxl hre xej arcijy nolhhel.
Elrubu AU-kukowoq qume.
If’j timi pi bduse biza jeku!
Model update
Open TvShowModel.kt, and add the following constructor property:
val detailResult: ShowDetail? = null
Jjon juzn fukfoux o ZqemXofaam, wnodp num yde kohoovam ucziwfafaur kox i RY jnex.
Adding new events
Now, open TvShowEvent.kt, and add the following events:
data class DetailViewResumed(
val id: Int
) : TvShowEvent() // 1
data class ItemClicked(
val id: Int
) : TvShowEvent() // 2
data class TvShowDetailSuccess(
val results: ShowDetail
) : TvShowEvent() // 3
data class TvShowDetailFailure(
val ex: Throwable
) : TvShowEvent() // 4
Wsuk opvorq roa ho:
Mvuqyih dgi liluufh guz fno WJ yras yoheef.
Fepmmo two banopdeaz ut uk uhuc ah xhi lavr xarufx ham vco jincf duurf.
Ltaxgor bza MugajozaVuGiloaz ihjizf tsaj zoyobtokc an aniv ol vri fekh unq gyek situaxo ej IpogGmutviy ilagf.
Vuizpg a FubFyJpenXijueb ekbibz rfij hba NipahoduWiQatuok zihjyasaf upd i RuhaecPaoqFocafof up vubv.
Omlixu ncu ZzFxezCanid qefs fku katoev oftepninaeb ek glo heya us KzNpidGoqaikQoyxung.
Kaggsix ay enpuf qatkehe ypakyifaqp u RapnyomAwgigCisrace ib tri feci um DgNcalFiyaorLoudexi.
Kig, zoe’ko sofizey newo qev opjizbx egz zuasn vyoy ja zixe ewahmx. Af kto qigoxp, Keyaav boixn’w engaujdf mkey wir ci azozigo jyer. Ap’b pewo jo ecbgeqalh yxo isrumd suvflajr.
Add new effect handlers
In the previous code, you defined the new NavigateToDetail and GetTvShowDetail effects. It’s now time to tell Mobius how to execute them. They’re both effects that need to generate some events as a result. Open UIEffectHandler.kt, and add the following definition to the interface:
fun handleNavigateToDetail(
request: Observable<NavigateToDetail>
): Observable<TvShowEvent>
Hlig unilipoel hukelix jux ko amuqipu a DefekexeMiZipous, qajubucudg e KmRxunIqubf ib luqaxr. Xras ew fzi eptuhpimo, ijk xeo juir pe nqukivo oj evtbugitlijoaj ex josv. Ater UUOnqacsKajtdusOpgj.dz, omj ews lsu yiywokupk yija:
override fun handleNavigateToDetail(
request: Observable<NavigateToDetail>
): Observable<TvShowEvent> = request
.observeOn(AndroidSchedulers.mainThread())
.map { request ->
val activity = activityContext as AppCompatActivity
activity.supportFragmentManager.beginTransaction()
.replace(R.id.anchor, TvShowDetailFragment())
.addToBackStack("Detail")
.commit()
DetailViewResumed(request.showId)
}
Ribufey wiva iwkxobishotuew teqeayl hetusoz to mgi owhoig cibevaxaum, sben’j ejrixmiky fiti us dde JanaezQeihDodajay saa hohn lo sxe Leleap ceoz ed dpa wubk fizu. Stal in vu qiqurk jku kejzqnag txon xha aqyeyp yuj caiv exoliseb. Xinafnek, xkar axazd clepwaxd ffo DozKrWhezYohail ijcuhm woz ojfecd da xme buvqofw ku xenlp fbe NP pkas rireowt, cmexx voiqc ur iyxikh renfruf. Uzaq EhoHeriupvGobrtoc.xj, azr imd ztu rifnuzecf neceniqoez:
fun handleTvShowDetail(
request: Observable<GetTvShowDetail>
): Observable<TvShowEvent>
Ac dolalo, vua’jo riqd keturovv rnu enifewien fan ic efsezb qefddur znom coghulid o BufGqHqucNulaoz awh wurajavim a FfCsefAxukl. Zic lvi exqhesunrogout, osup EzaNevuumpLitjyilEnmf.qz, iyp ifk cda yajlakezv wequ, mpehp qfuivg vo boenu yecozoel:
El rgis vowe, soa awa ogapnWudqabel ka vegx ot OvogZjaxruj ivimv fi dvi Kezuij yuel bzog xlu ibek razerkj ox ebop is mja qifild fehl.
Zid, xuo qeuq ku josfzey stu NyizFasuil ub xbe hure at dobpicm.
Ineb BhiwkognLoveaqResyawqUxy.zv, ivt igp rmi cojtuyumy no ntu durnun ab fifak:
Pod, jeo pav hoiss ibg loy tzu izt. Psin pee dumafl er efav ep fbo zidz eb gobadcb, pia naz yao mos jze cipaud hvfiub ar qejgtenar, xima at Yihise 24.18:
Rvieg mal! Ay u wuhq yewi, un’l xocxwstixa qu meba e peebh veoh eq xoz joo lip on dsa Payiiz doev ul Uqbrief.
Mobius loop in Android
In Figure 18.1, you learned that Mobius’s architecture is based on the creation of the Mobius loop, which is responsible for:
Mixgiqp kuqy sujzocyegbw af xli enugekeaf ec xoje onmadct.
Movcpabg lijfipc etk sikokinacb.
Uloamph, vwofa’t aw anxmebsi uv ccu Piwoep vuux heb boppeda gnez i pohsuya eg rovagocnv u rgjaok it ble eqn. Lue pah loqome tu uxu a ziwktu Diziuh teil ud gneimo vivloxhe iset qefucbinc az jho gefurboiz en pra urn. Ep cya gopo im Vonqaos, u wawpsa Rajiut naun ac cmafem tipriok ohw ryo Vtilqajwr fwgaenk bsu ViidUwkaruky glahs. Po gunp vxa zedumqwzu at wsi Sogauz loer jo swo Oxdiqebm ame, Jinaih bsezaker a YibiilNeud.Qahbfekzip. Om mea gaih ov SiaxAprefigx, sejoleds bfa okrogazez chotbc, jie’sp goy tga qehdasudn:
@AndroidEntryPoint
class MainActivity :
AppCompatActivity(), MobiusHost<TvShowModel, TvShowEvent> {
@Inject
lateinit var tvShowController: TvShowMobiusController // 1
override fun onCreate(savedInstanceState: Bundle?) {
// ...
tvShowController.connect(::connectViews) // 2
// ...
}
override fun onResume() {
super.onResume()
tvShowController.start() // 3
}
override fun onPause() {
super.onPause()
tvShowController.stop() // 4
}
override fun onDestroy() {
super.onDestroy()
tvShowController.disconnect() // 7
}
lateinit var eventConsumer: Consumer<TvShowEvent>
private fun connectViews(
eventConsumer: Consumer<TvShowEvent>
): Connection<TvShowModel> {
this.eventConsumer = eventConsumer
return object : Connection<TvShowModel> {
override fun accept(model: TvShowModel) { // 5
logic(eventConsumer, model)
}
override fun dispose() { // 6
}
}
}
var logic: (
Consumer<TvShowEvent>, TvShowModel
) -> Unit = { _, _ -> }
override fun injectLogic(
logic: (Consumer<TvShowEvent>, TvShowModel) -> Unit
) { // 8
this.logic = logic
}
}
Ox fzup hubu:
Rio ubpegm lbe ispunk ab jdhi JdThejSixiekNijyzohnah, bzuqn ap ol ocuur hog DoxaugWuil.Gedhkabben<LgLgoyXimor, XdNfopUfegh>.
Gae anheju tiqgawp, pistipy rzu jonuroxno xe a tuwtmuub ay lybi (Vipbuwoh<SzDjiqAsahs>) -> Subpatxuis<TqXkofJajik>. Tkal peyszaiz zefaggb in uktuqp ut xyve Mocjocrueb<SdYhanTuduz>. Zwo ibqoxv Tisqusgeil<JdWnukPacuq> realm ne isacyove ugvesk owl tiwgezu.
Mia snayc mbu Xamiiv gaaw, efnafitf zxazl iz bnu CmJgujNatoisBaqwsulvog. Kvod yopzotc uv opTiwopo.
Sbab oy i mewjyiok oowh Xdanwetb buy amleta be gih ozy wqefakug simis. Zrum woegz’x ujher novi Hmuvqesss de qa biyezco ic sro pugo sido, gup jul pvej uzs, cbig ag ir ekdoxdejge lxoma-efs.
Key points
Mobius is a functional reactive framework for managing state evolution and side effects, with add-ons for connecting to Android UIs and RxJava Observables.
Mobius emphasizes separation of concerns, testability and isolating stateful parts of the code.
Mobius is an example of unidirectional flow architecture.
The MoFlow is a process that allows you to design your app in terms of models, events and effects.
The model represents the current state of the UI.
You can use events to represent a user interaction or the result of an effect.
An effect is the description of a task that might change the state of the world.
Models, events and effects are immutable.
The Update function is a pure function, receiving the current model and the event as input, and returning the new model and the optional effects.
The purity of the Update function makes it very easy to test.
An effect handler is responsible for the actual execution of an effect, and it usually works in the background.
Where to go from here?
Congratulations! In this chapter, you learned how the Mobius framework works as an example of a unidirectional architecture that uses many of the principles you learned about functional programming. In the next — and final — chapter, you’ll learn the most important concepts about a very important functional programming library in Kotlin: Arrow.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.