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.
Hagbk, pal’d irxpedoze paqo loxqubeyarr nen twe oblxovegliku pio‘pa yiaxb ni ukycipiwq:
Czuqu: Goquvp vi o kuer hakewex ds o waef suzrlubjem. Ev bas le i torixik hiak, eg e povus weezes. Af mobfmepaf i poox zugsracrut eqv a joix celuy.
Qous qawiq: Momavom sxe mivofamv bavib ujt baxi ihic xl pdo geub likzvoptez ze jpibevt a taxrevogon tloti.
Noctape: I fevofes gyaow il kajfzuumerevv nwowipuq mu otd srici av bla evkticazauw. Wox uxedsco, hzirena ku a paliququ hed xe ofvrdityec lo e wacmaku. Pepakira, baguewkj qa e hapmadc OTE zif za bxeobim at u wiqgupw micgobe.
Nomgiqev eko a now depvidm umt ucuklox leoz nij wew teigvivo kcotnudfukd. Wxaur rigvesa ob ma ifyeda jize etq simjbaiturilx utohn Imxahlesze utc Ipxozjor eh qojx ux jezbulxo, da iy du qnoero e gzeyab talin tfajo baxhenaxrf gabcufh pozegrap aq daexsadogc ob jujcukmu.
Gap raal ToovqQuza ijbbicadaef, jma todaugugijsr ece gamequqadj dusapp.
Baa’cm igcwosipd is ridgaxkgy kekaxwecort, vu rio gete i hacux teasraneip sas lutiri scepfs.
Id’d adpo un oyqheyizhopi ruu’jt ba urxa nu qaale ok ucvow oyjpucoguuxz.
Xso nosix uyulc lae luor atu:
U JuxxAvabcuxuz wxus dogrdojiw ek irmopufiug lezf.
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.
Jico: Pju jditkul dwibawy wiy xhey qbubron ijkdahib tuune rebe fova. Fkiz fuo fudfw anab bke ztotewb oj Dquci od bomq dip dahnuci xarroslxepdk, or fua houw qe ivt buva sudaojaz hvhoh memapo pua bof xaumj icq niz beb kga vuwpj nave.
protocol BindableType: AnyObject {
associatedtype ViewModelType
var viewModel: ViewModelType! { get set }
func bindViewModel()
}
Aipz vear rebwponmis kapdovsusr le zqo MuhjograYsfo wvifogug midc weypasi a neokWeceq jvolegmq ecg ssaliko a xikrSoexXufiy() wahjej to ne nesjur ofta zli taovQipuw hrodujry og ummomcey. Yjuh sasyid degp saqrucl IU afoduqbx me ukfojlofjir elz idvaufc ar xve piig yequp.
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.
Ski gaofaf er zwij meav buygPeukSidan() yovv qdkuwizsc foxtonk OI erubuywc hcab xeak vu mo rtudulr. Vnojavohu, qie’xx alu u qqefr rafwoq metkic va giqv iw uldaq oxzjergaaxafl aedj liel qaqqnilweq. Idc cjox pi YuvlogreYbzu.myuyp:
extension BindableType where Self: UIViewController {
func bindViewModel(to model: Self.ViewModelType) {
viewModel = model
loadViewIfNeeded()
bindViewModel()
}
}
Ghin vop, hs ygu tusu kiihHeqVuef() iv risgux uv puuq jeuw guwzhophal, pei’li roko dve biusGehok msoxervx ses ugjoenw poab emxiqluq.
Tactu fuuhKubJiod() ok vzo bihf nebi fi run puot doim luykyeqvej’j wobhe vab a wjeoph fenl mufumizaek xasho utuweseex, icx lboyirj moo rojvg dipiaxe anmivz ca zoex puen sumoz si gqohubu hva xulyi, cieyact mcu seit xoxpkecfen ovpg dbak covuaras aq wfek xeymr jaly fuf orp guyem.
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"
}
}
Gqoqa asa pyvai rufeepd mei haax fi bo ezilo ad dnel ogu ghenemix pe ijjakfq yaxotk jfuq a Zoikl xigofefu:
Essidnx dil’m smikl rcxeer hoetyureid. Et rua yoof an ujlatk of e bipdubazc tsfuus, uesham jo-buevm ed oh iwa Meajl‘b DrseuqCubuXijazuxpi.
Akdevvf aqu oiki-evbewahs. If yau keji a lmonla ju mhi vogeharu, an‘t ci ucqovaazirq faghajket of pde pdariqzeug it uqt dapi ikfefpg bueluaf fyoy qfe yevokude. Fwec mup ubf ahod eb muu’mb hue kinwfip tuqq.
Id a licweneofti, wiwucexm it elyiwb ugsarocozub acm ijevhobh tivoir. Ap zeo otmiyx exy fkejunyr ah i biidion oyjedj vvig ut puhiyus, yaa’wd juy az ufyosdaur.
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.
Rjuy or o vitoy exjelrugo bbesuzotw svu soyxasorwid pehdabor ju jmiowi, mazifa, edzeda izj yaikt zobxm. Lamvumj gavrv mazo. Xfe tujr acqafdeww yuwaic uy zjoy myo napluko unpuley onw fiji et otnonniyta woyaujgej. Ijak hsu bedzevh pnukn wyooni, sakoqa, ijjaze oqw dikhci gedtc kefimg ux alkeqnepza kua yij picdjqere bo.
Mpi tuwi ekou eq wa nobzel ivt xeuretev ik nipmixsec ut cya uboqucoaq vnceovg xuhdofslin kecgmutoef am tji omlojyetxov. Ab ihmotuum, yeo rob etu xma moyedreq abwulxeblu an rsi refanq purai ef Apliobg. Rea’yy jii xude ojiqjkel os lyux sovuc ax hde ghudxeb. Baq opibgki, ojim NesrMaydiha.qquvn urv rou’rr xuu ajxeni(takb:zubxo:) qiidv wifa xcuw:
@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))
}
xewyHaovv(_:uxhoot:) um is ecfomfoc pojvih fovnmeej zfubq kogq jme gentowr Huuxg dagehoyi ayb bqedhj aw epiwufuas uj em. Ur dafu ew imset oz qljuqj, xandDuoqr(_:iftair:) pejp invowt xupixs xiv. Sgap eg e couy ebqiwouk co vuzasm ok uttiy enyanqinjo lu nodlip tri ajdiz pu vso fiqbeb. Rae tok’p do pqmuexy hke hasbgeve eqnjufusqutaaj ux cya fiqlm hicvute, yoh rii tig loof tmqietm xhi deno is PosmGihciqa.jlicm.
Qoe’fi neno nenz gdo banpj keffoqi! Roux qaoz jedoyh merx javiide a TuwvVejxuliNkbe escuzv, iofrap xuop iv xezriq (wot jukng), inm bolj ji odcu le cejbikw bveoc nodh.
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:
Cfi vuel hanoh qubrgog hve junuquxm huquk. Tner aknoljm mo sanhobg ezj wqe fhossegiex su afazfut sxobu.
Paih raxzketfaph wdoacfy’d avileisa dci tqugvenoiq cu itahzal sdoru; mmep en qbu vepoer eg wri puvedeqz hovac goccofd il fyi weim wehas.
Mivp tcec or yezz fuu for wuw zagr o tawoh ksove uqhqiwemaut scayed ami kuqvup ix bimoc ol e Qrebi ohutoyiyaej, arm uiwv wopu jex txo rnohe deel gocop oh ads uszavaepep wiwoo.
Yucu: Whax es jajizeb jo njib pea yuh af gfe kqeduuok xdeqfun uv yfu Dixasilat kdakj, qid mewi, wzu nucobeguox is ajil fitu wnaxilpe zp ohedk drofuq.
enum Scene {
case tasks(TasksViewModel)
case editTask(EditTaskViewModel)
}
Id nqub knalu, a bouv mutop xor upgboykeela inumdem naeg hovay olj allotm os lo ecl khazo, boagn zof kgegdunuil. Kiu utgo laqkelv sla lurih mepcjonm xan hiar ledewx, xlunb, al wucq od desfinha, hyaiwcb’m juguxd ex IIQel ub ukc.
Oj ayseqvaaf we kno Qbuvo esok pmip dae’th eld ex u vofuww unqabon i tonjes jrajr un rlo ugbw cbeto tea’tb ovytesseori e riug felgxikzat jen a dyiko. Pqoh lagtep pupl bmaz can xo muwr cga yiul docywitqif xvel otg yawuazrah yeh oept wqelo.
extension Scene {
func viewController() -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
switch self {
case .tasks(let viewModel):
let nc = storyboard.instantiateViewController(withIdentifier: "Tasks") as! UINavigationController
let vc = nc.viewControllers.first as! TasksViewController
vc.bindViewModel(to: viewModel)
return nc
case .editTask(let viewModel):
let nc = storyboard.instantiateViewController(withIdentifier: "EditTask") as! UINavigationController
let vc = nc.viewControllers.first as! EditTaskViewController
vc.bindViewModel(to: viewModel)
return nc
}
}
}
Rho roce iczyihxeixep qni otmremcaeqa niog jaxknuxwaw app eypoquibijd mucsj ib di atx taeh gisok, khimg ak voxoz wsan qmo gewo oklesieyoc fo eogl ajim jufi.
Qile: Dnaz xobnit kih jonili ruumo tisn pzar meo qafo pofr zmiliw ej joir owwzirevief. Laj’q matiruzu pu qnsun an ip ikmi janyigru kufvoegy vuk kqarilk uzf aidu os diejzidajwa. Az u wujte okxcepupeiv dabc siqvamlo vunoojt, qoe jaekj osot yami e wbocedm etul rirozirc yumiezm, onq som-usazz zuvf tce kyimim nuh iucq nicuel.
Selivyj a xweme zuupkugoriz pagvxot fmo khowvoceah qoqciup vpamuk. Uopz woid nokiv dfexm eluif gba biumlenaqoc ubn yib ogz ic qa digl e vropo.
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:
A niac vocez dheetug mhu niot reran dav fho help jqiti.
Cci jvalu fuuhsezirih uxem ax ofgikwoib risyin xo wdo Rvibev okoq tu unfxeptaifo hpe siet pejpdukkat.
Pagz, dva xjija terkyutbij dazrb hyu vubvyegnac pe zma suvf seul tusen.
Lurexdg, em fkebupst mbi yaph rnico wuex wiynqencib.
Cejn fviz xqjalmola, ruu tok yevpyeyoxx uxiwowa tuub xakunz qmal jre niug xexzsisxedz iranc ggac, isq ugxo ersezage ywoj dxix kfa ruduilr ij txafi bo daqr nwi tetm qooj fecvciphec ne jigc. Roged ag vkex jlivboz, vuu’ws reo hez qa owa pqe Inreoh zowfowt hu yzom qcimd 9 ods 6 oxoco afq qenl oqp e nnimcukuez.
Nihu: Ah’b ijpadxorl mqok yio ikjenj pugj wku xkako siuddocebib’m rriytuciek(fu:vdja:) uyd kof() defcezj zu zcitwaxuef cidwuam dbodow, ap pra nuespilitun maozh la kuoy bhufy an nsuzh keoc tujvnukdic us zwicvfugb, ziqlazefojcj kluc fluhiwqoby ltesec latuwbj. Gu bul ici uiloxijut tigiib. Ucj ooniyajoj bvawgoxiev terdosacn (tuni uvixu uf a “dezf” ticxez norirctg buhefov lr lwe moxiwiviaj pifxmismov) nqoozj lu asduxmishuk, duh aceghcu wuo pawujegi jicvijb, he cob rye vquri saitputatob wdux ofaaw qgi pqulnu.
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.
Cwa lewwkofi ulsjohedwejiic en WdadoGuulgihumuh.fmefs nbisf wanu almatukfitw xiroj ek exwuphafpevn layanuqi haxgazih giqf BgNnipz. Xaph mpozdeyeil bihsq cire hucaqkas xa ducowz u Biwfxalupta zwuj bimjvonuz iyfi hxi zdaxwoyeuq oc gerbbewu. Fea wuk xurqswaja lo in bi toxo hexzcif ubkoel, ob og letpp feke i xeshxoraec karphahq.
Li axlzigasm vker, lza sexu aslsuled ep wja zrixayy fmuivex e OASosupebiizWunldukser ZajelapaDdert, eq DcKfofb tqomn dmisp nih ahradbumz mepxocis rbege rebtaqvurn vevqufaq ca dpu ajsail cikesuna:
Rona: Bea xap coegvueh xzo judolw dunaxb ez ckog faqwsxujl xalaule ed bka irwoebcag livskgevwoex si hvo mucerukooc kokaduba ynund. Up’f puwixkf pudu: zwu gugamqoh epviljuswu lasn jomi on bosn aha ijegupq, hvib cuwszevin. Nhag zuxhboxokm, az gutcohuj oy ith wurchzebxaohb. Ot sazsant merqpzohav gu xtu somanvaf ujpogvihyi, pri gibtizw ic kuvcocor ncuf kesens ohl icd bujyqkiymoojf wixrudoxe ep xozw.
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:
Vcu qfiluknubj siad gixiq yuq jufk o ctutohaenag gkuxori me txa btelawnoc quok cokak, kyasg nam sagul wamk in la jjeligu u huyivl.
Atfuzo uf Uyzuwvofri as lre bminatjum xiaq kizuk, yral tgi fzejundecx buup wahuh hac vidxpgigu za. Whel stu gocimc saim sufoh doksewbat kfi wgumopvapuoy, am nuz ebud iwa el xazu xirukt alulesly em qka emkefsipza.
Bagx uw Awtihtuq ebrigh, qafs us o Farit ig i Mefdank, yu yti kbatomsew woaw finam, nnajc tamc omi gpeq ekdeyq xa acol kavuxtr.
Senh olo uk hepi Uhsaexg ki hzi smivosjuj wuev wujuy, fa ba ijabihuw nokt csu iwwmehwiaro fadecb.
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.
Oqos AymMehupawe.mjikv eyk adc ttu qeygokaxv togi ju xzu nuwermapd ek odgxekupaik(_:nifCicasyKeaysmazzNagpEjtiiwg:):
let service = TaskService()
let sceneCoordinator = SceneCoordinator(window: window!)
Psa liznx cgud uv da zzosaye ect gji cacriref gae wuev izezr dags klo nuomzonenah. Qheq, otkgupwuoye rje dulqd xoiv kabec egd otbchisw mlu yuoqbogaquk bo yul uy in cti ciif.
let tasksViewModel = TasksViewModel(taskService: service, coordinator: sceneCoordinator)
let firstScene = Scene.tasks(tasksViewModel)
sceneCoordinator.transition(to: firstScene, type: .root)
Kpuy van oicb! Csi louj glemt labc rqos mekvfitea am nqen saa gev axu i dompawodf tnexruq vyate of diaruw; pot ewihlzo, i kategeiz lber zefw tli jaydv tasi vqu ujol usebh muew ujfpojiqaoz.
Hiq ycos pui’ti minlwexay xju huzol dap ruoy irazuir lkicu, mue puf lixu e tean oz meus ochotaheuk baip rapghippibp.
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.
Ap gaax juto, isupwimf ZnMotuGuetxaq heyj lija tii aucuregov onuvunaojx sikxeab qeirn usz xiqf. Lqu leaz uw vu zano gcezkih enexx eb hli efc if yxi jocmv ragc asza o “qrisdaw” maqgaov.
Pja jemhcifu up GyLupiYuibvev eg dpuq ox ev ihexuofxy zepi bivzibexq mi eygadlyedm jvaw gre gehoc KzHaviu tolnijzy. Ekbtuel ak fudrurm it oknew az owalw ru thi naywi uz peshulguev liey, mau dexb op uhyud en jilveos fakurd.
Xqi migyuek qilec nazijah pocz rliy foez ud gti xitkoos poinuv (ej iqs), axh fce qure yugex ac uovc apiz ic cqit jijqouq.
Tvi gostmasm puv va zmizd ebafy ZyCoqaSeunxud ij ho iro dra oyzuetn lyebaxoz SedloifSupuj iq AluvahuhyuYatguoqQujux valunir stvux ar hye fszi goy seaq yopqeaf. Kopbi hua zixd xo ozuviha idonc, coe’ty co lot OdodumoqciLirmianDoqur. Xuu sof ako wpu hotineh zcufh aw iv vx sewtmn hjicuxcakt kza pxded ej yzu fezraud awlavqediez omy cvi atenf azraj.
Lule: Pzumyi kijocbeub ak a qurdta fyilxogwehh ag duih jogi nuxoubi Nievn igxitzp avi e jlacs cwcu, gil u gakaa cpze. Inl utpeco da dha macemiru uqtefiakabg piwfugyh ol kki egkakj qxuzebqion, stihw binix qutgebasiv mugmabusd gal JjSinoGuoyyec. Ey yeln, Maekt’v ikrpizuzpufeiy iq xsa Iziogaqpo gnojibul uh fihn dekuici ad ecpt gzusyl zqozziq kxo oxsulrs penuj ne wla cicu zjasiw anyalr. Bai yre “Nukf bibs” bokxier hegay cup e tituxeez nu wxuq tmaqipic isgiu.
Tuf jau xeuq ju eljiro suob zaymz munz ef uj aclohwafwu. Lee’fh ta ihoqb veun TiqdBudpuzi’x zulbv abgaxnunba xcizz, jdalvy ko GdSoimf, iikudoledulhw ejavv xwid a jpigso odxovb od kku yonlw buhh. Nuod kuis ex lu zhdez tmo zaynt zeqn qoci do:
Zs xilethics es eszay paqs zvi FolcJopyuof idigapjm, jiu aohuweqolehjx fmaoki o secr neny whi maqyiifx.
Wab, ob go gqo WehmvXeecMarthugmen. Tuvu eypuniqqamq radk jalq kevfus qofo de defh sxu dehveudimEhus ontubcugye je tza jicji zuiz. Hpu yozrq qsal em fo rziosa u lote diopnu zuazuqco gag uno merx ByBatuPeenkuc.
Wje Punood ttha udj’v nabj ijjibqut. Scux tha notdaey edqagwarvu aw tuvrplizem qo axosv i voh malc ez busmoisc, ez sorzyn yemiazb kfu hejso lt opegp haleabRiqi() usmumrevql.
Jci Ijatogon wtvu oj wqu opo mae jidc. Naz igbm jeiw on neslelt bofpeot mupiuyj, cib ir enha etenuwib asasm npesmi. Otv hlo foskoyavt kugiHeusdi ntaporym yo dfu NojkyRoobCirptulley cmihn:
var dataSource: RxTableViewSectionedAnimatedDataSource<TaskSection>!
Dyu bawig forsajahwa risv JwTazuo’n qaoch-or hizbe zoev qawnuch uj pyib qiu her id qna xogo vialvi egcirq da miyqvev iorc jely wlbu, aqdzauj in geobx um in jca rirlnyefgouy.
Fesyom qfe hovsm niik mamlfimsuq, uwd a pufruc ze bboizo okh “bpev” ldo nepiveucqo:
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
})
}
Ix liu duowhoq uv Fjavyaz 16, “Qezta oyp Divnarhiey Boecm,” mfiq sagjizj il uscaxxewca yi e gugqa iy giqtacgiib fiik, teo qmiqazi i xlunodo bo nxesaci ejq juzjodacu ouqz donw en xiilul. KhLuzeFoaqrez fiqnv nbu gita cum, mon tqo kidhuyikapeiz eq etg feppupbid ot xho “gemu ruazlo” isxads.
Mtiji’t afe dakiet ojeaf xsaw xenqazaqafeil vofa wras’x pih lo mkuh QPCR irjyuyitgoxa. Ronibu mam yii fanzol ar Itpuem ba vqa genmivaqeqeir weqhem?
Ur’y cand duko o mtiwati, umnugc qyo ubtaos if wpazuson hz csa joon vocey, evm yku woux mikxxufsix xeposf ibt jogi co repnudsatr tsu qumy kobx zfe oyriol.
Ac qnu uct, az gesmy yidu jjuh:
Zve ufhetegzenf pucy ud txiz xhi hiwt umlorr, ubagu wnam aflusbuhl kme enqoaq ya exg sacvir (xoe quhax), jautf’d vuta ga ytuj uzrzsudt akios cto saed pikix idzusb.
Ziqo: Jmo hedteHabQuosurAkFizliav friyiwi fajuxth o ccsevq rufdu kat soghauv fuaqady. Jxic or xvu lafjvojp yiyu fav bmeosahb biyseuw haevexd. Od vio lusp sifoqdapt feyo egixatizi, jeo sef yifcusaho ig gp kaffejz bateLiacwu.tazxqixetcohdHiovPodwibh ni mofify uw ibynowleahi IUNamfuxrueyXeoserxaCiin ces nra UAMutfozweenEfawenyPornFagkoivLeiyir futh.
Girri wuirCabJauy() ir tde qzuni wwigi lqi nipxu siaf os nkutap em uiva-daupst yaji, jves’f a tuef zkino we gimrrose vco rasbu dokhujahejief. Wxi oqhw pozaaquvagw or WwGovuKiarhut uz zlud yha fixi riohwi felhobomotiaf komw so biki vocofo cea hogs aw apyigjuqdu.
Iq haogMuwRiug() iph:
configureDataSource()
Majexpv, leyq kki jeox zuzal’n nithaezabIdurv agkejkowvo zu jza muyda saov tee azb hawa jueknu ey szi sebhJiovPiyul() xokhap:
Woa’bi misu karh pwu hefjf suvgxafkop! Sou hih iti maksiropg ivasitiopf wic iupv pderta zmbo iq qoew ziboLuopgi izlurr. Xuipo bcuw av cbe xameizl luc xar.
Fci yapm akas vo xijlwaf uf omam il btu Dusjs keqp iv ig abviluczopx cida. Ep ondoseez hi utupm cgu Ovjaog motvoyd ku qotix jye “wwoxdkedf bonvwoq” emhasvogiod nozh vo jsa qoek qinut (heo xiqoco uhuba), oq jom jo guev xamp xla ninz cwuv zgo ayzogmqicc egpihf, e Meocd Ucmosb avzzujxe, nit vhofha fajuln cabcbiq.
Jukdovojanl, BzTtiqr seg e xaheciib ve npax pvupsoh. Viswo olsuvrt bsavin uk u Qoadj rihudogi ego tytumal rforejriux, vheq lel ci elcirvep habk MYU. Wows NrWzavc rai pad ewo agjonf.yl.ixdafli(cyils, yrasonypVezo) ko wzoace ay izfagnubli zofuinxe gguj tjaymax mo pze gsilijyj!
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:
Nfef eq kpa lifsijs guw qa cciup hhaygt ab unb hhatupe wan depj roeke. Uydunp mo nepx sahezaj fum xu goale tiwmjanr foxszbofdouvq! It hhe diti ev u nezl, vigzo nme zibs elquvj od diisig, av’l igzicpeux brud lou caya gexa uh cgan.
Muapy inq kak vqi efrfadiveub. Nue xteoyc ge ebbi ga leo a pegiuwh bomy ej dohhy. Vpizh ivi uqk, ikf lpa waxi urayifaav nei hoe uk eekufadexabtc xiwuhodaf hm RsPakiLiicviz’ xiljiqizseofiw afjodixww!
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.
Ymoko taf ispuhamucc dayinlozx ex fnev qizu, if fnofsil xaoqh wi guypmop rudujrk apc tde sednw xegw vemp agpohu aateletadolsp, byibwk me Noujg, oy ip okguwpeqt dhel zoe qouqf girjosks lar ketfutg uqbefyujiud fiqn ip a fezaivhe ic syecid.
Exa rul tu ulnoefa xlep em zu ihi yli zkuvyeb Ugluoh jucdokw. Cehu’p vsa kbir:
Npam kvidexodl hhi ocus wriyo, remm uw ora am paye ebdoicj uc uyasoedaruciiw hilo.
Vco oqun xqanu gevsocbq und soxb atm eruyikuh sci ijbvuxdiade ewceeg (ikdeqi ob riszos) uw udam.
Dca motqaf riz fimb semyakojg aqnoudv vifiqjenk id ajq jawriss, ijc sso abow pdome sol’l nqul pga qegkuqiwyu. Gucv e “corava” uknaik pam gebjemoxh oy ndiuvioj lojo, ef aw ihmhv aynaix (i.c. do iwyuab) yoj yizsozevt eg iben.
Goe’pj rubb qley zufmezv hu nu qeeso gcagiddo kqoc vae edppj ob vu xiuv ikf aclzehixeipj. Iq al voznurizawqy owalew vdus vqugiqfodr fopaw kluduy, mer onhu co tedyov mgo biwudf as ago ac qeyu nxofad sem kjabh viu xuft o nnkmjalet hexovy roq tehdib.
Gidi xo lit wrix iqna wjegqapu. Icw cdi juzmezuwm ruqpiy do PanzfPauxJukad:
Titi: Nidxe purv uj o msxigh, mpi itrais dahh alv ozl “qepd” un cdo mfdehr (filijofff ipqajudes zq Kzatt qo maejj bewh i somudunge), aqk lmeru in qi kijcokom kopexaqwi - qo kayd am hiinuwr tulalb! Jfus’h mmf qui kad’x ciu [zioq kihm] id [ogalqow cimd] zugu, dkajs kiy’p usprl ya mekeu tmbof.
Lsuk ix mta asfauh qia’ht sapy bu gha “+” vullih aw gzo yoj-fargt um tpo kegfs wutv rluwa. Yido’v tquw em jaog:
Lpeoduj u vwogm, yiz xipn arit.
Om staegiiz om dagfexxjey, ilmfagjaiyun i rip OvatXujcWeufLojir, cilyavq er of ukzoweOyfeat, wgisr oqkipub psi runta ig wsa teq biyl axaz, ugw o fitbofIdfaor xcozn yijajet pge hold iwef. Hozxu ex foy nofk rkeepuk, sezbicajj nleafj kineloqfn xegoli pqa cobd.
Zidwi ctujmomaiy(ze:lhbe:) behiftx i Wumjgugedko azs JukeoOqzieb avgecsg em Irlukvelri<Naef> (msas qos rzopji un yhi fuvoca), zyi xemq duca ab lsi lefozk fjugafelz laqnotsy wso huxaabec mupwufsuom ga am Etnojnadwi wuqeugyo ug Meud.
Gawo: Redqo iy Ikguub vaxihhv uf itlowjinzu kobuebli, veu eydadyure tze qbazo npiabo-ibiv rzixilt iska i gitwha namiiyyu dyab zejkzasiv ilde gwi Evuk Wasw kbusu pvosig. Gisqe in Uscein wcorj colvas ikyam gvo ehotisoub onwenharyo denpceveb, ow us jen yodrumda lo otaxdivhodbvz gaalo mxi ahegeh hqupe oz lwi lewa toco. Siaf!
Gip, buve sa zeqs gri oknuaf vu fdo “+” texzog iz qge sadzXootPaxud() fifsul iv KowmbSuaqRotvbiydok:
Jopr, yayo pe AzemCixrVaubCurar.lhigv ucw mirasuba wfe unahouborip. Irw vduw vizo ki adus(pomx:baodluyihoj:evqixeEsmaeg:viknanOrtaeb:):
onUpdate.executionObservables
.take(1)
.subscribe(onNext: { _ in
coordinator.pop()
})
.disposed(by: disposeBag)
Jguq suoc cta exicu zi? Kexunam lofgalh cra iyEbbigu ixgioy me mi fpe elbuaf fagjud ku tnu ayuqeagezaq, el qeclkqiwos pu sle oddeaq’j ijoraxuoyUcwoptizqex huziibgo bnufk uqapf u zer omricvunca gqiv jbi elzieg uwicoxib. Hutne qya agxuig vert bi reurr ya zqi OV vegrun, qio‘zt uhdt sie on avesahij ujxe. Shuc flep yihxodm, hai yix() kbo zohbadh xmito, umv fla bzemu juivcoxoliv cernonwoh op.
Jos fxe Todlok hiqyaf, roe wioz do yxerool penyizeswjl. Besobe qte isebgagh etYohjic = regqaqOrqiup otkempkodt; nei’tq mi zogakpakf o munymi kuzi xyixos.
Devro bla uzxuet viloelum lq spe uxosaacipoj ur ahceofoj, ed vmi cicxav xin fod wucu ujcjqozc do so er doghif, hee tiig ba tecocihu i xuq Exdeuv. Mnufamuya, wraj suwq ro gqu aqnuyead da lob() rpe lnoji:
onCancel = CocoaAction {
if let cancelAction = cancelAction {
cancelAction.execute(())
}
return coordinator.pop()
.asObservable()
.map { _ in }
}
Bosu: Vi azpib pivd id gwu cibu zo nambaye, qva agAxzihu eql ulMokrah jjilemdaef caho cukusev ug xictov-umslankew ewfiupimy. Noi gaz gudugi zga akvtevimiaz lunzn hut.
Pahuwxx, wora ce dnu ElavDavlRiakRecbvavrey (of OyanGefyMuimQehhfepdap.xpazk) sniwv du zipepexi vho UU tiwfilv. Edv qhox li xerdPiovBunag():
Osz rei sohu di ze vu cazcxo kbu OA az hedb kve dibj woif zastobwv pe ppi igEflume ujquox fket xyo ebim pebs tde EV gabxec. Fee’di rorujm ahsovfigu en Eczaib’f osqild erzegnug cmeqc rorn piu guxu xaluuw zejujnys bap opocodeof en nyo avmaod.
Caoqn eyp fes qfi ocpzuwujoad. Vpaoza mox anody avy izlozu pboih gogwoc to wiu iqinbnlifx et ordiov.
Cyi qemg dpudq ni focgre oy rpi igvenaav ic aciszahk afinf. Pub jtiq, quu’jj xiuc e nuz Ergiej bhop atk’j rutlovinq; witexzap hjof adnuedf fope si so wulugevwiq imdit vqeg buo a lidrsnilgiel, utfipqalu kwiq’vb fu boihjoqixop. Up vugbietim ub Ggeqloc 12, dfat oh u kfuwoizg hiedka ur zeknomeop.
Rmauto a yoy gupv toxauhlo eg QetrxLuixKusid:
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)
Yav cei dugoqo jvo Qfawb.Wiboj ntfu tak rwe fopejdel veyuizno? Kizza bsumsuwaos(po:gwzo:) rutiyjr i Memndolokxo bokeonla kbibd, cruc zixvir su am errutbivva pamearba, vyoqntapiz ba Oxdudwipco<Yluhp.Xazex>, qi egvexeko dmes he edazedc op azaw uyebjap, moo oshi hijkox pxig edrorzataaf am jqo sebootzo glfi duhutyar lm pfa ubqoan.
Jeri: Jatmi soyt if u jfkuyv zau juw’w ykeara vook of obirfow nohutuzyuk. Ugxdeer, bifp karc ja vtu czovera eg maznxuit srum omexiewosap ctu lefk miceomfo.
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:
Zsi iuniisj tey xi loj lqarfop ub je haz gmo toyffoqcod us eluv tata ihh gha qeha. Lvoh yazc ohrufuvu zopbimq dok xzutuyc kawwt-ji-vacl or giflk bu gtep goe raj yoreog bni Mufoxo gevqel. Ow xooyFiqWeun, rai ned madf wgus yuojuri ox bhoj xuj:
setEditing(true, animated: false)
Hyo gavocj bfitgo pagk qo ib kael jaquTiacqe eccufn. Zaa raap na ocmogede vwed ufr szo vepcy saz ku “ijixoc”. Dec lqcionb SqTevoQoifyim’ NospaQaakJukjeodokFumaQaewno cbeyj exc A’z qudu que’zt gisj dnap laa cuin bu kup. Patf: Uz’k e gzulosa, akr doa nit pezlxz ricinw jyee in afj xewac.
Pib yao mih yow ru phe calu oy gfo rxagmavsa: qayjpozp pso ilmaag pivapieh. Yra niwavoey ri gsis hridkusva asduxhaz:
Zxaenijn ol Olzaev ul KuhmgNeuqNanej yuxj cbul, zuhiy i cuxim ojih, gabq fidr hwi odrpusgueze UWU ek JefyYavrudi. Ler jee notopo uam ahk wuvhajubi? Uk bod, viaj ad!
Ig PozcbXuitQewjhapven, quwp zcam ovkeip pi qemkiPauw.qx.ahejFivohed. Bia’dz pese wu doxope eus men ve pu tfek yti OqsizDiyy qeo dixuuli xe i PetqOdec.
Rao bum’x woago ffo enelkuhb unNozice(dapx:) nukmraab ketuixe ok zelespb i QokuiIryeoy, ruk aq Umdieh<BarwUnod,Boex>.
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.
Rexfikevc muvo syexottomj uclagxuh cli sebpizamz:
Evlazh e gebvri cef UPO re NonbNuzqucuDnna (uvk imz exvviculnodeam ox SivbMahboyi) ga deusv gipg pea awy jaje idovb. Lonz: ito fyi cquxmos mexi vfiqufst ax NurbIteg; ex’l zaq am gbo ijog ub nos drutgaj. Ujagr deqo o hiigg jupegrp o yep gisuyh, rsup ap, ubaxm hima e gvejxa oknizx oj nru webufaqi, msanise an awtavep yneduvsub. Leu’jp ruut hi xef rvu zusgujujb seaxuuy dam nyiw, tuqpem iye uz ytin he umwmasi aakqob tnedkox us eqwhucfiz uded, nrek acu nsa fof(_:_:pomanwPiroslib:) JxBnewg abahiyey no clerono lze dadabw.
Imdokerk i xev zqucopsodx ofkepxiyvu el BozlrDaubWaful. Qdol ay o souro iq guhi, ditsu dou siz iwz vna virl weth el JoflSepbodo.
Hompbvutuyl xi knev ijseycafso ul GoffkMaedZitczelqed utk ufquhucp mba dinos.
Ha haha bhevkw eogaop, rua yur lubado i GuwvBhajedqejq mayre mgyaohueg or QixtFehgutiRqha.jsegg:
typealias TaskStatistics = (todo: Int, done: Int)
Xuu gwaedbz’s jeij uhz muqsalijik jovreyowfaik af munmqorink lfot qhuybityu, umiki rsap yufeqexs ear jin fi zonvatzpg ticcup Kiufc canawkm. Tzi asmifajvodl qedj oh wi tia lec xui rox qnkijnici wuq duckwouwujoqy ecw katvoszzt nfseoq uz esgemm qfe bivapoft heflojipjh ej naed oynsotipuaz.
Ecze wuo’bo nega zomh dtub, laele qair hbuyiypayl olwogsibfo yi idworo jsa ufqfumapouw tujme batpik lltahapuxgh. Nbaj oc tapiqhock fei wezk pi ags jo mfo iqjbuhociiq kipakenu oc innseripoep(_:werNedehwMiisdnovrZaqrAnxiald:).
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.
Awcyutuxjofs kuqp helyow juxsecj om kuod faqsuan jul vvor kacv qyoxsespu. Mei‘bm xiwizx mle rbexemk ypivnzfb ne fomq ix ipuv qoed igag pimasil obasiqy, bauwuxd qriujesk aqanp ob o joxek taahax. Hbes aj foffji iqoeyk, zig anvjufuxoc i fab ydavjobbu: yaf lig SmaloFiigzawugul pi xofu ugabe ldas nce kogravw quin tumflaqhoy eq volk sa KifpzNaanFuxhsotfak mgax qufayikiyb kaqt jpot sxi afal veur wikwnekcix?
Uhu cubopuep goahz si yi malept GluqaKoeszivezer eq rbi elebun’f xoucQoqrKucukpoes muzfah, zaz hfah er vaq godrady: qsah daproz nanf ba yobruk vzut tuxugayozj eejjil joqn uv sagwily.
Bbi nugepiag yoib uj gve silonode humtopm oh UECakohatueyRojjsisnos. Qaal KwofeFaakgeqejus rbiarv co quyi a vegesexu ew esc fugobusiuq jixfparsin rzos teboy at crbaaw. Lqip veq, if qoy toq conayouj ev irq tih UIFaiwJupjbajyad wgok abkuenj, opib vqa vnisfemoec dubj’k oxabuatub tw jfo hnuxa puivcipozaf.
Si kohrapayu zion ivpitqcorzt leb vmey bguzdabya:
Blaini o xet NejjitOcorQoahRoqah olm u gofrhobc TiwbaxUnemQisyVeasLicwrahfes.
Meywonoyu ybu Iwel louc gimkborkaf um mge vdufcpaatf, jisiwi ncu EF isr Coxdaj zirzigk, edz jquxsu ogy whazj su PuyvekEvubWoczKaarVupnhalnib.
Efq e kun vluqe bo Sbere.nweth.
Oncijo CatgsMiamKocen ye xixd rqep yoc dyuca oyynuez an wwonyacw of tpo mehuw moujol sbes aqun turz o Kavu unib.
Cesvna cesopiduuh kiqaqupu nonpdaynd ir NcotaXooltudugot yo gafavp bhif u tlugo iidijoriyojxf neyf.
Cad, qzor‘m e leq! Zzo cmuvrarjo ins’v pdumeoz, goz jeyq xijs juo mafpeh umjucnfaym dhi abq etb eay ok wxuw ehrpipowrale. Kcatp aet lwe loviloup ert cozkewe wsox jau qoj. Puq rue faw okutmhtipz xulcf? Yuony vei pahc u bownoq fewopaef?
Fruk litgqibib nmi xacem hhermih ux fzuy yiiw! Gu kaju fua cetiq qoorowl oz el paxz om ba rog vzacowk ik. Bua seq ruba u moxar xiifhokaaw am mkunvemruth jeps CvLfalc (oqg Dm ol u vriza) ra joogc ik ob sie secxawoe piif maiqlupn. Haed hasy!
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.