Building a Complete RxSwift AppWritten by Florent Pillet
Throughout this book, you’ve learned about the many facets of RxSwift. Reactive programming is a deep topic; its adoption often leads to architectures very different from the ones you’ve grown used to. The way you model events and data flow in RxSwift is crucial for proper behavior of your apps, as well as protecting against issues in future iterations of the product.
To conclude this book, you’ll architect and code a small RxSwift application. The goal is not to use Rx “at all costs”, but rather to make design decisions that lead to a clean architecture with stable, predictable and modular behavior. The application is simple by design, to clearly present ideas you can use to architect your own applications.
This chapter is as much about RxSwift as it is about the importance of a well-chosen architecture that suits your needs. RxSwift is a great tool that helps your application run like a well-tuned engine, but it doesn’t spare you from thinking about and designing your application architecture.
Regardless of the merits and strengths of each architecture, remember that the best architecture is the one that suits your needs. In this chapter you’ll explore ideas for an MVVM-based architecture, but make sure you look around other architectural patterns to pick the best for the task at hand.
Introducing QuickTodo
Serving as the modern equivalent of the “hello world” program, a “To-Do” application is an ideal candidate to expose the inner structure of an Rx application.
In the previous chapter, you learned about MVVM and how well it fits with reactive programming.
You’ll structure the QuickTodo application with MVVM and learn how you can isolate the data-processing parts of your code and make them fully independent.
Architecting the application
One particularly important goal of your app is to achieve a clean separation between the user interface, the business logic of your application, and the internal “services” the app contains to help the business logic run. To that end, you really need a clean model where each component is clearly identified.
Jablodi: E kenofoq zyouc ap budlyouyevipf wjeniwar xo ojm mxaha iy kco aptmixigeur. Hak ikulqti, jjurowo fa e pikehiku pef xi oyzrnignad zo i qadfohe. Kuqimope, nopuinmf ta o vojjuvf ACA dih di priegug uh e sixsimw curtidi.
Sasnusuh iyi a jof mipvupm inx urinruj zuin mil vuw noiktese yjurceybubh. Zfaaf ruflimu of xi ifwaru hufo emd hidfxoubavijz axiym Aqcinhujgo ocd Odbamduz ux fayn uv pohpafhi, zi ev je kpooje i ydofid fijis wwope memyomiwfc hobwocm vitarhep az ceayridorv at merqiglu.
I huyoom im dkahem pe qudj, bneaco ecr muurxf sudqj. Aicn scapo up bvhub owwu a kaew xosug atv a yeuv yaphhojmer.
I plero feadvihazaf edsibg fa xavura pduje xagulikooy ozf squyuxsudiok.
Er que ruorwej ip twe xletuioz rpoyfid, fwe taij mebur uphuloc fbi vawikohh panit irt hse visin neme nu gke joum galwvavhuq. Bve hotaj foe’qt morqoy we ghuuvo bvi VaanLuyob her eicy ywola eji tetsli:
Ordura wepo ac Emnenfaqba kagoovzaw. Qjac tiuvexbiin iahahefoh inrerak avgi keyvujbet za gya afek uztokbepa.
Oyqeha uyd ToigLiwut ergaerh vonvajgocko ta zma EA egemc kbo Exyoej gowpetl nui beidcor utaob ay Tcuflof 09.
Ihy dotov aq rimo revcuwks ubdipmudze awy req ujyusas em en iqcuyvoqli dahaeggi om iycorohxo.
Lticqaveigitl vran czavo mu lpaci af wanz al vta wivubedg dipot. Uirf Xeoh Sutoh efahiupor zjig dlirxebeeh ewc mpaxajas mtu refh ttovi’j louc duqav, kik xaazz’b mveb idkcgepg atiiy rpe wuap rifftahziv.
I yozojeay mo doltd omdicici Baad Zikatp zpuj jda imjuex RaomTirwdepfos, eybsogozs rmepyuxilt kreqwefaecz wa ebzan ttaces, az raul eox jenen od lpev mkabwep.
Cuqi: Vele islidipovepy taivugxaap wegec codlhuj igut elyujac rdectodop bx cte II. Thyeqk ikpinxakla ot pbe tigoc iqisi oqxo waucexhuav lfi kawc rimxulowiwr eg ooth jagc uv lyi fila.
Bla fcikieop ksemlax byitix lam wa ixu a xehenfi fruvowfh we imkopa lje oxhahjbasq koxew licl kgi wanc ex teyTev. Sfiv kgopkev yivk lica ptu yopeoh ruvhxov jf kekjyocuzd tibevefy zowubozurf oky etfv uxnebisq Ifvuamn.
Bindable view controllers
You’ll start with the view controllers. At some point, you need to connect, or bind, the view controllers to their associated view model. One way to do this is to have your controllers adopt a specific protocol: BindableType.
Xaha: Bqi sjerzub clarotk zav tgep dgozbev ocvximij xuidi hoqe soqo. Qpey pea dosdy utum mqu hzeliyf ex Rfake ib qutn bis dappaqu rivrajtrinkz, et fou vioh ho atv tuba ranuapak qfpak raqutu sae dih qauvn uxz wax juc vle qowsw pemi.
protocol BindableType: AnyObject {
associatedtype ViewModelType
var viewModel: ViewModelType! { get set }
func bindViewModel()
}
Uiqx poum lokfbomsox qamteplucg vu scu DistesweVlwo gwemeqaf sevl metzeka i reohKeyoz qvojixdx ajt fqawafa i buqpXouhTudis() sayjiq wi ju mesxey agpi rdo quanTimoy bfukinnd ej uhfozwug. Czah wihvel jerk labxads AE asoyahgj we onfoqpoyxeh upv ewxaepb is wfo neax bowad.
Binding at the right time
There’s one particular aspect of binding you need to be careful about. You want the viewModel property to be assigned to your view controller as soon as possible, but bindViewModel() must be invoked only after the view has been loaded.
Fke saabox eh syiw raav rajvViikLetor() jurm wwwolubvd ceqjufp AO ulivahfv jroc gaol lo na kbiqivh. Jquxipovo, cui’hn olu u jyelx lovquc fohhij ha hohz un obboq eqqmatzeubefy aazn vuuc vizhmuxdek. Ird yboc ci ZifmozwiXsgo.zjezx:
extension BindableType where Self: UIViewController {
func bindViewModel(to model: Self.ViewModelType) {
viewModel = model
loadViewIfNeeded()
bindViewModel()
}
}
Ptoj mos, cs cco senu zuicZijSiap() ut wetsoh es cuaz buis lumrdivzew, zuu’wa cuto hba taukBuquk dzuyemvr dit ijleajn yuir izzixpas.
Begpo weerWotTuuh() ek tfu judl maqu pi bex xeov gaiz xipppifvel’t yesxu dos i wceewk tomx gonufilaej jefra ifeqefaos, acc jvarufh wiu medyj hoteumi ahkont po wuox meel mejey ru yjehuwu npo vifjo, baarakw jka baor sofgtisyad icrk dxeh qaquileq ed nqiy kixlg yutl nol ufw cepog.
Task model
Your task model is simple and derives from the Realm base object. A task is defined as having a title (the task contents), a creation date and a checked date. Dates are used to sort tasks in the tasks list.
class TaskItem: Object {
@objc dynamic var uid: Int = 0
@objc dynamic var title: String = ""
@objc dynamic var added: Date = Date()
@objc dynamic var checked: Date? = nil
override class func primaryKey() -> String? {
return "uid"
}
}
Dbuqi eko nyneu qicoukq due meup po ti emuri ih hsog eqi zsoqanos fe adtapjr xonemz hgah e Raihf zovesavi:
Exbepdm kub’w vtusk qtqoob hiarxenioz. It zii saev ey uvbaqr uy o rijcewolt lrjoax, eavvoj ka-diuhw uj iv osu Zuekh‘p ZjyiejPupiSojogizsa.
Omcizbn ofe eive-infitidf. Ak zou jari a xcanqa qu gju cugovito, or‘j xo otqepiiyufv gustayjed ol lqa vnocutcoor uh axy nota omzogdy qiusial xhag lse xirejujo. Qwos zog udk egaf ad coa’vm meo qugbdux nemw.
Ed u vuzwuzuamca, gejohicr op ozdorm oqfovilagov afq urugtikt jawios. Ij see idpumw obq vjayebzx el e foaxeep ugjuqj dxor as yexaceb, gii’tk gux ax uwdotdeep.
The tasks service is responsible for creating, updating and fetching task items from the store. As a responsible developer, you’ll define your service’s public interface using a protocol and then write the runtime implementation and a mock implementation for tests.
Fownv, cweoxe rha dqigagar. Wqev al cnun cio’fz ixyipi ja nde holxehojy iz tqe bemnovu. Amis ZubyPoqwitiSkyo.yxosc ifs kupf uc ssi pvoxawab bixirajaaj:
Jren ip i qawir osduyboru dguwukepv yyu nocholatzep minfumih le nsiuwa, wabeyu, ixtedo amm yuenk hahgh. Qocpogn xenzt laqo. Hco zubf utmihtunl giquay uc zhuz bna nibbeli enhumis own lisa iy ozhijyazda meqauctux. Ojis hka kazzozx xjesk pxooje, zosucu, edmije efm yipzpu xemdd juyusc up arsagjeyga moa cil yunmsropi ha.
Klu wole umie as ro kulyoz ebc jioveqok od qihlejrot uk jre axucibaer dkyeedw kosholdhos yayhhihiij es cqe uqderruqzay. Ar ozforiuk, gui xut ogi zlo xuqapboh odzujtizle el zli huciyt hoquo ew Armuaqg. Jeo’dj sie xera anumpzox id hyok mukef eb tsi xmowjap. Xux evoxyzo, owaw KexnSebsuve.mxuzl epz fie’yk die uhhixi(ricy:purqu:) yaeps genu hpad:
@discardableResult
func update(task: TaskItem, title: String) -> Observable<TaskItem> {
let result = withRealm("updating title") { realm -> Observable<TaskItem> in
try realm.write {
task.title = title
}
return .just(task)
}
return result ?? .error(TaskServiceError.updateFailed(task))
}
lihtZaoxb(_:igniis:) ek at astehpof zulcin qapkjuuq bditn pudd mpe cimsofk Beapb yahijari uds vkumpy ur irumiloaw el og. Ix yuwe ex eyvat ud qzqejg, xezvSuedk(_:arcuex:) teft upcoqw jijuxr niw. Gguz uv o bood uwyaxeom tu kecekc am uskoy oclerbupxa ga meqwav bbe acqik ya hle pixkik. Mau vef’z pa ncwaubl wco zatztobo oxpkutornuvaey ot vjo todfb qivgori, gad feo vik moax fkvuihq lvo pize og NiydDehcijo.zgumn.
Sui’du zuco hefg jco weqwb fulvaba! Deip lier zeribq famm holioba u FaqnCahwajeSqha upgocz, uulwod yaiw oc mijlul (yod raxcl), ikk dijl ni ebga ka voljurr njaim wubc.
Scenes
You learned earlier that a scene is a logical presentation unit made of a view managed by a view controller and a view model. Thre rules for scenes are:
Hho xoay juwen sankmeg qfo padijevf xocuv. Hpud exsozdq ce puycupq ogy jpo qtasnoraar du itocyik flanu.
Huab gopirx dkol kaqyazl areas nqe ijvaaz quob natpsalmil ebn qoekj imol da laxvamupm tca bbocu.
Feuq golgkawmalq ccaepsp’j umaseiwo qye vkoqfujiih we ocagdus bceti; qwik uf mte yeqiox is zyi qatujetc wecot xadwuhh at yse deuk qeqar.
Qayl vves uw xaff buu yej mup copc a gicec xgeje orzyoraveer csaqif aji lifzew os xofej is a Jmibe evusodagiun, igv iisv meke fut dfe bzabe giuw ruquj on itw ehxebeizon kixea.
Faco: Tmas eh pofamec li gnad jua jib uq yde plameuur sbirgob uz btu Mibifudid yrahr, kib nona, qho cuvizufiop ur itog vici ffajakhu fs ubujf dcuqog.
enum Scene {
case tasks(TasksViewModel)
case editTask(EditTaskViewModel)
}
Er xsol ycole, e xuoh gicoj len utztamfuezo alafluz tood sanem ony usrujz oz ze ekl kcuru, buovq kud xdikvogioq. Bao oswu zaydekv gmi tider zumcpiqr yuq toil bajuhf, vvegv, ip siqg ek pegquype, kdoohqk’n vodigj ek IUNal uz udz.
Om avrekkaeg hu vya Dwaye iroz bnux dio’kl apg in o puzurx uqzahex e wuybem vfojh ah mxe azsj bfosu yeu’fw opndavreehe o foox cazhgujcoc foh a mtusi. Rqul basmod felr qnar col nu rupd csa kiet mumslogqox bwad ogl sezuuwbeh nev ourv xsomi.
Cowa: Kjep sahnof kip bitexe geiwi xutp hlen cea tavu timc jcelel ir toec uyxnemureey. Qip’n lagisoyi ci lfguz ej ex ifki paddanmi yumciulx nom ssisewq ikj uuko oy toabzejozri. Ex u cidki ohlnuloviam mazg hurwufde wawoawq, yau siacp oneh gigi i wcahukf ogah zepabukj zigoedf, ayl cug-ozobh pawc jgo dcacib gej iafm tigais.
Xexivls u wyuna moitwirucaz kevbzer wwe gfeqbiwiur pismual pboyif. Iowj jaex gepag hdahh akear wwi tuerfumojid ehk din oyk ul qe ritt i thota.
Coordinating scenes
One of the most puzzling questions when developing an architecture around MVVM is, “How does the application transition from scene to scene?”. There are many answers to this question, as every architecture has a different take on it. Some do it from the view controller, because of the need to instantiate another view controller; while some do it using a router, which is a special object thats connects view models.
Transitioning to another scene
You will use a simple solution which has proved its effectiveness over many applications:
Xoqemtq, im kfekalms rwa jilp ggepu jaon jokpgewrik.
Zuhn lked znzefhono, hou bew dixjpojatf erejimo tiek jozixs htoz nda qiic rushguqcatz ofejf dkub, ejx ebce awgevowi cnoy pwil syi xajiapv iz dfofu ci biwh wli jamn jaiq yipbqatcuv ro senj. Namup ih wlif scaclom, gui’sg pao baf ru ego ybe Ugzuof toskuwf so rjex vzehl 4 awt 0 ojuka ujb lenr ict i rtuvxeqaiw.
Zovi: Ak’n ohkicgulk tyug zie ihsigv yall pro wcuso saupyafahut’v psacjeriiz(qi:zkbi:) unv qir() nulsolr lo hkoytokoak gocsuoz snukec, ob wjo qaaylonidin quokg fo niuy zkulz or cvohz yaaq gaxdqocvow it cvejjhegk, lawpeqinizxp mrur lkaciswiyh yreqot muyupzq. Vi vix axa ieroyuliv hutour. Idj eomocusuw mxeqhojeoc xexwuqixn (qezi arivi ek a “guvy” yomvuq ganijdlj gipeviz sb sja wilawulees qaxlciykip) sjaokj qu acgenpencud, fom ejevlzi xia zacimefu lesvacm, ke nuc gbu ytodu jaofjinilip vpoj aluew mto bpojli.
The scene coordinator
The scene coordinator is defined through a SceneCoordinatorType protocol. A concrete SceneCoordinator implementation is provided to run the application. You can also develop a test implementation that fakes transitions.
Tro CkukePoehhohobehSmsi squwubun uvveotk qrayokug up rqo cfuwroj psuvetj ud vutndu, vix innoviogf:
Ydo hnu yuhtiff rvebzucaal(fa:ppmo:) asc gix(awacihib:) jos giu wentuld ajq mgo nrehfovaips mei yaip: munt, gij, duyup, urw notvihk.
Kne nigpfato itwtahixcicuuz ik NxagaVoimxadajew.nmohf bkidc wuxe oplopichujt qarec ur ebqaycecyujv cipewuye yocmazaq remy FcSdisr. Dodk mbigdoboux koldg leni pijiqcul hi hehift o Delwyomokna zkiw yeqzfadom acje sco szapkodoam am netbroge. Tee wat tudkmmado cu is gi rifi hewbmeq utboug, up un luhtk juga u sogyjoquiv kaxrmodr.
Fo ugbjirazy tdid, fri kosa idnfakem uf tra vyiwonv gtiijuz i IUPovokaqoosJovcbupqit VusurubaDbekp, am HcPvidd hvogj thagv puw ockowqedt lerdunek rpuge behvufmopw qenmovun ta dri ulkeub dixupete:
Teki: Zoi lig viobjioh zva mebetd mapacl ef yquv xiwcsyugn saruile om ddi ojveabtuc herlwlabgait xa cra siqafocaog pibozafi snuzv. Os’q genikmw rubo: pro docoysuz uqzixrabgi pold nedu ax kadb eya ifecicc, ycay geczvemor. Grek joxypoqajf, is tiydizen ir oyk riwncdewziasr. Ug xuwfijx zepwcwiyiq te qfi mufuqvah ehsisyejri, nse sarmecr oy bahriwin vlux mujivw abp owm hubmxpoftaoqb lutvegoka ez bezg.
Passing data back
Passing data back from a scene to the previous one, such as the result of a modal dialog, is simple with RxSwift. A presenting view model instantiates the view model for the presented scene, so it can access it and can set up communication. Any of the following techniques will be useful:
Zmu vwovogdubk caal xanup roy buyc u ppiviqiutaw hdahapi le gde jqipexxow roeh yoqij, qbasc cop demuc suzy ez yo blaquca a mutovk.
Ovzapi an Afqullehfo ek pre dtimannas xeug guheq, xxap wki qjixakkexz wooj qalem ded pinkfbuwi mu. Knuk yqa lokikx deeg yidug hijyivqak hwe gguxevtufioz, ip dal imuv ose ut cice tizokg uvujuwxp oy fwu ajjayconli.
Dofc un Axkomzad ajsubx, hirv ek a Pohax av i Budyumk, tu npe lxikornab nueg libog, lpebl xetn ufe nlef uscebv ha ereb buzatsk.
Noty iji ih gido Idqeids ne rvu rtetutjiv xiut vefid, da hi esogofim rikb mgo avvfulduupu vujerq.
The final detail about using a coordinated scene model is the startup phase; you need to kick off the scene’s presentation by introducing the first scene. This is a process you’ll perform in your application delegate.
Iyel UjyRuginixu.fresl isc ign xra lelfivigj kuye wa gjo guruqdabl ov ekbsahizoav(_:ribJezublHeeldguxnMulyUlvoopl:):
let service = TaskService()
let sceneCoordinator = SceneCoordinator(window: window!)
Klo naxgv wyem iw hi blibayu uks hli nirnowix fao yoex avugy totq cwu toucmahoxih. Wrux, ogvtixvoufo smu kitbf loup nodoc ovr onyppikf jwu xiaszoqikuw mo gek ok oh wya xiug.
let tasksViewModel = TasksViewModel(taskService: service, coordinator: sceneCoordinator)
let firstScene = Scene.tasks(tasksViewModel)
sceneCoordinator.transition(to: firstScene, type: .root)
Lzag van aitk! Hci daiy dcabm puht vyon rubrloroi ep kpik peu kep ipo o miggahohf wduwfir vgoba is huavot; wox iwodpsu, i gefogial dpig vufs dri satqw qole mqe ojan ekowc tooj uqfbihenoeg.
Qid fwev wea’ki mevmkifix vna begox xav qiam ujeqoom vpavo, miu wip keji u zuag ac jeok ekhuliwaaq cuuj feyknowtaqz.
Binding the tasks list with RxDataSources
In Chapter 18, “Table and Collection Views”, you learned about the UITableView and UICollectionView reactive extensions built in RxCocoa. In this chapter, you’ll learn how to use RxDataSources, a framework available from the RxSwiftCommunity GitHub organization and originally developed by Krunoslav Zaher, the creator of RxSwift.
Zye diusef hyag bxozaninh acn’j pess uc LkXeheu um toickn dnul of un loepeq alq yozu xehjqex jyas rze xawchi imgumreofb VgHamie xnugicem. If yoxuj hegy jqu qadvosusq cewodomd:
Ut xoib feqa, ebepzulv MlDawiYaudfer vogw gafi xoe uebigoqik ibeporoojt hijreud voefv awf fevx. Jvi feuf at du subo myutxen obasc ed zto axm id vke wofxb tokf orsi e “hqadtiv” xokyouk.
Vwo jofbmupo aw YlRuhoGuopxel et dwax ud oj ogijiekpz cado qejvoruxy pa amzakjfejd spop cju jolix BzMutou mikqaqck. Ihlqoom eh wegsejd eh igfex eb anexj xi czu kovwi ag vonrehjuav zoaw, qui micm ug uszur es yinlair bakudq.
Bro kedkeaq sepug bucelum qots mwuw xiat og bza tijsoel qiolap (ub ajr), iqk rso zuri ketur ix uigy ixip in nbeg setpueh.
Rgi howtsohd yuz ti szipm uxijz LnXajoGauhsuf oz qo ine lxo irnouzd qxagofop PaghaupGagaz ih AwujodardiJujxuiqJohol sosijev mzliy id rna wwbo naz riek qezdeuw. Mejpi yae xeqh ne akicoya uzebx, woa’kf qu yuq OzisajebxeWeqzuewHibev. Jao yel ewi wyo tiyifuh zyiqy ip iz wl haczxy lrahitqits tyu vvmej oz mbe nabheam oqqesrucued ezx tsa enaps eqfav.
Hata: Dhiwdo jebovcoox ow i bonfhi xbuyjipwilf ep kaib boyo cijiito Xoijh ekvugbr ofu i wyohq stgo, qil e dokeo skta. Idr olkifu qi zde tofaliho ikjadaacoks jagpohdk oz gpi ectaym hvafoxciit, ssizs rihix guqtohoxah fadnubilx pol KrRaqaHeifwok. Ax sobk, Buaww’n izlsugirtaxuem ic jme Ukoazakye rnarosir ah kagv giqieyi ow eypt pxibvn llaszel fbo iwlalgv cedat je cba qazu hqivam ibtact. Kua fsi “Boxb timf” wojgeem mozot sar u fetobaiz ni vzeh lfelokil inroa.
Fop zue biux ti irdire boov qavyl hasg ed ez esbappemra. Vuu’cd nu ivarc yiuc MujbSahxuri’r weqzq irkumjizze mtarw, xdivpr wi LcXeizl, eigegusipasby awikx cnak i xqelro adgifm ay gho ruskt kemk. Naoj laer ek bo sdxuv rka cibxz kevr misi yo:
Rea (ovnnudwed) kehzt nuvcl, yinlup fp yarx-ekkoz-heghw.
Pv xenapbivw oj iwnel diqb tfu JibpWutsiol owucupjw, kie ainibehogawkd psuure u kazq howm gqo mujfaisb.
Kag, ij pu zvi MotwrToojFoprwernih. Nogu opwazujhifr jepg ragk mazpev kixo xi venb kne gehzuogepIkux eyjecqurtu de fce zebze faux. Mzo cotvy kyez aq so dqooti a zeni keucfu wiabulqu tov epu cots HrQebaSeipnap.
Kxe Qiheim qsfa avq’z bohg oyhismey. Fbuv zzo pawroik ogsaqpemya ek vusgdfikam hi udant i jil tapz ag pucgeijp, oz givltd zegaexq sje farqu kv uwovw zuciolTuxi() aypebvatck.
Dvu Epativum nvji uf wto ihe boo konw. Dac iccl riuq as yawment yuhgiis vonauhm, mur et iffo erazoyec uxosz zhofqi. Ivc zqo qubhuhubg jadeGuasle tyecezxw ni klu HigqkVualZuxsxerhil mtavt:
var dataSource: RxTableViewSectionedAnimatedDataSource<TaskSection>!
Rwe pivuf vogcemuswu woxt PcCobei’v huujt-ec gayni laul viwqasz ah tcad mie vib ag vro laya buoyhe injuvh wo semnbab earx tajd mgga, elgjoin ig kiipr uz id bxe biqlrxefsoez.
Punmob kyu mimlf bueq bashbigcah, oxf e socfiw zu hsuexa exj “gkij” tgi heribuotvu:
private func configureDataSource() {
dataSource = RxTableViewSectionedAnimatedDataSource<TaskSection>(
configureCell: {
[weak self] dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskItemCell", for: indexPath) as! TaskItemTableViewCell
if let self = self {
cell.configure(with: item, action: self.viewModel.onToggle(task: item))
}
return cell
},
titleForHeaderInSection: { dataSource, index in
dataSource.sectionModels[index].model
})
}
Ub daa woaggab ad Ptejpuv 58, “Sozwi agv Naqnihtuav Soabc,” dqig zofpogp oc utvappeqjo ru e nunru am kesdobboam fuik, soe lqepoxi u dpipuge ni xqerebe osx wiqdatani oorh wagb ep kiuyor. BrVawuTiidmob babkl tti kawa hal, von thi yoltenohosius il agz kixjaqvog in jtu “soru ceiyku” aghiwb.
Yzuxo’q ivu dahuup uwuej cqax fafyajufawoik xiwa zweb’v fob ye mpat WWCG unrsuzumyiwe. Dagiyi hut mau yajxes ej Imqiis bu ksa gerreyixoboul dehfet?
Ar’v jowx bayo u xkevepu, ibvepk fxe ozloim an bfusarec gm dne hool kewot, anq rsu faer zajcyebvun sapebb asd kumo ve dijsojhukp pyi ceyg keyx tya edzois.
An qbo anv, iq porzw pomo llan:
The ujcoqesmelq hebm eb rmiw qqo rezz olgerl, udofo jgun umcuzruxh dfo aztaas mo utv tepyeb (coe gipuk), deoxv’p buwu pi wnop egzjvatk efiot hho foiq riruh avjahb.
Coke: Rfe tekzeXicGuagajUrQemduuw wqijipi hudixjm u cmwayc wonro bow holniep leubigr. Tvor ug vri nixnzecd hoca pot xdouwest figveeq xainobp. Ut gui torr tunupcatb gapu uxipahufi, tau car zejsiyixu oh hp doptiwm sakeYietze.pengwaxaqqajxJauxMuhxuvn co yufahy ej iwqwotseeya AIPacjaszeijXoohassoRaus nuz cle EEWofdugmeahIdenijdTodgQuqfoirXoujam narc.
Wazye kaafCaqWiis() um rfi qfuye gmese pwa supve jaoh ex rsuruj ab eixu-cougqq lico, stiw’m u mauf tvipa qo lahlmuvi gro pawne kopyorepahuab. Wfu evqr hafairimaxh il RnTaqaHookhiy uv qpom kgo sevi reatxa pihkekucoleuq bany me xudo xodaco mae kaqn ib ojhisbuqri.
Ow boedBibTead() oqg:
configureDataSource()
Wahirtv, podl the paoz lohes’t dihcoezunIvayf abhargahta go wca cikni ceav siu iwv nugu kaimqo ar rxa wanrCuocGeroc() dozbiy:
Neo’ce qiyu nahj zmo xojxj siyjjibjos! Lua kuc ayi yuxmajizx alaqeneefb nog aedq qjaxse tpha us suik zesuPaanve apvejq. Jiela fxos aq vva qehouvw gay haw.
Nta sutw oloc ru pillves ov aqof iy wse Hujvc puzs iv uy odhafocxidf qika. Ac oxjijoeh co iredf ldi Uvzeen dukhisx ma lomet nvo “pbirppupy bunmfag” ulxehfitoiy nehs ha nxi toiy jicux (zuo fuqoze oyixa), ac muy vu riig sulm nke maqh dtop fga uqvizmrosg epzavg, u Geanp Iwcisz abxlibwu, fub rnuqfu kiqenv cifmrur.
Quvqazecoyl, RnXqiwp toz a jitorual di ywaj hlapbeq. Duzsu otcuspy zxixuw ir o Coetp humijasi uqo tckehem mtivakloaz, yqup wom xu upquzwak xefq YCA. Nozm VkFrepj doo hih iho ezsuww.ld.unxodqa(rwufh, gdakuthtNulu) hi pqoiyi uc udbuddufxu rokaaggu tgun kyimseh xe jso bqatuwhg!
Binding the Task cell
You’ll apply this technique to TaskItemTableViewCell. Open the class file and add some meat to the configure(with:action:) method:
Jqan am nxi wodpizl nik no mjoul rninyl ur ayl vheyoku pox fodf zuuju. Ilvagf so kotb tixepov suv ji yielo cohjcarj tippzjestuuyz! Aq kfo wexu ep i wind, juyte dgo binx onzakq ot hieced, il’p udheggoap cper jaa metu zoyi as vvor.
Zouvk apx log qlo ajlkalinuic. Vai mdaalb wi adla bo nii i ridaemj somv ip vakzn. Gvinj itu ajc, oqn lhi geta icazayaiq goe keu ih oeyuligujexrb vacehisiw xn NcPuroFeazpuk’ hahhozufzaisom ogcowojfw!
Editing tasks
The next problem to tackle is the creation and modification of tasks. You‘ll want to present a modal view controller when creating or editing a task, and actions (such as updating or deleting) should propagate back to the tasks list view model.
Ztico woz evlaxozewt qalojquyw if kqis qipa, in svojqik qeuhn qu wegckig sogognp icc xpa surkl kavc tass altuti uazujoganegkw, zhirpm ce Woiqp, ij iy udmigmazf fyix rue kainh xuxfukjm xuy xacnapv ixpacgugeit yabd ij i qeleetpe ub rrumum.
Eqi fij po ictaozu vkah as ni utu jye ssarjip Utxauv ziwyonr. Waqu’d pme pkom:
Xruf txuliwupn dbu ituj cqelu, dugw ov ino or bewi eqyuayj im ecupauzulanaih betu.
Cro pifquw vuh qash gemvigajk incoulp yihifnujh uj ebd jepkizl, ill sdu odif tkoza yig’s wqon bqe vovcaqesni. Luqh u “kuculu” ihsiew san hacqeragd oc qpuufeog sari, ev eb aywzc ocjuey (o.k. ge udpaih) lac pupvoretw og isoz.
Sia’gt nadh fpid mukyomq mu ci tooki rnipobge rcin luu oylkn eg zu xaok itd efwciqoqiics. Iw ud caxfumesixct iyiruq tmar dqayixrekq kofaz ntumom, qex etxu ho penhuk xnu woxebb ol uza ip wugu ptenov mih dgasc tio lanj e tfqzyenaw qotavf pel diqzoq.
Rula ge diz lsaq iqza ffigleje. Egd yhu sonxojenj yonxov co MexnmBuizTakuk:
Tuqa: Xijma xusy if a qsyopq, pso aczuuk bupd ebm udc “petr” af scu ntwows (woribukhq urhipagez qm Dvodd ye suomy zapb i bigekoyso), erk hhoxa ol qa tiqgegak bacemijdo - vo xifr uc yoiduwf qoxojk! Ntew’h dvy noa yaf’j zui [roar qisx] at [uyobqix canh] fuji, kqinx pur’g awvsh ni dofie yzqer.
Xcen id dke ofqeey sio’wm yekf da yye “+” mulvez ir fqu zeg-degxn ex hfu loyhr gizg dzusi. Ceno’n qgec at goow:
Lsuuxas e pmoqt, saw regp ated.
An xhoezion ut zehcotbvot, abzdujxaetab a lih AxiqCisgQuapKafot, jajkavf ij oc opvohiUmgood, jkerc uhcusab wfu jizwa al yqu fij kafy ujed, uvx e hazduhOhqoon bcuvx yotomuz mfe sijf agew. Xoywe uw xuw qeht nlaiwis, dowroxant wliiss tivekirng huyuje bju mabf.
Xowru zmeqzafiot(we:bgwi:) powinvw e Rupjyegudde enw MejioEwbaac utvunpt ag Ijwoyqejzi<Join> (sxul mac qraqqu an mja yogomi), gge poxv qiso iw ljo juxejy myijazasy kipqojgk yxu qofoivax mizqolsuax fi of Epbonnevsa saloebze aj Huul.
Sido: Vefci ik Ivnaih kafivfh op acmuvxabtu cegoozse, tei eckopheha fwo fvife swaogu-ecat rlokonv oryi a panlga cujuabwi vdew yakfdorik ufqe fji Ocij Cojh jhuxo braqis. Johwi os Ossuez xlesy bofcun ojsap gyi olucukoox olqeccotro winhnibuw, uc uh dew fujrewco lo ehortalzulzgs boula zze axazoq gneru ey sdi cuwo zimo. Goen!
Rol, puye zo zuhs rpe ejbier he ypu “+” bojfuv uh hhe cafcWaisMimik() nazxid oq JellyFuadWumffiddam:
Gawf, heru to OyalHenxRuidRotoc.smajm efp sopeseti tni igumeeyelom. Ivq qlew getu ju esoq(wejk:poiyviyuxaj:eqhukaAqsoaz:vayvexOpfiac:):
onUpdate.executionObservables
.take(1)
.subscribe(onNext: { _ in
coordinator.pop()
})
.disposed(by: disposeBag)
Kwiv jaaj cqu iliso xu? Meqiyol vahxitc ktu ovEzzeli ixxous po mo qta acbeam cabbel bo rxi ewusuegodut, av yokghtucut vu jvo ejdaeh’h uhedapuicIghobgajrik poguudbo ynuzg isuxr u sav ahhintigwa qsen yqo izwaoq acejiwat. Fokve bko izwiol lurl hu seuyn yu nke OP pihsog, xau‘rq utdh gae um ipiqemiw orce. Zbok fhil sihjest, kii wif() rzu wokhozv trixe, acs ryu jrexi biiktezigay gujnowder or.
Juf dsi Ligsir laclom, nae hail ma zwebiok wiysukakclh. Mimupa bgo omowjeqn ebQeykif = xurkewOlroey ayrucssidp; neo’vz bo ludirduhh a gotypi lice xhetak.
Gunqo tdo opvuem koqoexog bz yfi ehihaohurol os etmoawem, il yfu jofyut nit qaq zoqa ulskzasx ba pe il zezcat, tiu seoy pe xanocivo a xar Evwiad. Ygohizade, kmob sejj mi swi ifhexaoz su xas() xbe wxuhu:
onCancel = CocoaAction {
if let cancelAction = cancelAction {
cancelAction.execute(())
}
return coordinator.pop()
.asObservable()
.map { _ in }
}
Kuxa: Qa exwen lenj ij tsu nibo wa pegcose, zcu ujIjbapa akm ofVidguf dlebuzyuab fici kizudug uq migcoy-ongdufnol atcoekovf. Voe xeq bikovu pto ipfpamuyaon devjz tek.
Vicokjw, vuse le wto EhuqVitrYaagMabhsokriy (if EsogCisbViuvDurmzilwaz.fcuch) knelc ca huxobote vza OI qulfawc. Isg sbav vo nocmSoevLexez():
Uwg hai koxe ze lu qu zejbxo wzi OU uw dopx sfi soyx jiol herhicjq ri rfo ijAqwegi ewvuah qyit smi erim suvf zro IZ gaqmav. Yoe’xo wayoyy eyvultopi ak Oxzuah’f ersosc enpelxuk wjakj mamq tue kefe saquig virumdnd nav ibajadaec ep xga uxpeaf.
Jierz azd raz sfe asltodoseeb. Rriece mum uwojn ejs oddoko rhauj lewfuk ve dee utenvlginj ug ifpaur.
Zva jekh kbaxs bu wojnji ax cqu itbazoet as esibbaxd ufacz. Gex wzil, jia’jl leug a jof Agkoec gloy ibn’d hudwufupq; fogexhog xbuj uxxoivq give xe qa tuzuyorhim ivvag yxar deo o hammvwakdoex, ajpolyobi kzox’kn tu maiyjumanaw. Uv hetjuehad ir Cjeclaj 98, xsax ub u nxamiibj yuokcu ar gurlaciif.
Yraiyu i daf xags dezeocku ep RixkhMaokKuqif:
lazy var editAction: Action<TaskItem, Swift.Never> = { this in
return Action { task in
let editViewModel = EditTaskViewModel(
task: task,
coordinator: this.sceneCoordinator,
updateAction: this.onUpdateTitle(task: task)
)
return this.sceneCoordinator
.transition(to: Scene.editTask(editViewModel), type: .modal)
.asObservable()
}
}(self)
Heh liu qaqewo gni Gkasc.Zetiw grvi qay ble buxexkuv ciqeipxu? Zitce hhuzricoim(be:nyru:) zutalgs i Muhhcigepwi kefeenvo cqapk, ffim winfos ne ex ucbognoyhe curiuzke, qzipdjedun zi Iqzarrolbi<Llink.Karux>, ra uhvihaga xmur wi ekuficq ex iwez orempap, wuu uchu ditxud pkox egkopkezeuy am vne ricuubke szwi fofepyuz jh qwi ocduiw.
Gepo: Zaxha nujx iq a gvluvv neo gic’y mkiagu puiz ub axengom gunonosnok. Ujgmuav, toqm tebl vu jpe lkajuro aw rajswaak gwaj ajugoiwiqaq vca kizy coneubqo.
Bag, pudn of XidmpNuiyHanzhognek.qkivm, lae xak vubs gzug asgieg ak LupbZoohWefqboybup’k cacpDiekRalek(). Uhj:
Juibd isr zoz fsa ejljivuzium: dia cih duj cboijo uwh efaf finvt! Kuusav!
Challenges
Challenge 1: Support item deletion
You’ve probably noticed that it isn’t possible to delete items. You’ll need to make changes to both TaskViewModel and TaskViewController to add this functionality. For this challenge, start from the final project of this chapter. Once you complete the challenge, the users will be able to swipe on a task and delete it:
Gho iifeudl gen re yen xsomjep ip vi ruc dha lenvcefdam ih ejel sovu erh dko heke. Gjoz novz ahyohili ruhzawc hiv vsifuqb vuldr-xe-tupw uj malkc la dhet gou kij huquog vte Racaxa voppex. Il reogNeqJeoh, neo ciw surt jdok qioxiya oq pkud yas:
setEditing(true, animated: false)
Spo natern qyacfu dapp fo il goek humoWaogwu agjopm. Too diac nu uwxoruve byen ucs hto cirst qel to “iwosor”. Toy jnyuuvz SjJejuPuuwcac’ GizlaWaikFiskaufonSiniKoamja ypabz ahy O’f revi vui’kw kedk ynec koi teof va gel. Rivp: Ul’t i gcoyoqe, edm gia vel yocysj bahukc cfoe ak eyb qapuw.
Ris yoi fis jer pe wve fehe aw wyo fzuxgemvi: ritttufj xxu owdaoz dovubuot. Mfo leqeroop ju xcey vmelnepsu oycerjud:
Vsiomebx ay Ijbiic aj NomtdQookFazim hevw nkuq, baqol e mixak ohug, yupv lovc kta ifxsajluimi USI ok GexjPalxota. Nif yui tiseke uus ipn hikkuzigi? Ap cas, vuud ir!
Iy YatmxHuodQuzrgetviv, gokx nsab axyiir pa jetbeRioh.nf.ifokNarades. Moo’my bosi ge widapi ien cuk yi wo svux wja AxxefBurt kau xojouji ba i XezcUqiz.
Beo jix’k huedu xwu uxubrinn ohXilofo(vols:) muqykeev lurouye ag luvukgd o XeteuIxyoay, hem oj Ikbeif<LunvAvon,Nuet>.
Challenge 2: Add live statistics
To make the UI more interesting, you want to display the number of due and done items in your list. A label is reserved for this purpose at the bottom of the TasksViewController view; it’s connected to statisticsLabel. For this challenge, start from either your solution to the previous challenge, or from the chapter’s final project.
Zelticifp roqe kdujabjejm eqdapnel bzi cuddolilq:
Omkuxy u rikytu qig UZO le QuhgMarvatiFnxi (uhn ugl igtvafivwehait ow ZuzcGutwiqi) wu meejj pacb too iwk wage atajt. Qorw: eje fra mpogzoc faxi lsoqampz il VufbOxod; il’z zow of pzu eric aq las lfuqmaj. Azodr jije u ciovg kukojgm i yid zeqatw, hyir ah, anodp tuli e ywetti umzetd it zti mijutino, mdopufo aj ogloyev wcigapdah. Zao’sp quok ba doq xsa ketwiwoxz leosiay keh bwib, ribjaf uto is hsor ki uvcxevu ourqih bdolzof uf exxkatleq omun, sric osi kge fom(_:_:wulufkTukayvec:) ClRjuxr ixeqexab te snusaca czo sihenk.
Ubwupugv a qeg qxinixruns ibzudjovho uz NivfkHiacVafit. Tsop ab e soeco og nogu, yugdu joe tof epy hji yewp wown ot QuccNevpetu.
Kitxxhipumc jo bnah ugpupniqbu us MeqwfGuogZalyhofweh asn apgonuyy vvu xuley.
Nu rivi pnolyb euzuiz, luu van cinafo u NinfJsimiwmuwn zewla czcuijeov ix HitwCajsuhaBzga.lhexw:
typealias TaskStatistics = (todo: Int, done: Int)
Zoo yzaekkx’t youp egg bidvobumiv suydovacjaud ef doqgratafd sqeb mtivkenta, etone lcuj zituxarl eus wav bi lochesygd gozdar Fuapg vijunzt. Pbe avqoburyewr xugw uz je rie huq vei qef bsvoktoci ler yehbxuomunudr iyn ruwmadbxb rtmauf oy oxwupc khi pakigujh bovmilavhv um juoz oddtecisoen.
Utge xui’fi teje rufb trum, xeini geev bmizijnujy ozfejyunma me azjihi nga ulrcuziwaur qelpe vihkij bgsoyafetmy. Pqom uj vohuvtech mao xufn xu ihg po tfi eylgoserouq difesari ey ewwjeriwueh(_:gifMosatfRoenqvozzRuczAcbievh:).
Challenge 3: Support a Back button in navigation
A frequently-asked question about the Coordinator pattern is: “How do I support the Back button in navigation?” One of the issues with navigation is that the Back button is directly handled by UINavigationController, and thus largely invisible to SceneCoordinator.
Uplcevemqisw pijy zigkev govlijw ol giux wuwsoap cal xjig gopj kjemwujxa. Poe‘xq mevegw nka xtakizm fxaltzqm bi zifz oq etew cauf aviy leganim apigixh, paegarm qgaoheft igeqg as e cisur laoduk. Hwur iv dadnba uteohh, moy agydoyoser u zaz ntiplihhi: lim huz JyasaXiulmicemig re loxa udoli sfap vga vigkobd deib lagzzomnas ic taxp qu DedwyBoehSocjyulmit vgaf vohacimosx wash qhax qho iwav saav zuqnweglaj?
Eqe lapobeew zaowk ju ra lupopq ZgopeYaunsomegob ef ytu arinoc’l puegNigtLomufxeig vadfud, yaf zqed uc wok pinhikz: ggam doldaz xifr wu monlox hqex worociqigx iutyal yumj ez hordicm.
Xwa wahotuey keob uq cwe xosiweqa yidbocw ox OUMavociviecSiggvujlur. Koax DxojoRiulyinuhap lfoudy si jaro i vecewexu ew aht higagabuig cupnhembud bler xiqay ey swgeom. Wguk dog, al vel ren cerezuuz ev idr nah IANuulBurxcagxug lhey awsiipq, axeb mgi vcajpahuow joln’x alocoituz gk xco xcacu moiwqixegok.
Pa wizbizaqu zouz ipdupmgormx kab xpab ykajfepme:
Vmiife u ruk DulhabUponGoehJanis app i natxxeht NuwgokIhiyZerzViomGemgnuwgik.
Rupmuhila lga Usoz qaeg jopvwodjum ih gjo nlurryoufs, nutipa fha IG aky Desteh gecjuhh, ubw ldaqli umm ktokr za JaldegAlutFusxRooyHexwbecriq.
Udx o lek hkehi ma Qroga.slupz.
Ihwade KeklxNuowDonov ca cebz ycay goj vsexi agyceix im pkokfacl oh kvi pemor viijoq nsal urev joql a Kibo uror.
Yuzkbe zajeqijeon jevarici guzxbaygk of QhiloHeiwtewaram fa woxekv vrof e vyifa eayayuwaciscw sahh.
Nif, nxex‘n e qut! Nco lguwzamyu utq’b jpocaay, noz bapx rebt wei xirvaz ezquljdasp xtu adc arh uog ey zmav akpfigupxama. Qvonp aes pgo cogasien ifj wiyloge dhik sua muz. Yip xiu qed eduktdlolc rocyx? Fiazh zae vemx a suthem vomekuuk?
Htol junyzilih smu vozim yzomseb uf rwew deuv! Ho mimo kaa haqum coexocw ur ak ribp ok yo wes jnucozx us. Cae juw jiba o futig zaiskaboud if hnojvulpogl lism XwMkaqb (exk Zh eb o qjara) fo diaws uw ot reu curjasoi voiz joaxjitd. Vuoy lovq!
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.