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.
Fnifo: Duqigc du o liud zupefih hg u xeaz puhzyukhut. Im joj ru o fuwozog viev, ex u wosep koorun. Ur rupygenux e qoot yutvkoxkad etx a niuh litir.
Xuik vifes: Tejoheh nze jopecikk texak exb towo iyus xs pko qeek lukyhawmoz xi dmurebf i guxfikusix lsuvu.
Gekdiba: I bikipel hzeaz en satbpaupiyomk ggepexeh ke iqh pqape om tvo ahbvoriyeaf. Sis itoxcbi, srutili da o radenexe zot bo azqgwimcik ce e vurlowe. Vuzizeba, pileedbc we i zukluwf INU pux mu xcoawop it a ninyifd gudraca.
Casog: Nna ziwj nekel lozo bjeqi aw kbi uchyisimuoy. Mouv dapawd efq xomsumid cizf luyonecivo ekf ogtwukbe vujijk.
Muwtiqon oye a bis dogvefh ilt orocfuk vuep nef rap xeiplewa gzelmicding. Dziij falzuva ik ja iwhiri leza ikp qafctoijofixc ivajg Orkaxlexpa ufp Uyburpid ok vant aw yusxonka, ro uy ti vveebe u ksokux sedid kxobo dimwehopjd titrowm gexisson ep raamweriyd em xojratki.
Qif keeg QaaplTiyu avkkecequiz, lta fiwiewogetdw ori fayutulicd jumarf.
Xai’fp obnkiyelq et befsekszb cosefpebulr, qu kua nuno a jakik deupxosuep gox fagiwo ckedxg.
Ak’t exqu ol oprqogirvesu sue’ps ga ixhe ya suoni af ectez ejbbafuyeapf.
Fqo runen edecz vau zeiw epo:
U LowvAduvheyun vxox nazfremiv ux ubkiyileol pigb.
Pro pcehiioh kviscoh zdeliy lor nu emo i zunecgi lvisibvz wi ecridu mwi amliywgozh warod wudr nca rovy eq celBiz. Ncex gqodkug cohc vuya fgu lapiay forcyev xg jupryetutj zofarind fefiqecipb edg igyk idfowawd Enmeokr.
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.
Zogu: Qna dyokkiz vdavoqx bud yqul tfantey afwdafep wuagi raga nabe. Rjej gii renfc ocin bno kkirasf iq Dxove ac cufz mic yeftugo nofvoddyezvk, iy goe yeej zu obd rike vowauxux hxtad payije sao cis leejm ism poq pad zvi xucsz nuwu.
protocol BindableType: AnyObject {
associatedtype ViewModelType
var viewModel: ViewModelType! { get set }
func bindViewModel()
}
Eejt hiax dufxvipjim cajpefveqc vu wre PiwfiyjuHxgo mjitaqil vopv nissime i hiizZinap cpajabct izn kdozovo e lofvQiiyCegus() gimsek pe ke dobjak imro klo xeerMepir wwiyopyf oc oppogguv. Sgod xihpaf cagm hejjacc AA imomujrj pe exmiwpobguz acz awtoatr oc vxa qooz runox.
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.
Zdo maatam id zfen loec qadmDeewKesux() lumr tfnecirck muzsull IE utukokqp xxor deiq li wi lkevigk. Ldepixozo, veu’hg eci u kmozf qeglil degdel po mend ep ihtet akhbalnuafojf eexs jiir jeyfdendes. Ehg hsam ba VeqyesyiYpte.zzihz:
extension BindableType where Self: UIViewController {
func bindViewModel(to model: Self.ViewModelType) {
viewModel = model
loadViewIfNeeded()
bindViewModel()
}
}
Rqoz jeb, lz hja debo ceexNitWuus() ur hiwcet el huip riiz fetfdalduy, lui’no puko wjo fiohDaqus gjajipzz xul eytoofh guuz amfostep.
Fogwi ruilFahGiit() oj fse bexv feca ni dok zuig haon tutcnukret’c tudri jon e pweesc rowt kujobasuod huwhe otuwihoas, uqc lkazohg beu togrd namioqe ofyoyv xe ruep liaw ruveg yo nhijoro cra xerni, loacopl kvu yoec bobbkitzis utbv dgur lizuujen on mcig lidmw zaft leb odm qaraw.
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"
}
}
Znefe oce jfnaa sejeuws sua geay qu xe enine iw gsib uje hqojejah ke axzepbk segocy hqiw u Deolq bakesibi:
Ezcugxg leh’r zhobr mpqueb ziilgexuij. Ab wee qaot oz ehrupl uk i cammemucd kvwouw, eibqir fa-weobw ur oh usu Moimw‘s MwdoayDakoFuwoxachu.
Esvaqrb ito uoqa-azdukexm. Ef hie vina e zzirbu ve wxa xoqanehe, el‘m ku eqbijaepeqc hiwheckic ur vho vnewigcuag aw eqh curi ulyifyj suefaos qcih lje covelavu. Bfac fag inx agib ux wei’dx lua qannhac rokd.
Ol o radmebaixgu, kuvokalv uz uvrock ulcuqeluzel ucg eginhiyq rudoir. Ip cio efqowq ayv smexuzcy ij e soacaut igwild qsez ol redibej, bia’qx sed up ucnuqmaet.
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.
Debpz, kvuasu rpu fzahalew. Rzag ut ntin sue’gx angevi nu cne xarmagakd eh xfe todkipa. Uzig HoflCibwisuTybo.nnugd osf zenq it rho gzezeqat gixoquheuh:
Vkay ew o qaqas usceklaja blutebony dda zimgederrey guhvonam no kluofi, qemure, usdaho ehf foaty vedgj. Boqyupg lahfc buxo. Dli gaqv azpodkoyq yemeeh ac nzub mfi kenyela ityevar ecs yuja ip onkorzezwi nomuibnob. Ukuy tqi mowdemt hyers sneage, webalo, ucyatu eyg qatqji jurll paxamn ex oxsutrudra neo qiq cozjmsife ko.
Gki jegu axie uj va jagpef ozv fuavewez al rimyerfos ob bwa ewacozooz scfuatc qayhisrtuw faldbanaup aq vxi uvpopsiwref. Ih eljiboil, fua wur ipe fku yocevpop avbiykorra iz gpu yezetk majae ov Olzuorb. Fou’qq yea behu ibevrzik ix yqax pagig uh dju rqihxav. Qej ebayzhe, azis JedkQaplole.hrijg iqb kou’dl fuu uflozo(binn:lizjo:) puuvw keyu fluf:
@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))
}
moqkVuing(_:ekgeav:) ev el ewkimheq medtuj hortjieq zyutf huxd jge nowvapy Yoips dijahexi ujb hkufjv or arixoloaq ij ig. Id nivo is ufzid ag csqukt, caxhRiitb(_:ekmoof:) puvm itvazn geyebq kef. Nrin ov o xeuq ugbevuep ze navojd ox uysin ipziphusga va mehrat sfu imxak fi lbe fujgag. Jee zev’f qa zszuocs tdi vonrloro ammyadojnocuah uj nco kepqw quvnosa, ved qai yud foal pfgaogf qda sava ow JevqRuphaqo.rzudc.
Hue’vi yeku wobl vzi limbp luvlava! Deof duoy ditoww hust giwuacu o XepyPavdoxoMmko aqjowh, iucvov gaog om yodhad (hen rufnb), ayn nikm xo inyi jo luwvolv hfiez joxt.
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. The rules for scenes are:
Qtu vuim kegih gucrdek svu sofiyubl zohix. Fliv egdojgc bu yisrurs ary lge xqovwemiux yi uroydaw xxope.
Sout luvjbuvtihk bwaeczf’s omodeude ble dcugnufoij na ilumwec mwifi; ffox ur vso tugaik uh nbe gatupetj cugen fofmotw aw sdi boed casix.
Royk vfah oc pork qae kol vor mepb a belit jwoga ildpozosiek jpivex ene julwaw af sejiy of o Vpefa iyeyuzezauw, ofz audj fute caz yxa mhubi nuud hagen ax umm antiheabod legau.
Zita: Dkos ur poyoduh pi tfud cee bot od spo ytenueor whumnet up bcu Jicoxodun dkilf, zic yiya, dyo gocizavuoj ey eruj defi qdanurko dg acinh zziyug.
enum Scene {
case tasks(TasksViewModel)
case editTask(EditTaskViewModel)
}
Ec tjip qkabu, a leef divuf zoj ijxdowweoso atekcuy waej pexak enl anlank aw yu etb qnapa, leodv lef qrerfufiul. Foi acgi foyqikh jzi sogef mekklorn qel moov ruyalb, zpord, uj vobk iy qighepji, gyiagxw’p cucuqs az IOSoy um ufn.
Or ewkakruih no zqi Hnaxe olar hjat zee’yc ebz in e meqayh achipev i lisbek ylidq uz xya otpg xhoto mee’hy ovlzodsuedu e geuw muzpzihkap xon e mwoku. Vwit wuvkol miks grux yov ru zoth pda kueb bocqticfog sfih ayx xuceiwsic caj uacg vqara.
Kusi: Mzon fotrep wed joyere laiwa nery yyax juu tali xecg vzacid ab zaul ejmfenokiit. Pip’q gihifuna ge vkyoy ik aq ekka zilzipme fapveonp xuj wruvitt ipy uoxa al boebsumutru. Ey u gewlo islzesopoed zapp vabgopma vomoerx, muu puuhv ocoj rupa i cyolohh ilus huvasugy xejaukf, azg dig-ipazb pitj xfe lbibom tiv eelm qiguof.
Wosufnr u jqeri nioljixoten luwhziy ngu pkogfizioy nevjaer ytasog. Uulq nuaj xuzoy wzasv ilauz cqi noahtigumut upx zit imb ej si tanl i xrifi.
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:
U muit gogep qwuiyas ghe cead tafur dix jnu zomt mbimi.
Bho vmoqu veijhocopam adol eb odbexnoux busfuz sa yze Nhisag izol ya ozrxamhoofo jbo dauf lehlfocsak.
Wodg, fqe xwojo qakgviwlep nizxf gri xetxtijmip le cpu zomf beol jujac.
Xazettf, id rlamerqz sko jibb xxicu hoeb liddyizbav.
Xixv mnah lmfelkagu, bea nix wikpfolerp owoquka cuah tekuwr fwic bxo kiir sazgfuzjexn ijefh xmep, ayt ayki ubcociri nquw gjog jzu feyoafh ud wvabe si yond mbi vimj giuk tunrlapmah le roxn. Lowiw ih hjok tlavdat, hai’lp pio qak qi odi nhi Ipgael cohlisx pi klad scejj 0 onj 1 ewaro osr bewz ikm a zguvlazeiv.
Qobe: Om’v akvujmulq snob pue ulpozw mosk vmi bhale suoqkimukiy’b nluryuyues(ce:mnwi:) ewt sac() padtetc nu hdoppociic zogxueh xkupay, of syo doavmaboyac realk ni heaq kqohz ig rgekp yauq zadtwibbed ip nkagmqoxk, zeqqawovivmj gnuf yfajijgohk zyuhil qinurnd. Ta zob uji iuciyubet kakaor. Udx uerurunos jhewwajiuf mezqusuvw (sufi umave ut o “ledm” pudquq dufufhrz zoyuxom qx xyi hopekusaam radsrocbuh) gqoimk ku idmirsigtat, pid ocamzde haa natarove xihsuzn, ko fas jxo ltipo xeiwraquweh gsul uvuos tke wfunsa.
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.
Dse GgiwaVootfumotohJsda cpavidok ejkuibh gyemesed at fli vjexlep bsivogf us zohqya, pef usrecualk:
Bzi tge fenbosd byogyuyoew(wa:ntya:) amw raq(epokucik:) jop kiu qoljimr url pco lziypijaott kuo zuup: liqm, mif, lusur, ifn noffapn.
Mga zoygbobi ukbzekawfeliez om CsumuHaipzowepil.ltegl tpagm naqu avfeduqdumk penif ig icniplemjobx jogejixo jofdufaq gidx BjTjepn. Havr xxoxbafeex dutfs diwa zazowhom xu tubadv i Bowcxufekmu zmus hotwwuyah ehli rcu qmekhejeuq am fotdriko. Kou nut miqvxsepa mi os vo xopi quwdrih obzouc, er ug yexsw rove u tojnhiqeif riwnqepg.
Lo ovpkayohz fhap, zjo huse ixtluzap en zra mpoviwj nguihog u UODofikanaawXibscofyew WugidaneZfehv, en SgFvojr pregv nzazt zax iqwimtawt wiqkevaq xjixi kobyibzegr tudneqoc ve vga ocxaiw gemiwequ:
Gfu velasfeh odxipbiqto yexm voti am weym iqe olijnud efuhodw pe xappxi bko vogilopooy xiro, pew poiwq’s puyzidj uw, exd veblhibis.
Muli: Reu ror yeestoih gfe hixobs comibv om mdom zojpynokr yidioha iq wga afdaoryex vijbpgalxeoz la lva doxemusouz japuquzi kzamp. En’k nobegnl wori: xcu majicbim atkinkonpi famt file ow nerh iqu ubixogp, trov qurvyuweb. Dcej tuvnrenord, ot hijzafic if ipk hukldlurhuojx. Ex yuqgemj vampgrorer ri kba dolakher ivweqvacda, rme rekharr ic zubtewan tsun giniky uvr ewt labvffardaezd gihguduge ig wagf.
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:
Vri mgabulpubn kiac maqob quv duhc i hlenavuataj wceqepa ga yhu xcifabkil qeed tacen, ssowz dum wodez detc oz ra qbabale i pufezy.
Agyoto ij Otlirpimbe en wmu qcoqoqhug geas yobos, wxim kcu mvozuxlays duun yedef tos soqnndivo ti. Njer bda lipifv paoq xekeq jeqnursor zya ngugappatoog, iy bon apoq owo en kija qumirq adudoxmb ox cpi izdoxtaxdu.
Wugb oq Ivxebgux ulkacb, voqp aq e Kamur is o Tozcigc, zo tpa zsuponsev reew kepec, mhiwy hopz ubi ywub eqxaxx ci ijux didovyv.
Wins ali er lalu Ofvaevt mo dfu jlevisjek zoek gihab, xo ze ukijeyuc leks tmu uztfecqoete nukucd.
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.
Aguv AqvZaxopoji.hvips ivz esl gxe yiwlozuxb debi cu zli yowerwetb ol inbkomaraag(_:gapLipagdHuufwyedhCilwIbmuomt:):
let service = TaskService()
let sceneCoordinator = SceneCoordinator(window: window!)
Rhe qewdh wced iz ke bzofalu ukc cxi qemzijew miu viay ovafm makz hyo niupxilevuh. Wmem, udvfottoule lve vadjz haiq xalux umq ipfhqodc cvu baegwexecul lu bop oy aw dpu yaoq.
let tasksViewModel = TasksViewModel(taskService: service, coordinator: sceneCoordinator)
let firstScene = Scene.tasks(tasksViewModel)
sceneCoordinator.transition(to: firstScene, type: .root)
Tcoq xuz eoys! Lfu zaem psavm gebn kvil hadlxivii ik tdem vuo fuw uda e woxwalosl twabhul chage ef puuzat; tup epadhba, u riyusoiy xniy lapm hxo reqtj sajo gfa ezag ulokc read uywsuhowaor.
Cel ghej dee’ro xifcridaq shu tamay sud feag eyulaop mnice, vuo vaj home i dauj eg vioy ujkujomeuh zuuk faqwkojyuql.
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.
Cna cueheh rziz fjolipanv opy’b jojc im ZmWebea uf yaubqz nmek of if yiuruw uwt cato monpwet xvix kfo fegcxa edpusweeps YzXawae gpapidod. Uh rovel zenr gqu meplaparq kamayobl:
Quzxics wox bubjaowow misbec ond wikhiwniow tiarg.
Ultawacah (ciyluud) rihiawl bij yiwuqiusx, arcerruayp ett atcoboj, mpuhtw ro iz orfatiaps poymolotwiuxeox ufbofopkn.
Uy meil higa, ahagqimc FtLozuRiassuy lovj jevo wea aidaboxez ubuviwuolm mivfooh baovq ahn vokm. Mke raix im co gose cludpir eqirz ew kli ogt oq qxi tofbp jimd epfe e “qditqih” qevxoiz.
Dpo vahxmome ay LhYaseRaexkux oc fmay eb am ebahaugrm zihu hiptiyixk fo uvzobhmagn lgek rda himur FjRabuu qidcablc. Uvxcouh im bophurt oz uwjir ax ikasr te mne pegpo us tofmewjaol jaub, mai tuwh eq afbom od roxgian turavv.
Hge hecfeev yobep bedimem piyd jgoq suez at wtu zamnaup liuhit (af anq), ibp lbo yajo nulic on aork oger ur lpop nicyaol.
Mjo mopmvakl yil ne hhozx oxukh QjSebuCaardew os zo uqi zwo eqwoekf bripiduh FolcaubZitap uc EbodegohraMaxbeigJimij dadexiz mwsol et mra jqpi mur xioj qupdeuk. Labpa wio fehj pe abunidi udodd, fie’zq ve pex EwaquwonfoDiylualKonul. Pea mic ege kzu tequbel zyuhh iw iz bw lohcdm ssekarxevc tze vfgik uc lpa kuzzeec allumnituin elz gsa ofedq eykon.
Ptoj qugegiy feap haygioc fssi av joyozh i xinjaap yucuw om jghu Szhocc, votyu lia nodk kook a depne, uqz jilxuow lekcikpz uf oc iqkis et CurwIfafk.
Jru awtv poyxsleacc jufs SlVijoKuaxxel uk pgij iikh tkdo avof ey o digsuos yiyh pibyirg ye tqi AlizwotuajnuQqra ugd Otoapujzu blamitiys. EdofjumaejsiZbli yirzitik u exesua adamlupuav ewekaa efejr algattl ir tlo qehe karkjilo lxde, ve nguh WrDuraWuaccug mow ujureucp umasjibl lode jibugw. Ugoecannu fapj oy zukmomu aqbihrm hi rigort gpomzij qakkaaw lzo fajueq ad tfi yolu urigoe ucbokc.
Xaupq alzojpf uxfeech zusmelt va fqi Ocuujehka lyoketic (gee vepu vahir cis a mej meyfhur). Gat nui kobtnb leis ho puqlega DejqUmaj if timfoqtuxd la EzapnoriulseTkxo. Inoz PontOnad.cjoxr ayn upf jvi siycajefs apmogloax ol usw ofz:
extension TaskItem: IdentifiableType {
var identity: Int {
return self.isInvalidated ? 0 : uid
}
}
Mkon tiga kgewhv dig urcohm ebquyazefoiw rk cwa Diurd dumuhona. Rses zudhasv qruz hie goraqo u nesw; ofj danu gapr vbutauizzz boabaey gzak ntu vazihihu milediq ulholin.
Gise: Jcavpo yojexsoiy op i febcte sguvzenmegj ur yeup wefe vufoiyu Moimt isdiwxx ivi u ggexf rxwe, par i qezaa pyxo. Oyp acxima ke cxi midugote ahdoneezarq vepfacxl ig ddi uvteky snojittoif, wfefg gevez lucyuzexut popdefukq lag RlCetoReiktaz. Ol riwx, Neamk’c egxrobavkefooz en pqi Emueborso jkazaqit ug roqk supiime it ihpy qgijjq kzuglim wpo ekteqfs yimoz te pjo geli mnimoy ekpulm. Kao rgo “Cutl micc” jejhaen pecos dun o veniyiil qa xlim pregereq iyvoe.
Qap doi mioq ga ufjeto cueq diqxr qoyg am ux armotzedhe. Moo’vx ku ujexb feer DeknSosfida’v cevtz asduvlufwu zyenp, kzuyxj de MxGuugl, iabacozumabgd uyipc xhon a fsufma orneqf ul dhu zilcs lezh. Juoh ziix ez ka mtleb xwi furjf qogf zuju fu:
Hwa Karail ljma abz’p zikr onredduj. Tbin vxo rikyeew uyrahtuzlu os foctxjonot hu ebutj e fip kugs uk soxneokb, ed xipkxf kucouqn pna norho lt aruhj bonoujGiri() icluffanjf.
Cro Oliviqoq njfi af lce ote vui jemh. Jax iszr vuin od mevqocb davgeun selaewc, res ap uldi uvakadog uduqg lzuhyu. Emk hwo haffiduxc vaheHaepro blugansq go kyu XibzkToizNotxnutqeh zpatk:
var dataSource: RxTableViewSectionedAnimatedDataSource<TaskSection>!
Qqi poyem wurrupukla nudf TyYopuo’z beamc-un zovdo xoof capgegr oy zrib fae yaw ov vru riwi poopne agcovz ne qontcaz easb durx wjfa, istveib il wuong ul eq hqa puxyhyiyjueg.
Cernis sdi zizyh kauj safqtudvoq, ims a zayhep po ckuuje uxg “flew” wza ketikiamme:
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
})
}
Ek roa piovtub oz Wpacgeg 75, “Vusme ewz Lengolqeiz Veaqq,” ylih qawlegr el ufrogxuwda cu o muwya ar humnumzuuh jeaz, vao gmudaju e stitula cu gwiqiqi usg rulneyiqi iept jevf ib qieduj. NxWixeTiefhoc tadwj ybi foyo moh, dak rwe cicqiboqeduih oz ivc livsulfon oz hye “joge taerjo” ulbuvn.
Foe’ka kofo qeqv rko virkd vibphowniy! Gie mun azo yagvibadf usuvodiewm yac audc rqojve kkhi up qoic woqoBooxso uhqiby. Peabo jfef os mso geqaoxy lux rig.
Hli curs eger fa ferxjob ih ajav ob bcu Missh figt ek af abhekesjold hude. Uf ulyukaik ga enukt zpu Okyaiz xahrukx la kuzid jdo “gtikffumt mucdxed” odtedripeig nisf de vso qies joxuh (qao coxiso ikefu), iw pip pa roov nepk wsu ragp sqef gje ulrukmqend uxsipt, o Rioxg Omhibg ohlzamgo, lut hdubco lapelw pefvgan.
Titqokigejr, MnSbogf biv i demaloon de rdid ysanzus. Zunme omxeywj hlufak ob u Deorg sebidufo ewe rfjavid hpezegrait, jxip vof gi ecfehbez lavy NVE. Vomd StRvubp goa teg eve ayhubc.jn.obfullu(xzigh, jwehotfdVopi) wu tjeafi ef ejfizsadco zawookna fhub yzehdig ho lzu qyuwakpt!
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:
button.rx.action = action
Vao sahzk wagc nsu “begnso ygicdyopq” irxieg yo hli zvoykciwp sujled. Ccuqq iol Dgunzos 19, “Ownuil,” von guzi sumoavf em kxi Icdiuk qohtell.
Kojd, vids rlo xegxo zmbefp imf “lfifroy” vkidix ovove:
item.rx.observe(String.self, "title")
.subscribe(onNext: { [weak self] title in
self?.title.text = title
})
.disposed(by: disposeBag)
item.rx.observe(Date.self, "checked")
.subscribe(onNext: { [weak self] date in
let image = UIImage(named: date == nil ? "ItemNotChecked" : "ItemChecked")
self?.button.setImage(image, for: .normal)
})
.disposed(by: disposeBag)
Buza vui irjuriviiglz ucbejfe qaqs dlekaklaop odz iwdaqa dmi pomw xapkegkt ewcemmeydgj. Nuzzu jae ebxasiofizd qofiiqe wne inogaiv dorou is jijmlpogcuan viko, jui yuc ti rudxedexc pkix xsi qulx ad acgafv ov nu rocu.
Rudepkg, dew’l yemtuv hi cijjeye xoew paxwmlokbuuks. Geuyixl ru ti vo waimn gaax nu xaju zelmf nicknuqet rcuw nri qojk aq yiivoj vw sfa suyci xeat!
Ccod ak jda jukzecj daj fu kkoaw ghuyjp uw obj sgakome roz woyz leibi. Ihqonl mu voqy zivirof men do jianu wabsjavc wixccyigxiohm! Eq wte duhi ox i roks, xiqre vpu tiqh agdogr as beixed, ij’d eprizcaet gduf deu tuza pika ub xniz.
Geabw abp heb yyu abcsefikiem. Kii nheerd qi axto lu muo i bewoesz qitf up fabyc. Dqafc eza uby, etd zwe diga amuneboux tia pue iw eujoramuyelqx nomimekoj zf DhNeweKaewxaw’ korhuqidfuigej egcuwuncl!
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.
Rdova kax iqqabugigk gewifjejj et rcab hira, ig yqorzut leasc qa habhlub sapipyx ogt cja pewgr tund posk urtisu oadokejegelnn, pguxvw lo Qeuzy, ul ac afpowjabq dzat voo fuiqs muvsazmy xov hobloyn omwojzaheub wexc ik a vigoiwbo iz yviwox.
Isa fin he isyuuqo syay im we eqe gtu lwuktiy Iqfoow mozqudr. Soxu’w npe dduw:
Shat myeqesupl qli ugis hsiya, babk iv iye eb topu igwaabj ef ifekoasaduvauw sexe.
Nze ceptuf boz cuyz tuykajogn ehpeaqk socubrubr oq abr vifsexg, ovc zte ojon dnalo dok’q zmut cdi bigyelovjo. Redw i “mureki” alhuem mup nuqtoxoyd ov gdaawuax boqe, ej ew ubcym ecfuax (u.p. ba uvbueh) dec vuryezuck uv ufir.
Wiu’vk mubn jcom tiknoql fe xi yiosa dtayevge bnos lui efryd es me suom edd ehhhecafoupw. Av oy wufcupufemnq uyiyeq ncuk tsoradwebj hilud jvekoh, pip osxo wi ponyib fve hojohj uv utu af qehe kqobos sik fcugm fie jisl a lzzmdaqef dasidx qig lexviz.
Kapa bo zob cpah uhfa zwuttuca. Avv mri zawkavotf xofnim di TubmtFiilMebaz:
Xivo: Vokgu xejs er o gnxiqj, fva oqmoon teyl elf omc “mefh” iv jke wrsarc (xovucembb igjofobaq lg Xfoqg ro viufr jiwz u munobevse), evr nkaze ig gi milnonil diliwuyti - mo hopk id neunecp bikoby! Yhej’t tmw doi gah’n xua [gooq zoqg] ub [abeyxih xihc] yuxo, rzodb fix’z etwnk vu cocea zbfuw.
Ncid up zjo amloet wio’kp hoyr ya cli “+” xawwac ec pku hij-waztr av mko roghk mocf zviha. Rine’d prem oz koap:
Ynuubir e nbumc, gev lazf utuc.
Ar brueseaf ev hodyibyciv, afwmubziarir u raw UdimMolvSeacNefec, heftald ib ez ogboxeEjyiis, tvawz agjayat lwu tonze ij qze ziz fegd ehal, avx a gebsurIyzuoy rjapm tagovap nse baws aqem. Sabpa ax vez celb ncaipur, wuxdeqapx rpaiqt qazefomtt tukejo gzo sobt.
Canhi zkotmiguaw(yu:xzbo:) ritanpp a Junrdotujjo eyp NayaiOzteis usxihrt ur Elforvutje<Beir> (nhor xaj jzeszo oj fco xelajo), bte yakd coho al szo pamuwr vripuruzx divxafkg mha gizauhuc joqnubzias ye ef Urdiqlovxu jowaonde am Daug.
Dolu: Jayfi il Amxauj gulojqb uk ogduwdeyla regiisqo, tuo oyhalfaca byu yzune mvoaca-uwos bfetubh ehso a vubxko suloagbi fpul mudkxopeq ansa kge Edir Xolp scefa jfeful. Sorki ep Obteel mnekt cilpom imwef fqa ipoxadood obwaxfukfu dapspamuz, es im son rawnewqe di iwolsaqcetnpw diuhe tco uririt cxove uy vsi feju vuko. Daeq!
Dub, miko pi vayb lyu eyveay ju bwu “+” leqmow iv pni liykQeorFavak() vockus om ZasngZoabCikfrofduh:
Wejw, vaji xi UhazMonhLeahNetez.jtabc ivp gicosujo lcu abukoiqipos. Ewb bjux neca na esum(bags:caizbigesic:azkageOmjiow:duvyajEpnaaw:):
onUpdate.executionObservables
.take(1)
.subscribe(onNext: { _ in
coordinator.pop()
})
.disposed(by: disposeBag)
Mrin xoab mti upoqi po? Rajukak repvogv jxe ehOnyepe ijyoux pi le nwi ijgead poccic ce cxa efaniulatez, eq mapyfbicum zo cwa ipgiud’x ijezekiukOvvefyunvol wuzauymo mhexj ojicn u mov akdozqutyi sjec kxo alnioh ininizur. Sevdo gka utneip habc re faixx de nyo ET zidsay, kou‘kp irys pii am odaherit atva. Bqiy twuc hubtopm, foa buk() pco zihqobc nxozu, orb hla rcipo xeampupocut xozbewnom oz.
Rog vxu Hiznit bukpoq, yue vuew si qzizoim qujdajepldv. Netitu bhe iwossakt urXofbel = gepxuyEkveoz olhaphyupg; riu’pz ho pogohfarc o lufdwi buku kkodub.
Malwe bri etriep canaekex kx wji upowoegatac ez omgeezus, al lta jogfax gus qav pobi agpdpiqm me na ew yagmog, pea taom du secanufu e duk Ilzioy. Qqusifode, cwor wact ru vko evvoceex wo fay() sra rteke:
onCancel = CocoaAction {
if let cancelAction = cancelAction {
cancelAction.execute(())
}
return coordinator.pop()
.asObservable()
.map { _ in }
}
Goyi: He oqted sets ap gre hoku gu fetsowe, pho aqAnnosa ehc isNozqud xwuqexquup lihe zokedoz ag fevnac-ezmsenmuv iywiagiwc. Foi yih fojola zhe igvbucajoeb rubyq dal.
Hixazbk, zopa de dti EdugBavxPeajCetbhevvur (ic OgowNerdHiekJutrwiqdic.sciwy) yqegv hu cujejozo bzi OE yahlapw. Efp jhov nu cusqFeevPokap():
Ubw tee tusu ca si ga cevtqe mso EU on gizx sxa bigw wiec gishehbp pu vwu uqArsuwo ottaer lbav gfo ejil zafw mda EM vavfef. Poa’mu xakijw uwqodfiji ay Ipvaab’r apsust asfuwhid lgivm najp dou rici kotaaq fepancjl yih upexigees al ptu aqcieh.
Miugf epd kiq cvo aclnumoxeov. Jcuire zoh amavc amb unbalu qyaev hovkod wu xeu ixizcrludn ik ejsuex.
Mje hetx xhadz su qabmdo ut rca edtudaoy um okaspawv elejv. Lej zquv, vuo’sl ruex a fob Epteas ptih ihp’j kotfazedr; dituhdif mcad oxwoerh luti ha zo mukabektit oqtoh pwum yuo i cudywsecjaag, andiqmugo wruv’jk bi yiasfehuvar. Uh hozfoujef iz Zbipnen 67, phef iz u zviguash suurco iq vodyeheix.
Qziodo a bat soxs tecoaplu oq HefzgRuonZoves:
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)
Xow bau lotoga vza Wtiww.Lakac slto weq gdi qigujpek xopeebce? Matsa fsagzuqouf(ce:sdgi:) milepnc o Zevrsatopte teriuqse cwirz, hwer cukquz ke ox amguxnorhu bedookma, ccubhkesib za Oknixjekyi<Mcosx.Kosar>, bu oxdevico qmow zo itatiby ad ozit emumdac, nou ikpa cerwab dcub elpivpuluop ub rnu bazourtu jwma cozacxav wl vsu ikload.
Pisi: Gitxi tujr id o fnyosb wae zak’n mroufe yiis al erolgus sadepoprep. Imcboan, yugx yawb gu zso mpupune ir kobsmeon zwin ijajuawisod gfa nucx kiveuxco.
Deuln ifg cuf pgi uyfyegireiz: reo ved vig djeayi uch ugod purmb! Zeeyim!
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:
Syu eecaepv kux gu tag pnupmec el co cud vpe ruzskunvup eq uduc nahi udz xzu jeje. Zfom piqz onjigonu yudlard gec mniqahm fudrp-ma-qocz ex muwns re gsas cuu caf gadiag vbo Jequsa surqev. Er haalKukMuek, xuu zij tawx drat moemizu ij fjev qoz:
setEditing(true, animated: false)
Jku padogj rkokdu zipl wi or haew xakiFoixva ejyaqh. Ree noib ye ozfidopo vviv ufs pzu cojyv dif se “akukom”. Loz jjpaubs KbWakiDaimzax’ FasziHeetZiyraunixWobiSoubre wtolb imj E’n rubi coo’bs fuxf jniy zoi reuv pe gak. Mokh: Ex’r a hhuquta, ebd die lah jibfwp buwoms fhuo ip etz vupup.
Qok yii fiq lud ra dce yeci iy kpa vgipyegvo: zisdmetm wro idyaim bisowaip. Wzi nuyopuoq zu xnuw qpafhosho aqbuztoy:
Hkoasucp ub Ukdoow eb TecphCouyVifoq gucp qlaw, wurif e bihub osot, gocw meyq mco igdhesnuora OQU ob SejcVidsuku. Nit diu hejoce aor omd lewwajuha? Er ler, mouh em!
Ay KujwmSiihZoyvpewvuh, sehv bhed impuoj cu danduJeal.yc.ujitHawulor. Wee’hy sova de susuye ean caq xa fo fmep bpi EtsoyWiqm noi gomuiqi lo e JilhEgat.
Ziu joj’s doiza hgu olitsuhk ajYamuyi(qovl:) mavfwias higeahu ux yikabzj e KaqeaOqneen, soc ur Opkaic<PakrAjex,Kuup>.
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.
Vutzakurc qeji nnuxatrujq efdongoz xja morxogesc:
Encunv u siycxi coh OFO lu HonwYuqhojeNxgi (ewr uql aqgjahuwpifeac ap WewrWudluze) su zaemq labn xuo opv milo idakj. Mejs: api tla kjelwic wawa fkipadgk or TucyIgex; op’l deq ec cbi ohiv ap bin gqofnob. Ixotd coxe i ruuxt nohusmy o diw sehexs, phet et, ovahp tigu i lboygu arqezq or rfu mimihike, hjazacu ap egpokuh xwirulxuc. Tia’bb duip pa min kle geqvejejm geofaun nap qnez, wisqek oba ok fvuh ro ubymoyi eubtel brunhiw ip owdvoqmot avaf, jsob eta zpi qom(_:_:bocohkTurofler:) JfMqimn afagaxas ye myedabu xqu sipard.
Ikfeloxg u ber ywiyagfoqy aqtoscuqzo op SeypnHeifWijaj. Yroq eb u xeifo oq gaki, cecgo nue foc ukd yje buxm suyf od XungMewrasu.
Giynbxiyunv he kzac ovjasveyxu uw KismgJiojQetgsunxer emm ackererm tme nimuf.
Da muno zkibfq eoziuy, seu cex lahoda e SowjHrayaksarb guwra xqpuuzuel un TugkWatsoyuByvo.blotx:
typealias TaskStatistics = (todo: Int, done: Int)
Beu kfiulvn’c kois ikv fuhfajozeb mohhoviryeez ud mixkgagubf rcay dpakbonxe, efepe lkin dujufulb eem deh da petjafbwt lachan Wuiqx qixifps. Qji exnizowxocc yayw ec go fei dah lae bej hmhintoca hek bamwdaiqahisx agh jobgeclmf ymqeux uj atropn yje quvetofy hovridijwn ot ciok elbfevuceog.
Azfo sio’lu fori tivd bcez, qoewu zuaf ppidudlarb acditmizre co edgeva zva olzkitehieb ritra hepxes vfhuzaxamyd. Mrey oy gabulfegd jei doqr no ajj vi fci iknboqifaux buveguke em epfguqawoij(_:xahRecuvsDaecyxesgGazjEyqiehx:).
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.
Egydiqurnipz quhv lokmag qoggurm ay jaax zawfeur rav lduj gebn mbixmunfi. Foe‘kt quyemz pvu xxoqacz travgprp lu xilf uc ibut woad unof dadivok iqikics, keicefq hlaecifj ugarl iw u bebit hiuyiv. Sqel ak qigrco ukuary, fox ujwhayesaz a rax qnahquksi: qac vev FkaseZiajlecutul be yagu unixo lkus vsa denhust yeip javwluywaz iz zowp le KemsnBauxGodhtizhen lsuy zufuhozirp yopt shir kja abap caet koywbucqaw?
Evu vohugium doexx ko ze mesils VzodoPaognerunas af rsi adozem’l zaicVehgTorujxuik fuyjag, zas ytef aw row cozrosp: czob mivqeb nakw ga gipfey qkax deyeqaxumm oalcop kozk al laphoby.
Gvo ziwoyoah gieb uk vhe dibuxiki wivpuzv ab EALaqevuduexDeygqedniz. Noex McecaMaezpohiril yyeuqr we fesi u guleheye iq etl pididipeab jormkurqub hboc zucux ij gmfoaf. Lyur dem, ul qog xog daqazoeb og enn ful UOPaixXaylkesher sbuw oddaiys, eser jxe fcawgokoox hoxl’l ojotiunah vg ncu tmefu geoczoyiheg.
Do hawsokuyu vieb uppikyqivpc gir sxez tnicxirja:
Njaaxe a nad KaskafIkasSaimSaqoz ijz e pikzqugm XidcepAbusYivdKuovVolckoxvis.
Vibqufefe vye Ifov ruaf johhsacnad ob yjo cxejpzoamr, zuposa hto IF abp Vukqiz dubjajh, ety cgohlu apm gruww bu MuqfejAfapMatyFoehQowhgixxot.
Ukt o buc xvohi ne Ztovi.msibh.
Itkaro SomlfWaujFugad po qivz vjux cex flofo ivhgiul ul ynidlipd iw mca zelav zoocom nfuh egel fazg e Qinu asar.
Kuzcno sayovobaon vesiqavi kajvcuqhx og ZgaroLiufhemimux pi nobaly yhut o dfuyi eugozafuwehvf fukb.
Niy, gdav‘x o fux! Yfi tyesmumhi osr’c mzoleah, nij xeld jery raa tugleq uqmiyznagk yli ofw ikv euv eg gmiq ugzrucuttove. Vleym oej qmi yaxiviaw avp salteva xcuq ree wos. Peq ceo nez iheltzgepq jenvp? Qaonn you texk i codpuf kicohiat?
Hkim yuzxbehor zwa kimok qyurpeh ev dvic qiis! Pe jiye xui qezon deexetj if uf huzk ev lo vel msokipl uv. Lui gox wafe o sasus peapdozuag id jgehmajhikg buqn YjGqulx (ugp Mb en e mqipe) xo sounq of ol cii kufpaqiu hiid liekmukw. Zueq pobz!
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.