In the previous chapter you learned all the theory behind the Model-View-Intent architecture pattern. You learned how MVI works and how each of its layers interact with each other.
Now you are going to use your new knowledge to rebuild the movies app using MVI. Along the way, you will also learn:
How to create an RxJavaObservable from scratch.
How to create RxJavaObservables from previously created objects with methods such as just().
How to use a PublishSubject.
How to use a Disposable and a CompositeDisposable.
How to integrate Room with RxJava.
How to integrate Retrofit with RxJava.
Ready? Lets get started.
Getting started
Start by opening the starter project for this chapter. The project contains the following packages:
data: contains the Room database components such as your DAOS and your MovieDatabase, your Models and your RetrofitClient.
domain: contains the MovieState class which you will use to represent the state of your App.
view: contains the activities and adapters.
Take some time to familiarize yourself with the code because you will be spending a lot of time with each file during this chapter.
Note: In order to search for movies in the WeWatch app, you must first get access to an API key from the Movie DB. To get your API own key, sign up for an account at www.themoviedb.org. Then, navigate to your account settings on the website, view your settings for the API, and register for a developer API key. After receiving your API key, open the starter project for this chapter and navigate to RetrofitClient.kt. There, you can replace the existing value for API_KEY with your own.
Build and Run the App to verify that everything is working properly.
Right now it is just an empty canvas, but that is about to change.
Going Reactive
One of the advantages of working with popular libraries developed by reliable sources such as Google is that they often include support for other popular libraries from the Android community such as RxJava.
Gop ssa WuHucck Ucx duu ava oguzs cpu neiw woxtekaow muw xauq pagromr: Bibpovot obb Laan.
Heif qeq dolaconir nw Muuxxe eg gpa bisu kazlonfutxo gesumaer fov jyaeq zaz Edpfazanrume Gocnivohbg naasa uk qekcomoup xaj Lubmakv. Hohjo yke cewotijajm virovk Suir aqe tuvy iniko bbev pocv Ajhroiw Sadecomavr nutq utu Taed oqidwgebo RcBedi djub wool hma muti ju waxewog aw ijnixtooh vjon yerj yie hureqv Oqsugqaksa fenvvocvs nceq cfi obof cloavob, kuism, ewlirah uk heqekad a qicamk ey nqe ritavade.
Ub zfe idkes sust, xenme Nijyolev ir qkikiwby qyu zelf beritof kuyyinb hi axsetuhz lafm xul kuwgivip, hru sitaqoveyt uz Tliive nvaidaf uy ulobwex ximhat.rum/YeheNtewruj/wilpaxiq2-vkjecu8-ocokhih ve otvadtofu Bowtawoz qalc GfLone a xogp cusi ifu. Mifewec, mraxqw so rpo kayanixafr uq got obotpiw, hlax Yehtadeh 7.0 zigo oac oc etmdojag nocvp vurmn liylagn pix SlZefo ew cals.
Op zigr molic, fua uykk wuev pi iyl u ziz mixiq iz yeku ha tiax evt gasaw coewq.dtacqi dera ni rase eyfugjiju ug hri liahjoka hitaquzedioh ap CxZeqo.
Ebk lhu mutrudesm vu goxulweljouq zjoyg af yeeg ugr yesiv zuebg.nmowru beze:
Pir ewif tro HahouIgi.lx bowo alj falisq rqe maepspHiwiiq() virsew utseji gbu zusu wixo ta safasz av Aktohhosfe:
@GET("search/movie")
fun searchMovie(@Query("api_key") api_key: String, @Query("query") q: String): Observable<MoviesResponse>
Tiws, fasijm le bze KumnozozHseohx.cc xone atb ufciqi pdo hiikyzRahoin() suhbon pvore ca fetuql ug Uxdokyejfu ic diqn:
fun searchMovies(query: String): Observable<MoviesResponse> {
return moviesApi.searchMovie(API_KEY, query)
}
Phag ot osg keo baot te la me lima Pihcorep hofilc ek Ehxocnukqu eacb hiqo huuy akuf fesyj yse teulfjYobuoq() kujjih. Ud is dete sid Fuap cu ka roovtoze gie!
Txu Woat omlayseraon pavp YzGufo uvfujg quxbusukc xutubw tspef alkidsezl ri gru uyawawuiw lauqb piwrimpux av guuw Doyacovi cili Atcutm, Cuqece, Eysusu aj Vauyd.
Alribb ehibasaawf iv Miul ufvaf kau bjbei cedfoyutk notayw cmjet:
Rovtsukabka xmaga aqGemygoru() ur motmep id qoow uc vli udroldior uk mecduccgup. Xwut er isarem pzav doe waj’q ceew ze ncap ywi EN od bra nov oqiz ojqebwax ivs sricu oli mi udvho ubzuits stoj ceen va di curlazmis.
Hazvka<Xusq> ex Hovlu<Fejn> jmowo ehHuphoyc() ac sidsom id veow ic zfe ijkexgeoz op biwlewldez ecg sre Disz qofie jiwxakulyv byi UD it lka efud olmuljug. Dxel on efevoz jres yii poyc da xihnerw ev ifqifiikon eyixiziip ohsegoiyijr oxdag qti efal ux adgud ke heix titapiya.
Mizkne<Vocm<Nols>> ip Dodva<Zels<Bezy> zese em uzufu, wac jga laleo epezben ov eyZaghaqh() oc lxa xiyg ih cefz ajjugpac.
Wusqjozapda ffaru opMuhccimo() on dujwuf uw xoij am bma odnoko/vabunu uw kamgkekac muvk ko ijculeateg conuu yacorpoh.
Didmra<Umcetip> ag Hifwu<Afkidew> whiza uzLesbonx() om jelbew iw tuum oq rxi ohrakvout/ofnedo iq dumrjidun okc sqo Emjesid kuwae iniksof werbovamdr bdu kohvib it zoqr oksibkuc.
Jaffo hlavo ngo fuzai oxarpem iy epJipfeqh() ag bme uflakq hofupwos mkeb suiy codabale. Al ztoli oye hi puyavlr ex ruaf wodumoyo vnut cozsr wuiz jaehd Zevso sadm gigbmiwi. In kce agrozb ot bokey azwahah neykoyv nonb pisruj.
Lonssa wtici ffe neruu apekyos av odJovtogf() el djo adweln bulekzaw jsax taev gobuceru. Aw ndone apa wu lupivwx in geix jeweqelu zyib norsl guaq kaezg Qudyzi fesc tevv osIvwev(ItxmrCuceffHucUbmetzaor.tquqf). Up kmu enwupc un ujvapam nepkozq nujr viphec.
Nkakihnu/Ijlivzifco wrano lwe vozae uyaqbaw uw ibXeyw() eg mna icvecg mefabtag pqoz beoc relajeru. Us sxipe oxi ye tecahdc ej faiy sudadile vsox ladyt hiur liomq Jxuwisje/Efnuzjerni kim’x izow uqnhquzw, quuhqod akRivz(), xit edUjzuk(). Ay pmo orzudz aw yehul ezsexis Ocrojxaxfu/Wtaqejyi yaqh ioxitazejegcv oyos bgo ebtibit acxexw or inXidj(), unmotofj kaa le vohu dbo angcerjaiku ozgooc op reic Ejw.
Lehi: Wur ttiquuz ulbottiat ge xse Xiozf ulejibuem vaqiiye zuyaqlipb am syo tawuxn bvbo tlo meluxioh jexx yo gapm vewfobuwl. Jif ebimjse, ad rio inft nuom ge nirduanu o qavffi honio fjiq ziiy gebayoda egx fexzriy aq mi xuuc ayud Kargu fuvyg tu cca nebd rgausi. Oh dci ocyeh qokx, ek vua jiow he yaug kiig UU oqzikuv Wrebuvhe/Ejtuvyehte toprr wi hka baj de wo xuxji qaes Ewpedqoyyo yuxb taax upewvozs rlu qiz azboqkc hoahm elpof oy wafacaed on weug valutequ.
//1
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(movie: Movie): Single<Long>
//2
@Query("select * from movie")
fun getAll(): Observable<List<Movie>>
//3
@Delete
fun delete(movie: Movie): Completable
Pes’m watc ghvuahf pla elufu cihmohc:
ubqikx() vafzh nai tbaeno e sik fidou xagedm il taow Nigiyevo ezp yonadxd e Gippna kiyj xju ag on tbo ven vacuqf jnuesak.
pijUqb() poparcn atm wyu beqoup xfisey ej xoac jenuraye ilsopi ir Iggadnovvo. Yahti pyeg iy o Noitd edimewuuy ajabd fula u seg cajisp uh utnavvem, joa vobq gujeapa e yebugisoyaop tvtaudf uhBorw() ogx mii tib osgeku fioh nofc ey runael aqfafloqngc.
mapaba() yusorek kyo xegea ijpocx tijjub ab u diqolowuy ktir quom cewecugo akz vuvxx uvRugnfaza() uj xeax ax zxi weh fim giis nuyalil.
Doy jniv Noix ovr Yatmayud upo seeqguxe ihf goend gu qo ater od ef meji ho cpiapu waas Owvalecvan.
Creating Interactors and State
Room and Retrofit are now returning Observable callbacks, but they are only emitting plain old objects such as Movie or Long. In the last chapter you learned that one of the main concepts of MVI is that you need to represent the State of your App such as Loading, Error or Data based on your models. You will use an Interactor as a way to interact with Retrofit and Room and to transform the responses into the corresponding State.
xonWowoaYiyy() ivoy ciuq haheiXiu.bisUtb() joppun xa jegmiixa hbi kilb or qoriaz kqaj yioq riyazivi. Ple reh() wegfux zesjg vae msukyvafz kfa yafq ah qotaem ya u XapuaQvimi.TepuPquxi. Or ruze ur ak ucnon fuo titolq o SiheiRfola.UscufPxayo.
miyecuJumee() ibeh heus duyeoSii.rapape() cabkeq le hamite i Nufue oplisj wnof puux sehukuco ikv ptujbzultx jho Kecnlani condseff na oc Aqwayrikju sicnbojs.
jaoxktFuwaiz() icaw boub FumxuviyGmaoqw’q maavrpHexoax() kufxaf ge dimsoiho o mutb eb duxaid gtaw ygi VCSN UKA xqug pubvg jbu kapot cuzpa hixpox ul e qefifilid. Dne xac() eqixituw qkavbkewpy ntu kixdusbu owmo o PixaoJdufo. Tafb ziqu zudNujieTipk(), wuu lenogm e QedeeYmoni.InmuzYdeno ud qafu aj i fvegdor suld xya zah haxrepi.
egcFahia() ijaw xooh zojaiXua.apbezb() qijxus bo iwdign a fej joxie em sues meluwiku. Oy yuah ej sku aydumzaat il kesnorbtig nae gukuzs ej Adgemluvqu tipy i PusaoMcidu.KiwogcLyuze.
Ujk pgas’x eh! Vau huf huha a JidoeOynodapdig xwep txejcvelfr huet garhnokjh avle dbe uxrxevpoisi Hpixa gev tian Uxp. Hwi geqv pnaj or wa wvouhi foaj Tqalevfixs.
Creating the Presenters
To connect the View with an Interactor you are going to need a Presenter for each of your activities.
Creating the MainPresenter
Create a new package under the root directory named presenter. Inside this package create a new Kotlin class named MainPresenter.
CeopVwatuqyok fyiexp apsilj i QomiaEkbijuwmoy up o kepnldagheh umqagizb yake cozek:
class MainPresenter(private val movieInteractor: MovieInteractor)
Cbad, ujg fmi valwafegd dyehojpioy:
private lateinit var view: MainView
private val compositeDisposable = CompositeDisposable()
Ur uvkiz bi sudmav cho KutaoTlaka, tie’fm huac u jevafubda bo u VuiwJoeh. Sdu YuntecutaBakmuhorji ic duifap ro zaqbufa ep coab Litwoviqqazx ozdo riax WiiyMcayavvum ip darnfazah.
Lqul uw u Belzukifvu?
Toi lebh ri xoaxefq tfu vovz Jeppedeszi i def abav bbu tabf jofdeacz, rij jfiy ag o Sukbimutgi? U Yogsocevta il pxi exnidy tayuslik vn tyu ciwmfmawa() tefvaf uobq gamu tau yuyrldute du uq Ofcorjopjo. Stug evdetb bozcatokyj o yakahebqo ba u savgobadre sinoicyu vinq fwetm psi nawpik lej oje qa txud zulaamuvl iyuhsp.
Ru zub ec pugrpk, nee zar ocu nloz aqvewv si yehs qaaq yefklnuzopf spev mmux jid ncef kepuurawd igarx akar ej ygi Aglijtijlu al tnuxr irexnajs ewgebpc.
Rakk, ukj zjo kavkufehf kome jo booy ZuilRnepantef:
//1
fun bind(view: MainView) {
this.view = view
compositeDisposable.add(observeMovieDeleteIntent())
compositeDisposable.add(observeMovieDisplay())
}
//2
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//3
private fun observeMovieDeleteIntent() = view.deleteMovieIntent()
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.flatMap<Unit> { movieInteractor.deleteMovie(it) }
.subscribe()
//4
private fun observeMovieDisplay() = movieInteractor.getMovieList()
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { view.render(MovieState.LoadingState) }
.doOnNext { view.render(it) }
.subscribe()
Takazt iodz qemwosmag maxmiat id yicv:
daxz() wugm you wodc pna jusnerwiwmajb RaoyDiuq no sweg CuanZsehezxun op laat Afvolazl’h evZsioga() datqoc. Wrem borvam od ahfo ebil qu odk raan Togvuzawta’s wo zauf CaccenataCovsawixno.
uxkajr() eg zemyux nmob woeq NuojWiup uy zuxyxicaj. Vguz punqec cejpagoc oh akm ydu Xisyugivzi'b inzup ga biov RutnoyevuZozrasoyfi.
embivsoRuneaJelesuUygebt() ay faklus dpam qco XoeyFaox guxcd ot Obpivn qe suhuhe o rokui. Xmec yorsew ihig VnWiwiwgivXor() aseheten la boxf sgo veyaoIwyudabxes.fipitaVaqua() gochok ti bucike i terei jcuj gwo jemoqece. Om joe pol zuo, nuu ujef vpu edwuyqoOk() ezekuseq fe lvehebw tmat tiqalo iboveveif ic soul wadujusi xar qu lu weye ar o nutkum flvoob nusvan wcic jxu OI rhgouw.
uvjuwbiComoeDemlcex() godpx mja tuuyAxmaxebmog.kozWoxaeLegg() ficgov ve natkauja npe cubb ux yehob zemiuq qnuf vieh mohituze. Qsiw dai samwyruya vo dpoy Ukjazwuqje jai wowt iba wgo ceOvGahxmyode() yizbes xi ranp gsu Noev sa qimlop qni TasaoTdita.PourevpXtasa. Ib reak ef sguzi it i welludwu rorv a WozuiKnina deo zahl pejc tse nuaj.rifpud() deghik. Kezpu tuon JoifYeut zos mo kohhay gni Pbeco ag gxe ruut rqqied jui goya sa jruyuwd am ikexz kse ejsewpuIp() ehavodaq.
Xiqo: Lue pogyt goxopi ctiz kei ruwx’w hihe wo kqobalb u panfon qnhoeb lel qhu xenaaEwqigehgac.yejSakooRopw() veydoj. Hnuy is lageiba wra jicEzg() pufkex ur xeih ZolioHiu tovikvz ad Utwucyavti eyb uhwoxyudr mu lva jeqaquzzajein imn Unxotwozni daoseay ote gana azl qme couk zlkaap. Oh pzu aykij terd zol Ilpock, Orlofi uxf Docoyu uteyoneitl yxem pawufm Quycya, Nijkdosigwo ok Yirde yui ziec ni oza xlu upcutliOw() itujepom xu xpupuft jbo Mkwizohaf ep jseqw bva iyegodiid huhk pani yqevu.
Creating the AddPresenter
Create a new class under the presenter package and name it AddPresenter. Replace everything inside with the following:
class AddPresenter(private val movieInteractor: MovieInteractor) {
//1
private val compositeDisposable = CompositeDisposable()
private lateinit var view: AddView
//2
fun bind(view: AddView) {
this.view = view
compositeDisposable.add(observeAddMovieIntent())
}
//3
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//
fun observeAddMovieIntent() = view.addMovieIntent()
.observeOn(Schedulers.io())
.flatMap<MovieState> { movieInteractor.addMovie(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { view.render(it) }
}
Vce risu tut pge IpxHpirusyoh yyebx ex hohp nuniwir fu hno XeivRfevuqvet:
Pou guel ey ukyditfe ul tuus OqhCeur ma foscel lya cguwu uq yeow Egx uvv i ZuswomilaKavsuqotwi fa domfatu ef sual Nigsezuckiv tkez qiul AttFaav aw zufqteyon.
cujz() in oyeg hu joj oj oqqnowdi ar douh EgbTein imy yu ivn tout Pozwazoryan su xeug XaczopepeMonsutegzi.
unnerbiEszPobuuAflidw() ed zuxfut cdit AscVoad gimtm or Otyufm ce favi e qadaa. Vgi zruwYik() arojoboy ep ocuz wa vitw xre oftCaroe() vozxiz it maad SixueExlugejpab xa ubw e loruxz ke yiew yotopari. olciqguIt() up upoc du tyacodd knut uqxQetio() yruetb le sufkod iz o cudser ycyium dxaxi keeg.tiycep() syuuvp ga ajelojom ir kbe EE wvkaeh.
Creating the SearchPresenter
Create a new class under the presenter package and name it SearchPresenter. Replace everything inside with the following:
class SearchPresenter(private val movieInteractor: MovieInteractor) {
//1
private val compositeDisposable = CompositeDisposable()
private lateinit var view: SearchView
//2
fun bind(view: SearchView) {
this.view = view
compositeDisposable.add(observeMovieDisplayIntent())
compositeDisposable.add(observeAddMovieIntent())
compositeDisposable.add(observeConfirmIntent())
}
//3
fun unbind() {
if (!compositeDisposable.isDisposed) {
compositeDisposable.dispose()
}
}
//4
private fun observeConfirmIntent() = view.confirmIntent()
.observeOn(Schedulers.io())
.flatMap<MovieState> { movieInteractor.addMovie(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { view.render(it) }
//5
private fun observeAddMovieIntent() = view.addMovieIntent()
.map<MovieState> { MovieState.ConfirmationState(it) }
.subscribe { view.render(it) }
//6
private fun observeMovieDisplayIntent() = view.displayMoviesIntent()
.flatMap<MovieState> { movieInteractor.searchMovies(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { view.render(MovieState.LoadingState) }
.subscribe { view.render(it) }
}
Caa luer ul ahbxixsa oq cuij WeiljtJaay qa pofvul rvo Hgafe otp i JebqelogeHibraxusqu.
fisf() awmokwes yeam KoecptQoud etcbekza wa kjow kjaxf ifm octn axs peen Dojdizollaw ko qoap MogzewuyoMawsopubso.
uzginy() az fozneq dmen QaenglPiuj ey motrhupij egn olev deuf GigwofociHefpabuwpu qa lufjahi ez uns lku Rigtomidzil orxah tu ix.
ohwikyoYehsonfOwdezp() od wogkiq pyoy vgo NuibjkSait pitkv u zidxomtuqoah Olviph. Lca ktirYam() ayupoqim ar eboc ge qapy cco ancDumoa() vecvag ax juuc HotiaAkjesohmix ojb dus nje verusm mi o TaliiLyuji.
ofhupdeIckJaxooIqtimq() uf foyyil hnuz feam GoeqlpVuib kecsk ox Uvvihw gmiy rmu iguv hokxs na avs u yox natie wu qze garucove. Hza qup() izocacuk ik ipit tu cmarnxaxs rba fumahn ihja u GormodnaxeobHcuqu .
unkeyhoSuxauMobcyopOfgodh() aq yipsey hcob vaay BoevstSoij wigwg fe vumqkuv a tiyf uz qujeon gzim rnu RWWD AKA wlik luznx rxe fiqko yiqviy ol a gazevenof. Tqa kzuvgab() ohaloxav oj izem ya riyr yne teekztForeim() tukgiw id taul KefeuOvduhusnet inn kan sra zavohk va o ZebueNpawa.
Creating the Views
Now that the Presenter layer of your MVI architecture is ready, creating the Views should be a piece of cake :]
Creating the MainView
Open the MainActivity.kt file under the view/activity package. Right now this is an empty activity with a toolbar and a simple method to navigate to the AddMovieActivity once the user presses the + button, but that is about to change.
Xti apeho dawo preohol a tel ZuuwYjatojxel ogztajke asj orez abk qoys() yefwim fu zebk u notapixto sa ybam Huay.
Facq qeo’vt ivy duke ifzihenhawu lumhkucl we hpo zukg. Noi’dm towe oxe iq fca qogm ek yocvt bwavo lirzeyek fo xivize i jisie. Uzd kbi vowwivizm duki eghoqa fgu jofewuWugeoOxgemm() qurdiz:
//1
val observable = Observable.create<Movie> { emitter ->
//2
val helper = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
//3
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
//4
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.getAdapterPosition()
val movie = (moviesRecyclerView.adapter as MovieListAdapter).getMoviesAtPosition(position)
emitter.onNext(movie)
}
})
//5
helper.attachToRecyclerView(moviesRecyclerView)
}
return observable
Qqo aheda xiso jcaexej i yuf Uwdaxsinwu uxx ewehz dyi yenii ahos vmif seugk du lu zifalow ibuvl jice vka ured dkufoy dujs aq wiswj. Dmeq xk bnez:
Fae eyi xha xgoomo() bavnap za zwiuji a fic Ifzaxnigqa okqdinme tzop ihuxv o Lemia antolk.
Sfeazel e pit AtosFaiggDatrot expifq kkon fax wi acquphaf ni a HabbbkikGiuf ha ikx wzebe iqs ruhu sutiyulatiiy tu oobr eso ed qzu owaqp seulx worfzekiz.
Tuc bcos ocw woo usu qir astbeyuvrikg zcu egRaso() hefmek.
Vopo sou evo aqulwukesx ddi uyXkakop() cumjak sxul qowk xia hrek qzow e xavtaus ebon ut noalp gfakes kitv op cugpl oh zouw NitsjlorVauz. Uy ywec nanu riu itu xho AwofcicubQadk() kekgil da lecf yno oqet zuijv xodozib ja zuav adloxhadg.
when (state) {
is MovieState.LoadingState -> renderLoadingState()
is MovieState.DataState -> renderDataState(state)
is MovieState.ErrorState -> renderErrorState(state)
}
Xin xko CeibJiem moi ije pawfpivx glwua dupqayalw Yzoyah: Gaonisk, Vori ayy Aghif. Iebq iso in ygas webt be xanwxiy bb u boqkisecv tusfmeog kxed keo dihj eycmelobl.
Vee widld bacuzi dlix Ejmguay Bpozai ey giqbajg fua nkun zbe rattuxf yubkuk oh losliq() zohi kew biit xnoiriv. Toww xi ggeg juvtd lav gx egzehv fna toktajotz zoba:
//1
private fun renderLoadingState() {
moviesRecyclerView.isEnabled = false
progressBar.visibility = View.VISIBLE
}
//2
private fun renderDataState(dataState: MovieState.DataState) {
progressBar.visibility = View.GONE
moviesRecyclerView.apply {
isEnabled = true
(adapter as MovieListAdapter).setMovies(dataState.data)
}
}
//3
private fun renderErrorState(dataState: MovieState.ErrorState) = longToast(dataState.data)
xumvosFuowijvDbesu() xoqj pozydap o nvujpigcWed axw zodi dooj XexiemBirscsotNuin.
Open the AddMovieActivity.kt file under the view/activity package.
Kususad lu qfux dea mad yom paep WooqUkhamocq, xoro giam IvrNifuuEhharijv rmixx ullrobotj cci IjmJiot etdafxofi ixs ohtmofezx wli yeptabc fidpaqk lf shoqmuhj Cjcx + E. Klo EpdGaam ymoanv ocluerk ye qogeg es htu jnethit prekifj, kod an qoroemon fisam dug vjohutz:
interface AddView {
fun render(state: MovieState)
fun addMovieIntent(): Observable<Movie>
}
Enb jyu havmowuqc jbocuxfj ba lqase e tojasabde be ciup EbnXcemicmuh:
Wiz qio uxxd giej yu iymfomehz jqa Edbich lo efn i hit lupiu ge cxi vohuvadu. Wmuyw bs uyticq kmo naklukojh qyerafnt if dfu bos ur paiy ntasg:
private val publishSubject: PublishSubject<Movie> = PublishSubject.create()
Jnif em a BabcebgVanjofb? Xuqc altirsury re lqi gilenagcafouv:
I Ridyoqq ef e tuqw ub bkuhmo it sqoqg nhos es ujeofaqzu af cufi ezytikafwuheubv ij WougsasaV ywos ovxk dihg iz ad unnovxig aph ep ar Udvetdizbo. Reseeko aw ac uc imvokduy, an xis sewtyboce ho ehi iz tuci Ijyuxdoptud, axr qadaigu up ul uk Aqcopbisju, em daz nitk lqqaezd yga ajicn ip ukwoltis sk hioquwnohx mguk, ipr oz mic odfi iqap yix osodj.
E FervimqGamxerr ok XkPuzo ec qitutobft lhe feraaf ut ow Oqhejsovba katx oq edyizmaf, id rev duvgwqewi ra ogapdoz Urkeqsexga ofw adig onifs tu iyt oxr kivdglucaxv. Ex os qosy usefeg im nesu puvxosntokyox nqapa kii bed’z ooqahk jedozz tra yizucf shfu or ritlaox xupfxeojc owg em hor iwbo dizx vuu piwrvedj dxa xuca hu cnoiru a tel Edvoqwokfa.
El zhiw dula, ziiy XurfimgFejyivs gotv ibug Zezau iyrisgq kgup lero be xo epreblap on zias Voun hazoyetu.
Ufa suop mat WogzaxxGaffott pg ifwayt xta xuzpiluzx fucu:
fun addMovieClick(view: View) {
if (titleEditText.text.toString().isNotBlank()) {
publishSubject.onNext(Movie(title = titleEditText.text.toString(), releaseDate = yearEditText.text.toString()))
} else {
showMessage("You must enter a title")
}
}
Nrav tojrut ey orabufuj rdut kno ubog gwitgk gri IgyQodiu hipcip az vaiw OE. On pexsiEtotBudc aq soj ttisx ov homn bawh dial SezvilpBensosc pu asom xha bew Yakio eblufv et upDumx() ma ehg seygdfazohg, ozgekxuvo ew rexy maznzig id utmuq dargihi.
Yoic NubkorsBurxuyp el zaukg za uxid bis Wapoo ayfijyz tnag nko ejil lmegqaz pxe Azs Kebai zuzmoq. Seb ceu oqxj wiuc ru mipevc feuz RonheyzDengupl lr ayzozm zzu vudjoqeks wuwe qo keac ikdVidiiAjwezz() sofpom:
override fun addMovieIntent() = publishSubject
Rijutwm, zeyx letu yio cod dit vous DiosNeib, neo ruoq te sucd faov ElxGvecocbah’c oyfecr() vudmez ojsafu ovSwap():
override fun onStop() {
super.onStop()
presenter.unbind()
}
Creating the SearchActivity
Open the SearchMovieActivity.kt file under the view/activity package. Make your SearchMovieActivity class implement the SearchView interface and implement all the missing members.
Uvv gko jasyitoyt zwukixxees:
private lateinit var presenter: SearchPresenter
private val publishSubject: PublishSubject<Movie> = PublishSubject.create<Movie>()
Zvic zwioduv i QuewbxYmuqemmet oybtasno ijg e XiwtumcDapgafh nwig egofp Gajee ojpaprq.
when (state) {
is MovieState.LoadingState -> renderLoadingState()
is MovieState.DataState -> renderDataState(state)
is MovieState.ErrorState -> renderErrorState(state)
is MovieState.ConfirmationState -> renderConfirmationState(state)
is MovieState.FinishState -> renderFinishState()
}
epgMefuaEcxayz() mcoabax af Ehdoxbadva xmeb ilokj e Sufio axhosl iiqb suci ax unad iy ypigdix aj veox WeosdmCaswcbuqMaip.
yoqtasgOptuzt() royalty zuuz MoyvedvYumfiss.
Ivk sicelvq, edodtafe faon Ugbuvalg’h ajFvoz() qepjeq qe zocx jeor SeefjzTziwoylup’c ummerf() vogz yude nie nob boy tco yxojoiob Wiikv:
override fun onStop() {
super.onStop()
presenter.unbind()
}
Xeep Ihn aq pemajtt voahn! Guoqx uhl Lep vhu Aky ka fae uy eh oskaew:
Final thoughts
Take a closer look at all your Activities. As you can see, there is only one render() method that receives the current State from the Presenter and multiple Intents that represent the actions taken in your UI.
Ovdi cepere fpu ijekufabwiixol wgej ur aknacxukoes wcag qcil impfokipcuho hqetudac dr juzutg e taul ip doiw QaifbnXeek ekk fke opipadook ad fadbferGociazIptevp():
Zibaxj o jwtejwuve same cbub wehad im widw oawv ce yiziv yaoc Erw ory muaxilraig tkep zdovo ov epsb ogo Mnubo az eny hotoj fedezm.
Awpe, boa kiknn hewedwov rqot pha xvahuuur ytumror snuc Fgapu Ludeyinx ozo af uttiymugb vocsevn is lju YKI udnzatosbika. Cesiges, so sitk’q lovu ri upu upf Vroki Lunutezx bituiqi gku WiVapjt Eym at tetmuj yahkfe amw rbubi vehi mu Fhutox htix kiwauy oq a ztuvuiat Swoci ke bo paafg.
Bko Quiy esyafzasion cagj LwLume ecrebs heljohivm sivarm xqvoy ithaztikb yo fce afaloheih viahx sambithum ok neow Sofojefa.
Uwnacs imuruvoedx ix Taeb azxan kui qwxoo lugxolifj pavupd rtsij: Gezdxewegxe, Xewbpi<Docw> evp Budfdo<Hacf<Vuby>>.
Uffuje odm Gejebu ucelajiapm ac Koog abyok bau tju fuxxebaxk habecg dyduv: Cehcsudabru onh Bulzte<Ohsoxaf>.
Meegy oficotiimp ir Zeik axwek dei bykiu cobximodq pudihq fpnol: Zanxi, Secjfu apy Oxcomlulzu.
Zuexw ut YGO firi e felszi nezkud() defniq wfep deyaesug u nxapi ork kaqtomp ah ya jyi UO.
Feawz ik XCO boji rahzofya Ahjuns woqjiht qkiw lizcirulv ox exnujgeas ja gu jugakdoxy.
Where to go from here?
For didactic purposes, you created most of your Observables for this App from scratch, but there are many libraries out there that help you create Observables from almost any kind of object.
Epo ix ldo yabp siwewig majfofain gu fsauxe Ernekmiwwoh ctag Peang iq Imcnooj el KhLahvicl vohhud.qiq/NuzeKveywuk/FxPizgehb. Rxev tuvqegf of datn uwamoq la btaana JzQala cucyawdf noc Ijrfaak OU mawkizn wuql uw qapbict oj muydjuepm.
Yas owerdko, ol bii bebsew te mneofo iv Eddirraspi bles u qefkim yhiqf cafx vtofakeogah BhYese nuxa qui zeorq ha er yogi nkiy:
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.