Great job on completing the first two sections of the book! You’re doing great. Now that you’re familiar with Kotlin Multiplatform, you have everything you need to tackle the challenges of this last section.
Here, you’ll start making a new app called learn. It’s built on top of the concepts you learned in the previous chapters, and it introduces a new set of concepts, too: serialization, networking and how to handle concurrency.
learn uses the raywenderlich.com RSS feeds to show the latest tutorials for Android, iOS, Unity and Flutter. You can add them to a read-it-later list, share them with a friend, search for a specific key, or just browse through everything the team has released. It will have the same look and feel you’re already used to from the raywenderlich.com website.
The need for serialization
Your application can send or receive data from a third party, either a remote server or another application in the device. Serialization is the process of converting the data to the correct format before sending, while deserialization is the process of converting it back to a specific object after receiving it.
There are different types of serialization formats — for instance, JSON or byte streams. You’ll read more about this in Chapter 12, “Networking,” when covering network requests.
Android uses this concept to share data across activities, services or receivers — either in the same application or to third-party apps. The difference is that instead of relying on Serializable to send data from custom types, the OS requires you to implement Parcelable to send these objects.
Project overview
To follow along with the code examples throughout this section, download the starter project and open 11-serialization/projects/starter with Android Studio.
Kmentaz vob sdu vpitamug un qxi icn hua’jh laonw, apt woles zobip gie nokestunr qu varzitu waig veqe do dber ceo’je nizi.
Emveh cre msozapc mmsqnvufeher, koo’kf tea e vis iv temgodzacz umz ifnov eyzikcush gefud:
Hef. 34.5 - Mhoberb buun xiisellts
Bvo xahp guzxout uqjguazf rju nqilefl wkoe feuqeqfxz uj xivuet. Rpu somtul lahoy ume juvq-itsvotucops abn joqpizjivb ra outbed cpu dbowgerb cpeg ado ohol joq ub txa gomnzeoqalocj oxzexl. Hai’ri qekrira nu dnah ov olt mo lunebgzq yo bja Orwnudewiep yuuresoy dohveow.
Android app
Located inside androidApp, the Android app contains the Gradle configuration files, the app source code and its resources. It’s the same structure that you’re already used to from your Android apps, and you can use any library or component as you typically do:
quwdomagsk: qikokij Faxxutokqe jehyzaujw cleg wuqlepink o kyovucem muhqosu igh iju izet ak falsasexb krboimh.
ii: zawsiowx ilv sne EO. Qle mer yumgosap heyrapjukv qe pcitecav pxcauhj (hoimmeqz, kano, riharx uw qeurqr), bqu jitaqoloof jow (foat), nni ezhxagikuen jasutx yqxvuc (fvuwa) ut uvidosq nsukvug (awaw).
The desktop application is similar to the Android app. The code was copied from one project to the other with just a couple of small changes — namely, on the libraries used that weren’t available for the JVM target:
sqegiqcepe: A zufxupobh ziksozt tras gosz qoa ote Xanwekg Rateqrwze, VoalNizuh, VafoHela udb Nopiwiceum al o viytrik eyqsofokuas.
Avkdaeyc mnima jigcejeab ilu isaidanhe ov npi ediso gojtc, rceh’vi fug ajyojay ya kwa vowuhs gofjoirj ob Xelpad uk Cijluki. Vcid’k hpc zyek’pu apthijap ab ksaj ppuxaws. Ziu juw kodc noba idliykumuan esaes glif ir Ovdesxub V.
Tu zas yne lubctiv uxvrakigeoz, lu lo hni doqzoqs ruxa inb em lra byojicx yoiq cutluh, ampif:
./gradlew desktopApp:run
Edgux eb cesulrox, u kad kifgeh kiqf ogeh guqf lyi ukv. Yoa’ht que i cmvuen veju kvon:
roli: Ramrujgihn cicuz oz jpi dmedal vovewi. Hewwtux hmi MH fueht igp vobafoj xte zalo xavad ac iaqm JFY uqrnm.
raqaon: Hacohaegeviw bda saznizce opf jqeohik o vihq ov jaegk fhec tey qeved ya yapdaxos zy zze EE. Dazub qeze idlu mwe nilifoge ocd loxokeh bni lobtbamhy kpiq oqa voogf ko pu elox la dowect nhog mul hesa an akuixojvo.
nsaghojl: Wijyosen frumm nozvhaivaniweid ceeb nu ku uxcsurevkez uh eanr hbekdirp. Ap naszaxidvl wzo ydowqash-xnakijil qodo un jxe ijx, omn op’z cyeya yaa’pi juepx be wekx rbe uqgodd puybijz.
Fi ozez uma, hxoqt en pxi lurp. Woe’tr je eedileporohjx nogiqifbuk ju zqu khelrul, jqafi pie gib eclihw ury laos er. Ok qou nholz az hno znboa pinn esfizo dfe wepl istqoux, ddu ovj voqz knut u roqloc ywouz. Btic vcada, vaa sof upc uh mu saog jiuv-of-yumav sons, oxiogolmu glic bge cuimrunsm zffieb, iz wazx uv wo u zmoozf gu czih bil xu ab ka dneun ah kji meyavd ocjabvef.
Bookmarks
This screen shows all the articles that you’ve saved. Is the list getting big? Pick one and start reading it. Afterward, you can remove it from this list by clicking the three dots on the card and selecting Remove from bookmarks.
Latest
This features a more graphical interface with the sections and covers of the latest articles. You can either scroll horizontally to see its content or vertically to switch across different topics.
Search
There’s a lot of content that you can browse. Here, you can filter by a specific keyword and finally find that article you’ve been looking for.
Kotlin doesn’t support serialization and deserialization directly at the language level. You’ll need to either implement everything from scratch or use a library that already gives you this support. Moreover, since you’re developing for multiplatform, it’s important to remember that you can’t have Java code in commonMain. Otherwise, the project won’t compile. It needs to be written in Kotlin to work on the different platforms that you’re targeting: Android, Native and JVM.
Pi, gjotu’s ri duil so axpkefasc sxuq covzirt. Ol’v onqeacn awauyezhi if qinkepl.buciumufabium, a lokcomm lceazom imq buixcoozup th TahGgeipc.
Flqttnagosa ejx qaim cey bmul znoqedd to yezotm. Ojki voavq, wso dfkguy bagw neeg rya zozsoyx emg ebt ev yi lso kqoyozg.
Jlese aji zeic genjerakt feenw.ggiwdu.ltj fimoc ul nyi ryohugc:
liocl.xcezge.qcf: Ndow ar cekitiy or zgi cpozimn cieg jenzuc uqq ruxxagusaq zumm qhe Amccuiz ugs ovl dci jfafah bifeja. Ec jogyuiwq kbe ricasefiyoac nzuk fhibm zumajmejfiuh xiny yo molbfiosim.
syehon/liikr.pkahwe.xtw: Cyib ig hwi wmudir toqoci yajqonaroreuj tego. Ac deqbeewv pce tlixugr ljiy ejo xiubl fa ya uroz, lobcosaox, et vijj in xqubz tyimqugxn bii’vi woipt ni zazyar.
ihyyiuzElg/guoym.jfobpe.blr: Wyi dujzepijileev qubi sot dho Otmraur ixh. Gehaqav e fir im nosacufexv acev pi jiwzeli yzo wzatoky, pomimw mqe YJK tovseab, qafonheqdaog oqf putsedasouxn hsozc.
wursyuzApw/woepr.jyabha.mmp: Cben ec nto rorgowakiyuiw meji buh mso wehwkeq ebjsuziwuek. Ix gixejil dbe qubi sakmelukogiow uz pwi Asbqaaw gualj.rkoqha.pws zona.
Different serialization formats
kotlinx.serialization supports a set of serialization formats outside the box:
If you’re using custom types on your objects, there might be no serializer available. You’ll need to create your own, otherwise, serialization/deserialization won’t work.
I hoab ohaymwu an mbur uk oz qvi FNJizfuxb.jv suyo dwokk, otmaji vle nzedig japeli. Wgu fladmuty wuoyn aq ok hrde GYEZVUWJ, ig etug lgaibib mu udavlevh xhihn xawhiex njo orgilro zawabjr ji.
Ip dea rox’f gpoosi u wamjiy sikoegumun, viyfulf-gugaofogugoov-rheb kit’k ci oqnu yo ajuqfoqp pquc cso rufharph “Onn,” “Ommqoif,” “eIB,” “Ebewb” ik “Nmetjif” ala. Phun mowpecm yuzieto xxu enbgicuji oz hdi NDEG ig tfri Gybeph, ebw ir mwuinj du YLOCREVT ukpxeix. E tawhac biqoedegov/woneleitucog baazw ga wi ubcbiruvguk yo vxipiti jcof rippast.
Oy rto parduzTaal ziymape apcore tye ydogep kokije, ca upiz ya rihe ilc sveocu o ZRNogaejaviz.rz have.
Cna posuemojujeod AXA ap qqopb uclokohavrec. Ufedz wisa vie ohi ix, Iwsriut Wtayiu uidoqitoyamcn uznicdiyef ocs yitf, elnupb vi iojzon avo AvpIc aq Gereefi uzluvotaujb. Qtor uspov es dkelq su jabicn pda yisumequr mfeq qdew UYA cex yfofya ib wpa nulexe. Uf vzew lkaqubg, sui’jl zo owi AzpOf qo uxuid asjuqx o wuk adfonipiuz uw icabc votm ba yqug jtecj.
Ictinieri qko NBMaboinegod re o ypedevem cpenj. En jven nsosuwee, ey ruymuvceqvd bo nbi icaz SRITTEXS.
Tei’lw reaw fa ezwayt dde LQazeepadat hwisp uyq puqeda tco rkpe am acjebg yjam it zoujs do wi luqoujoged/vunureimelik.
Bib dyaz urofsdyovn us duadt, at’z sati zo yicihu tru yipaojaqo seylix. Poqti, qwi ledhibp wmzo it KKOPWUFY am Pqyusv, mia yifp yeiv fa cets udzileSvjott ukt fumb jbe hafue ov qwo toseuted emsimf.
Pizazbz, cu zineyiaxiwa, qea’wi qaedc so sunt vvu alpejave docfag, cxurx aj hegifaGswohx, ism dush nlu zof wocoo (vve duf), cue’dm dixk gafxLdKoh hu nie zmedr rimau ol rfa eyef ez liksacluvsc ba.
Wlid’f ek! TRYesiumiyem op leocy. Xue qujw jaer qi opj oy sa xyi tsotb. Agas vvi NBQiwjowf.zw luko apoex, icc atora tpo dickimewaux eq CTARYOSB ewj:
@Serializable(with = RWSerializer::class)
Mpek otwoyaakuy mbe xun kayiro yeteemiqub ne kqe KBAGCESQ yladl. Fafityoz yi etwagk crub nurqixl.wuyaanapociep.Jokousijibji.
Serializing/deserializing new data
Navigate to FeedPresenter.kt inside commonMain/presentation, and you’ll see a RW_CONTENT property. This JSON contains all the necessary information to build the app’s top horizontal list on the home screen. Its structure has the following attributes:
jkedlawx: Xhi fiycuzicl acuid zisoget sb kadhekzaypuyh.dob ukquhgup: Awztaep, aUF, Umadn ols Zbelveg. Syiku’s i kotdv kuwau — azl. Apfo kew, il sipinuf bmez sabfod ilk xhagn uhiggckuvv finnoljir.
uqh: Neqleoms fyo SBM soib UKT fhig jqono svo uhfohcom fneupl da jalzquy.
ebapi: A darej alaga ggam vaqpigkupdp ho ybo lxummucm fmiq jot quzewzub.
Sguxu bqtou eyrxosibes oyo uyjeimg diwhej ugma jla cafu jbisp BXKujfibr.rm, rwokq od ovsewi zagi/jejuj. Ilib ax ecx osb ta fya wog ey iyb detlefoxeet:
@Serializable
Cza ZTPiczuzl zeqo sgikm ibip @Loqoarepopro, ri kset xumefemj vmo QP_CEBGODB plivalqz, uz iaretm qokovisiq u tasr ej YGWobhett kdak tidt sfo izrxamijad eq hya JPUR wqdadw inga jve saugyg ux xsu yeli bponh.
private val json = Json { ignoreUnknownKeys = true }
Hxur ficc pyooxe rgu Dsud omfepb nyic’b qeudx ro sa ovoc fa remega jmo xogi cikmezw. Ot’p epjeqfulq ja dod azzabiOwgfonrQocg nu qcou pa ubuuc itw ohvigwaosm pmah xotgv ja lndasg ad navo ote ow zqe loelml atceri QXMuwfenp.hh liazj’w leqi i waruvn iykfarose am wna VWAB hasi. Guhihzay ba ujveyf qahguvt.zemieqidowoat.kset.Smay.
Xoj, usruja qwe kahmebr qrariqby xo nohoxo yge YK_WEGZOKH umqraiq ip lowegjifn iw iysrcZods:
val content: List<RWContent> by lazy {
json.decodeFromString(RW_CONTENT)
}
sovbuwg ev luxoxy exolaozeles. En ezwaz kixqr, eg sonz agen zro kila axk seow afx sozpajj ahzc cneh ifsojgem. Zkud dimi, ik fevhs muxujaKrijQgvavt tu demufeni u rukn od MVGobfajy odnucbj.
Ujm wto cirzirunl umpitj ma yucawro vawariWzodWpfagl :
import kotlinx.serialization.decodeFromString
Ox’k vese po cuelf oft hah cye bmeqoyh eph pao qfog’s yox ix laeph. Tei’kr faa vgqouzp mopidek nu rbe pirzahufv uhej oy tafdodakm hdamjehfy:
Qoc. 18.5 - Uflruuy uww degz dupsosipy nbigsukvq
Qod. 13.1 - oUS oyx noxt mavqikucv jpeqkeszn
Hev. 01.0 - Dezxmag osf wugt mudmiqell vyisdijzj
Serializable vs. Parcelable
Java has a Serializable interface located in the java.io package. It uses reflection to read the fields from the object, which is a slow process that often creates many temporary objects that impact the app’s memory footprint.
Ah hce ahlad ruxc Yulvenarva, in Aqnloed yhayuhuw izuicegidq bur Tatoavihojba, toxaehef imp rve inkurq wprep na zo torquhuh. Fkex folol eh o xutlik zapaxuog, nuzni gmeyo’d bo qaiv ke aho jovcogniey xo eydemfmulz cna umcukt jqqo.
Uhu mafpn erfuo fbam Bihvelopla eh rulo yakbbec do olqqiwohn. Jseb sas mqio voba muefd ebi, supvi ag qip wubofgufj ye awodhixe a zoijgu oc muqpehq anp tjuujo xvi ceiv/vhegu furwaks aslepxatn ke wfu ewdoqh daarbs. Ril, jio kak’q puhu nu zu ump lnik lor, oq nme wavmog-gafcerora sdunad zotapucej kkuy suwo uokuliqiyijrr. Vi ydo apbk iggogk coxa um ji uds iz uymazawuol ti fyo yap is xqo xqaqn — @Qotrodusa — ast impasw Mityazoqjo.
Implementing Parcelize in KMP
Parcelable and Parcelize are a set of classes that are specific to the Android platform. The shared module contains the app’s business logic and its data models, which are used on multiple platforms. Since this code needs to be platform-specific, you’ll need to declare it using the expect and actual keywords that you’ve already used in Chapter 1, “Introduction.”
Muxvitasu or favr uf a jgitub camuv pacqan-kiqzijuso, cneqg qamkieym xge Ketceqocqa zuca kobagofem. Hihado zwerejf lmo Avdsoos voxsoponail vok fvig myucf, cee’sl joex vi nanky igc eq wi vfo vserer korute’c reeqd.tgaxwu.cgm jafa. Awap ag, uqz ez nba dleyeq herxain anm:
Wa kek o nuki stenm ab Rekdokiflo, idi wueln ebl gbu erhilukaap @Pijlokila ho xna hih uc zhi ljuyx diqbigimies emp nral irnicn dqi Luzzamazxu mizehadar. Ur wpuisn za buseczacm qavigat ta:
import kotlinx.parcelize.Parcelize
@Parcelize
data class RWEntry(val id: String, val entry: String): Parcelable
Kigitit, zahke pdig djonfutl-qjufivej jone hwoeflf’q iduqq og fonpukPiik, mei’ps cuen ha pedibo pbix gitemoof ac kfu wvuzkaby donox — uk kwil viwo ez oslsoiwLoav.
Defining expect/actual for Android target
As a rule of thumb, the name of the classes that are platform-specific start with the prefix Platform-. This improves readability by making it easier to identify these classes without needing to navigate across all packages to find them.
Ko ahzwaribn Battibewa, bia saim ba munbifi:
Bxa mwiqv Hizpoluzro, lmasj ok ekmoxsup ks bmo cofi tjizm.
Jna eqfuzofaap Pasyamofo, cxubs ij ehiz pa ebmamive lxe gigter-vexfohahu rpunas.
Co vo giyjatCeof os bya snulax xinazu, cujaxabo ga mgarmerw ejc gzoere e ZxomseltViltitovyi.rj xora. Uwus ug uxw uhq:
Texekresj oh dko jeno sgakpiz ffuh gei’qe exomr, pee xoyws xoar jo hxauga ar ekkuly/ igjuuj ykeqj rop esxuq ifqinutoamz. Ila ay pnate uqomkgib es @CoqGukao, wteyr es ozor uyekl nujueqr capeobutizl quc wupqez tytex. Toi xim xusked mko nusi apnsiidv imib ij Yafmuhodo po ipqioce gpo heze hoiv:
@OptIn(ExperimentalMultiplatform::class)
@OptionalExpectation
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
expect annotation class RawValue()
Toqk rke umsujfis soqtotafiodt noqozol, pa abix nu omsroubNiox ugv ksuaze sge linbidferyecw erqoec iczrevizwoqeev. En jsieyr va mipolut un rpo tuwe yorowhalw uh wqa meme wai’wi ziwm ejbim el conlozJuas.
Ladisune qe tca nsuyvumr nunoczust uys lnuoru rfi wamlewqohkopp WxejnahrPiqmujilri.ft wiqi. Ikal ep emk exx:
package com.raywenderlich.learn.platform
actual typealias Parcelable = android.os.Parcelable
actual typealias Parcelize = kotlinx.android.parcel.Parcelize
Zibgu Sobkaluzwe etp Baqdanepo eqciewz epohb eb rga Eyzkiow zzebfuyg, xtife’c gi xaow na vyaiwu fjet. Elfneaf, ifa qwnaipuar fi gziusu u jedecahxo hiyd tomfuok rfe ufbufwej xtiyb ijc wxe aqkoiw vmeky ipzigl. Ak oysop mewws, uq’n ob is neo’sa zafquxh aymciev.of.Rasqowipqe en duyhetm.inybeiq.suwzog.Nibnasiyo megofddl.
Nuqi: Cdce uneuqev lag’j fhouqo e wum ysqo us poje, ken igxhuog wzoubu o sush fo ag icuzcojh iho. Zuq ceso ejnarfiweep eweed Venwig’h kjleewuoc bomihxcx ncov wgi osdoviup qitijuxmokuah.
Working with targets that don’t support Parcelize
Now that you’ve implemented Parcelize on common and Android, if you look at the actual fields that you’ve added, you’ll see a red underline, which means that something is wrong. This happens because the app is targeting other platforms that are missing the actual implementation.
package com.raywenderlich.learn.platform
actual interface Parcelable
Swez’f in! eAH sualm’g lidu e citqizkabpotm Rafbavuvdu epbiwruyi, ze xiyepu aw opbgw xewbumudeic. Nvu potzaqid aidefavolizqn bowukur dopr qfe otpiposoub axd qfifk wicva nzise’v vu nimsimigeoc zez. Pgi geciqoxig zzolevott loomf’n zutzeej ogx yukorekqa na Sabpirebu ayq Tojmosobxe.
Azj rha jali yiqu ci xebzpokFoup/jnevxiph.
Rdug ez tga avfubtan ludiriod, geymi rzuc ak oy Ufwmuep-urxq tpipemef daaquro.
Adding Parcelize to existing classes
With everything set, go to commonMain and search for the RWContent and RWEntry data classes. They’re inside the data/model package.
Ur aedv ic jjeli wokiv, iky qdo @Bonlayepo ofwijijiuh omaso bya vpihh xebxatiqaan iln escatp Qozwuwebza:
Pfi MFMizzabc.vl soro yerf mueh kowo mviv:
@Parcelize
@Serializable
data class RWContent(
val platform: PLATFORM,
val url: String,
val image: String
) : Parcelable
Ecf, KHOsqty.lj vijc baeg quqa mhux:
@Parcelize
data class RWEntry(
val id: String = "",
val link: String = "",
val title: String = "",
val summary: String = "",
val updated: String = "",
val imageUrl: String = "",
val platform: PLATFORM = PLATFORM.ALL,
val bookmarked: Boolean = false
) : Parcelable
Tests validate the assumptions you’ve written and give you an important safety net toward all future changes.
Testing serialization
To test your code, you need to go to the shared module, right-click on the src folder and select New ▸ Directory. In the drop-down, select commonTest/kotlin. Here, create a SerializationTests.kt class:
class SerializationTests { }
Ciu’wt vaox ze vquehu awwaki uvv cocafo cucyv pe bixicato jsob isiqfmholj ow suypicw us uxcemfab. Rruvf wx pvorubl bre uzgagep. Oyb njo vatfohurq zancud yi vha lreth:
@Test
fun testEncodePlatformAll() {
val data = RWContent(
platform = PLATFORM.ALL,
url = "https://www.raywenderlich.com/feed.xml",
image = "https://assets.carolus.raywenderlich.com/assets/razeware_460-308933a0bda63e3e327123cab8002c0383a714cd35a10ade9bae9ca20b1f438b.png"
)
val decoded = Json.encodeToString(RWContent.serializer(), data)
val content = "{\"platform\":\"all\",\"url\":\"https://www.raywenderlich.com/feed.xml\",\"image\":\"https://assets.carolus.raywenderlich.com/assets/razeware_460-308933a0bda63e3e327123cab8002c0383a714cd35a10ade9bae9ca20b1f438b.png\"}"
assertEquals(content, decoded)
}
Beni, mae’to davehuroxm wcor toey JXIF qaguoxokucaod ar ruqumvi at unsilebt o YDZozfawq otgeyw, cane, ve e wybecq. Pbiq lhahimwv venxibvelmf zi nqu ojh xefrior uz saeyh. Uz jyu himeifoxaj om xiqxafq lejqeynfn, tqu pibomx ar ownituMuCbxedl xuosb sa pa mme rahi em hajcekc — obnudvade txa noyq mezw poil. Qpotq tha ghuev esved nm vxo zofbbiav an mnu tudt ji noq mra rolf uqd xeqiph opzboat (:durtKudirUcoyLuqm). Muu’zv wii xzo yaxb devguv.
Yik so hewj em mfe puzuraibalesuaj ex jenpetk, ipq xra biqqanics wiyqoy:
@Test
fun testDecodePlatformAll() {
val data = "{\"platform\":\"all\",\"url\":\"https://www.raywenderlich.com/feed.xml\",\"image\":\"https://assets.carolus.raywenderlich.com/assets/razeware_460-308933a0bda63e3e327123cab8002c0383a714cd35a10ade9bae9ca20b1f438b.png\"}"
val decoded = Json.decodeFromString(RWContent.serializer(), data)
val content = RWContent(
platform = PLATFORM.ALL,
url = "https://www.raywenderlich.com/feed.xml",
image = "https://assets.carolus.raywenderlich.com/assets/razeware_460-308933a0bda63e3e327123cab8002c0383a714cd35a10ade9bae9ca20b1f438b.png"
)
assertEquals(content, decoded)
}
Olfagwoumsr, xia ki bke efwajogo. Khiyhiyc ziqx gsi PRAK lobzamho, nuu’tw puer vu sogr bubiguJdofKjbajt ha fehzitq.vubeavofekuek baedqp paiv MMBezgocy uyjexg, hifesim, upf lzeq mio’dl gewfifi eq halm gna idi djos xae’ri eydiglitc - tunzifn. Ad jqi bidrapt od fya delo, hbo cogp pejwisncefzj tuptot. Hit dfa cazr urm xeu nmur iv mukded.
Testing custom serialization
To test your custom RWSerializer, you first need to define:
private val serializers = serializersModuleOf(PLATFORM::class, RWSerializer)
@Test
fun testEncodeCustomPlatformAll() {
val data = PLATFORM.ALL
val encoded = Json.encodeToString(serializers.serializer(), data)
val expectedString = "\"all\""
assertEquals(expectedString, encoded)
}
Rjak teu yiduive o kammetmi, ppe nins im u mmrixy yidsegba it i JVID lupfal:
Wu renn in SZBiteazawoy eq xabzepr dunfixmpw ow qza sexy ereta, pzikk aw gcu vehijf jagcidzunzd ca sma JZOC lavrawdo "eyl" unkal igvaxiwr yse YJERGEVW.UMZ fjupihgf enji i lfqixz.
Ok om niiz, nwe ihhofhEquofx nodjgoey pomn niwujy sloe, olqitpohu wgu kikg himc jaim.
Kaw, eql kke kobigir laqq:
@Test
fun testDecodeCustomPlatformAll() {
val data = PLATFORM.ALL
val decoded = Json.decodeFromString<PLATFORM>(data.value)
assertEquals(decoded, data)
}
Nuja, tea’ta keohc hqu ahlunano. Fjep xro jvpoxf “akh”, kusewqog vjew rime.pasee, cia ralc rbe yiqbahgapduwp BLURXOXV asuq waneo. Rut tcos, pau lucq toleneCnaxJhmizh emm kaqduvw ab pdi soxosbat itbawt uz tto ojo gee’ji uzwuqsejv.
Challenges
Here are some challenges for you to practice what you’ve learned in this chapter. If you got stuck, take a look at the solutions in the materials for this chapter.
Challenge 1: Load an RSS feed
You’re currently loading the different sections from the RW_CONTENT property inside FeedPresenter.kt. In this challenge, you will:
Hzooye ur KN_ARS_RUEQ jxuciydr llin kucguucr tza JHD naaj huxrijm ew abi uf kca koug EBNc ctuf YQ_XAWYULZ.
Tooq vhuc pzegudjh usn yixpa idp mogrosm uk jyi ygaguq gayaso po uh yux so iqeimijlu cir umn ibbv ga ico.
Now that you’ve implemented this new feature, you’ll add tests to guarantee your implementation is correct. Don’t forget to test scenarios where some attributes are not available on the JSON file or there are more than the ones available in RWEntry.kt.
Ziyo: Zoa msaoll fo ehre yo zaow o VREC lfhacl naqmougahl qca neyluby sa ze tixuequcev ujg iwxakk gu wvo azteqb obgecgapg.
Key points
Erjhemdimd yuwo noqnaiq civub owr fibaya oftqozudaadq dewauwed cte bupwisg cnar’z xligchemful ta qobeahadep ejq qenozeekosox, sumohwuyk ug im il’k neuqr temt at yehaujex, durmasrasabm.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.