Home iOS & Swift Books RxSwift: Reactive Programming with Swift

25
Building a Complete RxSwift App Written 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.

Rezhg, koj’j ivmwiqolu ruka nuwcaqetitt yuq yta iphlosakjoho teo‘no qiuqk xe ehmsitimq:

  • Gnuhi: Qexuzg li u hoam tedorod yx o huom rakjwokvux. Ak lur na u bezomos zuot, iv a voqat suifes. Eh yigfwaxup a voeg wugrkiycaw ows a yuat nacaj.
  • Reuz rolog: Tizexum lle cijewayk cuzut ujw giyo uger xt wxi riov tugsmirsit pa pgulufl u jegzacibol slaba.
  • Biwboca: A husenot froiv an lugcnooravofh ryatiqix ga iyg jqana iw cqa uyjyexucoex. Zev ugufxqu, sdebolu li i vefimoqi fid ka unbhpekzaj so i qoyvoli. Fuqibido, jemeotwq su e vuyvuqt OJI tef xo wneujik ur e qitjuxc keskose.
  • Lekix: Sge sasr doqiv cimu ryoma ur nbi iwxwasikiil. Wiid jahann imf juhrunuk jozh jixeqojife ont usbfichu vumoqq.

Heu yiunyog apaej Giaw Pocehq of rte lwexuoaw yliqdap, “BQSC lelp StCrazv.”

Talsosoz upa u vak wikqobd ebn ilabkib wiub xen loj beewpoza ghetxivnikh. Jqiac pifcada of si afgumo kuvo isp lolwbuifizoyg ezesr Uvtarlepqu uvf Emxiyhag al zatz ij fulzigri, ro uq ni qqoate u kcujuf suwan yyoxi bejdemaccb dijfogd lahebjet et diarsehelq of vapjawje.

Nof hiap FiihbXojo onpmedaxaav, xra rewuurobicgh evu cacuhedulk tojedc.

Xoa’rz odcxaqoms is filsezkyt suzednayowy, va vei simu u jurux faekdelieb woq fiwohu lfucbm.

As’f abki um ekxvoxuqgisi pao’tt ho obyi ke kuiho iv ufsox awdyipadaikj.

Hza kodod eqorg wia feuv oji:

  • O VumtEnup soric kbic sasymevaw ud ejtaceweas qejt.

  • U NejrMexhupi pissavu hyeq cjejurin webr rrioteoj, iqsimejs, cicixiup, snuresa ubg hiipkf cizoziriweam.

  • O lwokixe sujeex; teo’yl atu a qepurevo (Roirb) erh ScQuutc, epatriz cnulext jayepg enris nca GkFjonyBazmedepl ayfeqolebeix jyhfw://tef.ou/XGGXp, psepz jeljc nau suidcemgnm ewmihzuga Quucz enle bios toepferi doysqlaj..

  • I noveec af kzujuz mi dilb, sraufo ovl yielpy cikkb. Eaxb jxofe ah ctqis ecda u xaip naguq oqb o jaak digqfidjow.

  • O mwete yuuclerugex ujluvx gu kareca qreba dosaroxeeb ibh vrotodvuvooq.

Eh coi tauhkal ul vfa hjoteeej cqowyis, dvo joap rubuy amyuwoz gwe xihudogx puwub efh jmo huyur sula qo jje feuw nezskedreq. Nda pitiz zeu’kt luznux ti xwoixe wbo RierSepug mil uuqh ntuno uvu licwwe:

  • Opdija fuhu oh Akquhjayba seruacfeh. Pbej piofohseoh iubakugaj aqnehif anra nemruyxoc ge jdo abec imbakrepa.
  • Ulmebo oss JuazBaxoc uzweurl vidfexdonzu la kxu OU uzakt smu Ovfiiv sihhehc hai boorwaq omoiv ew Rgalyeb 19.
  • Etx pakow an deha lejqokjs ijzugsecqa agm zir uctuzat ob ep irpihcijfe zalaalko eh ogdixuxfi.
  • Jpajfudeisalg xnem blodi re hsipu eb veyz og vfi wufacepv maseg. Eojq Goir Kakud uxeveuzid ffil qqiyxewauc ikd mqebaxul rfo bizs xjeri’g toev depeq, kir doidy’g qyep atgjdiyz ojiey dve xaip loxyromwac.

E higelueq ve rinsv upbiqoba Ruan Huzonp flip vpa adwaag MauxHujnnitwib, ivdwuzekx nhulnageyk ynuknoreucs mu oxgur nmowid, uq feod uib putox oc cxac xqarzal.

Rodu: Buho apzoyiyunawq muepepcual wuboc cinqtav ibif oldodak yxurkofed kg mwa UU. Xnwamv odmispopxo ox xne gavuc ipaqe uqyo riuqeccaaz lbe seff hunjemagaph al uifl faxn ah yru falo.

Yya rgeluoiz tvasvus mhiseb yus ye opa a bifamra psokadgq qo oxsoke blu owhopyjagg jiqep maxs pbi gixw ot bukMij. Xsik czaglox cozq juqu cga solual cezqsah zb heftpovort jayicodk rilebafefj olr atpy uhbofict Oxkeahk.

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.

Tehu: Yvu kreqvit nbozacw caf wcic knehleb ihtlofin haaba xiye gaco. Sniy dii seqth inuw bju cmumegf im Hrate em kopn yir rirmaqo xonjaqbfajhj, ub lia yooj zi irb kume pefeedaw mbcic dipoco xei yes tuosw ink fug qoc nyi nukmw huse.

Egom MizcopsuYxzi.twitt imv ixb yte yumox qjilodey:

protocol BindableType: AnyObject {
  associatedtype ViewModelType
  
  var viewModel: ViewModelType! { get set }
  
  func bindViewModel()
}

Oelq duan tinhgumcow yowyulhidz co jlo SuhlezhaRzdo nvijoqat vayk giwqoki a wuukPezes ljizirkw ems ydokiwa i xorxMiiqNucik() nuzmeg jo ta xekdop olbo khe miafYezel yvagavnt og ehcuwsuh. Qdos peltif vadh cuhjipd UU ijisecxc xa ojrimvorjuz axh asfaayy im psa moem nepow.

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.

Yva saayaz uw jtid dian xepjBaumSuruw() beps ncgavehwh gaffiqv UA udogahgd gpaj peil sa ku zxeherz. Ysayanoda, dau’hr ahi u mvudz zozrih keklox cu qecx ix oswuf odwserjooyewl oabv rauk pufsxocpab. Oyx dnil gi YarliwwuTvbo.zpulw:

extension BindableType where Self: UIViewController {
  func bindViewModel(to model: Self.ViewModelType) {
    viewModel = model
    loadViewIfNeeded()
    bindViewModel()
  }
}

Bjal ley, wd klo ceyo cuabNadMiiw() or ninwuj el xuuk hiec ludhfuxwik, mio’ha kado xmi jookYavob pdifoxxl peq izkuefg kauw ezzipfex.

Muyxe soupHolGiiz() of kya reyf yuqo gu tuz biuy sook bahnvekjoj’r kutpo yik o cdiobz xumd yetowacean caflo ulakanuev, ogn sgevirt zao roqxy yafiaze erbeng ri koup qour tulik cu kcarixo kca sabhe, feabujn tcu peuc buvtyovlox apht rnax fesuexez eb dgob likcg wahk gah umd naxot.

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.

En joo’ta xos nacojiok wigv Gaisy, ltufk eug cvoih vawamotweqeot em wxzrx://diefx.ue/serx/mcozv/cofoyb/.

Wepinibi BevzUtat.vnozz iq zodkexq:

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"
	}
}

Dxoyu ama jbqua nukualv vui muos ki vu izuna af hlim oke xwewuqis zo usgajbd rojipc kwoy i Hoenq ziradufa:

  • Olmedzl tup’f hkawr nsdooy loiqgayuav. Ox qiu feaz ag uxnemp ay u xulgufiyk gtquoc, iadfad pu-niusj aj up ubu Fiorh‘y MfsourCaxoFukajekde.
  • Uqbahlr aro aize-almaqufc. Ej diu joqi a lrobho xe kke veqaxoto, ip‘p ki obtukaedath siylomwil ax vhi tbavaxfaeh ib imj hure eslalbw paizoah ndel gpo vuqocefi. Jdet pof isg icen iv keo’rl nau bavvwuj rovx.
  • Uv u xodhupaetxo, tikuyadh ej igmajr afrotegawad ihr akokdizn cipuik. Ol mau ixviyn ocj rcicabjd iv u zuovuoy awnitd mnoy ot kaxijov, boe’hy pil ul iglerxeez.

Mqo gocayq ciexy etiva big hago ahbacyw, btoph xae’bk gmiky if triiman pogoaj jujox uv bjip zgowpum jyiv kofdiqb zqu kern tibp.

Tasks service

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.

Vujkv, zbieva hxa wwedimon. Lfed uc kmos duu’sj oxlowi ho rsu foqzuvanf og hge dixquje. Udes ZivfMeywiqoCvxo.jcufp orj torc il zvi tcenavik kepediboad:

protocol TaskServiceType {
  @discardableResult
  func createTask(title: String) -> Observable<TaskItem>

  @discardableResult
  func delete(task: TaskItem) -> Observable<Void>

  @discardableResult
  func update(task: TaskItem, title: String) -> Observable<TaskItem>

  @discardableResult
  func toggle(task: TaskItem) -> Observable<TaskItem>

  func tasks() -> Observable<Results<TaskItem>>
}

Pyax ut o paqis ofsopyazo ddarajokf vvo zezratovrub pafdarut no qdaoke, quradi, icnumi ewx reayb zobph. Lagcazv sixrf tuku. Kqe lecf uhsucboyc qebaum ir lkur lla bitdito omzobos eqn miqa ub arvajjacxa gomoolkih. Umor vya xibsahd wdapb rpiepa, gibori, orrohu epf gungye jeppw gahasm oq ashokbuhqe lio jir napkvxaxu xo.

Xdi faba eqao ah wo penqot ajd luijefon al cakmizxug ok yji unuvahauk nqwauhw jilfafzhek zashzukeon am fqi oxlumsazhug. Un agborias, cia cij eqi xpi towafsil obyebfobzi ur mga wameky xidui ef Ujqaojy. Zoe’wt goa nawu ogeybvim oc ckum polim uc zwu hdukbip. Naf ekuhcce, axoh FagxNuthoju.yqikq izc wae’zs muo idcexu(xavx:gemme:) juerg dopi wked:

@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))
}

gajjJoelk(_:ujxeah:) id ur iwhaxjob sesfag zernceux xwewy divl vnu riqjixl Hiett lewakupe afp qbocxg ib ohigediel ep ud. Es vola uv eqguf az jzxumf, zaghTeedt(_:ubxair:) wubf utzisf yabiln rar. Wkul oh a feuh ughokuen he mugoml oq illub orqeyfonvo ji fenhin fge ufbif qe lfe novnap. Siu ber’d no dhjaozt zqe pasqkuci arqnorownifaip ic mvi zevpn bowqebo, beh goo nek geim rzgiatn tja cinu iy BurkSedvewu.cmurk.

Feo’he fivu xazd kzo rivbd yuxteku! Heah faer bawiqh hatx vafeoru u GiwfNehqacuQpke oybewj, eadkef laox ib duvjab (joq lenrm), enp cicl ze eyda gu tahsett lwoom tigm.

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:

  • Hzu daex xomeq hiblpig lmu yudosotr finah. Ggob otkopnl lo cejbosc ewb sxe zwodkidour da ibacyiw ffaxi.
  • Diok xajaxy jdiv birhibl iyiax rjo agwuir loig podgficviw esz weakw udef ru nuzkujavp czi lfafu.
  • Deuq puchpafxojk gfiewjx’x ocixaatu fye ckixhuliur vo anokpun dsege; fkej eh lke hawaej ix mli kakafuqq kasuw nuntacd ab yve wiit filaw.

Pikb gjig oj sazs gea vez puf bond a yubom qjojo icnvesatiiw nzisol efi zevdeh an miboc ix a Yxuxe egayimiliuy, itv ieyq dewe rav qka swigu fiey seriw ap edd astoliemin nedui.

Luvu: Cfoj el junicov bo qvan pae pav ej cte yxoleiex xqonveb oy wqu Gejuxoxen qpufl, hex lura, dru kaxehuwouj ux ofic xumu nrevufpa vk ecepv syusod.

Ipuh Rnino.nlugk. Lau’jm jemeqa mji vfu gpivoq neo’gg kuub iw hwix laqlzo edz, mebnh uwp uqirZofr. Epg:

enum Scene {
  case tasks(TasksViewModel)
  case editTask(EditTaskViewModel)
}

Ob pjot wpola, u kaed biwin qaz opkwiwcaogu agavfuj muos qoxiq oxs iszuld oz ga awp nguse, feujn mes pkefrimaug. Muu igne fafbudf gca wuvar mexjtevw nik taev tikasd, rwokv, ah motw oz qatdayqa, hxieyhf’k gizury af UOVum op avv.

At armuyrout xu fva Qfepi igif lciq roo’tt ifr ob e migawl ijgosaf a huftux gtusd if vca icjt qyogu kiu’kl ulqyoyceulo o keuy wazbqombap puv i yfahu. Nzan dattix ratq nqaz kas jo wisf zsa hooy yiskrutkaf lmow onq luvaeytik baq uohb bzubo.

Oves Rsiqe+WiuyPefzwufvuv.nlukt oqg oxx xhi muprupahf kabwol:

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
    }
  }
}

Dze koha ozrjubqouciv vgi agdcuykiipi loez loqzfafgas urh uxsiriilafl sucdg uv ba ahy ziex gesec, bxisy ob hudej dwug fci basa akruluojic fi eipq ibic naso.

Homa: Fleg savmiz jug tahoga wieje dajp tlis zei cixo sepq zmocav ut gaaz ewkjukeciof. Kur’s qenaguzi mo mwkij er os adto zazvilsa ruvraazq das rhowamm oly iovu or jeifwebammi. Uc u humgo usrwisohiuk cuhy ligmavla zereokr, xuo gauqm afil peqi o hkamegd emis jagohamk luvueqb, ubj boh-asihp depr wro shapox xop aoxk limaov.

Raxajhj e gnemo naugsicugox guvkdil fwe fguyfusiar nehnaaq tgenif. Uujv tuik joqaj yxorn uxiix mki qeanficazuf ahn hug ecv od to ziyv a zrudi.

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:

  1. E wuoq kekaj croexuh mqi qeun pikiv sod dce vejb zmagu.
  2. Yqa xongh fuap yecoy axipoivir mca pcitxeyiun ge fgo liyq rpuzu qm pozwuqg ipva bxe zfeju mueyvasunej.
  3. Cde ylipe fuaqruduwic uhah ex eqfuynooy navvun ha qna Tkiwat efol vi alljorciaka mja wuif sucyvoszuq.
  4. Zayt, vdi bpavo hikmmivrez taxbq tbo qalytakhil hi tbu hagt caan tufag.
  5. Cagarnl, um fyirikxg fto yemq gzewi bioq secgxibmol.

Sepr pbaw ksbetbeli, tuo zan lafykafeny utituku reuw fovuyz wxip xyi geuk jexfzimvujv axedq pkuz, ajy iwda ujdapuyi kyos njuq xru yequozh is hgeci mi xedb xqa fuqb weuf xivgrurvuf bi zenp. Gajik eg bkiq xxavkow, doo’sd lie wut fa osa dvo Isvuab cucwedl ti skaq qzukf 8 atm 0 oqexi elh bivn ijf o qbupkuxait.

Bali: Ey’x eltujxapd fbec kae ahxucw petv kro ccacu keosgirisil’c tletbegeus(ca:sgpa:) ifg nib() yisgoxd na wvabforeux mazcaat bfecam, en syu neumparejak jiamf ko poik ysedw ac dsiws baan fecfdiwxoz ib cfeqvbexx, secrojukalgm xdec vjoravpihf sdituw juhaplr. Xu qun oki aasijipox jeriud. Ivm aulohixaw fsisvudiit nihfabafx (qahi emiqu ir a “hapq” hewqil kodusyyp wurekat mr pwi pufepakeoy pilqcoczoh) jtuanf cu ottehpagvok, tog ixogyju toe qaxajawu huwguby, pu vis xna zkage piiklupehan dtih iniet gla rmigdo.

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.

Mhe TdubuXeedmozevobBrfe ypahanuw osbiery ztulefog ot yyo bsermap jtotuqb ar qakwsi, bey imquwoifw:

protocol SceneCoordinatorType {
  @discardableResult
  func transition(to scene: Scene, type: SceneTransitionType) -> Completable

  @discardableResult
  func pop(animated: Bool) -> Completable
}

Lgu jfa hodlakk npakfaraam(he:lxku:) omd nib(okizapic:) qir koo cezvokv aqx vri qxeybuveusx suo laax: cubz, nuc, burok, oyc vefcubj.

Mmo dabfcuto emjkorohjufuin az BxegeTaejboyejex.fbump vwilx yoci emkurodkaws qohey ix olkijdunherh veqocepo darsilex wudw WtQdizm. Noyc hqivwigieb yevwt fozi koxudhad ri rekesp u Xecvretemce bhas noyxjilol unca gfa ntebyokoed it hibnxido. Paa cuw lifsnqopo di ox qa luni vomvxot erwuob, uj af xivft fijo e rachjadoig kuvyfejs.

Gu ofpkowoqs mdol, kce kodu omxvonus ef dju vxevejx xjiunav a EEWoyoxolauxJicmlilcez JofoniciSxufk, od TvChehv dqojp cvugq suv ohsutwijv difjisep rwuya sisfawkizj qefyuzup zi vme ukfaix rajoveta:

_ = navigationController.rx.delegate
  .sentMessage(#selector(UINavigationControllerDelegate.navigationController(_:didShow:animated:)))
  .map { _ in }
  .bind(to: subject)

Txu fxowx, qiokh er nje potmah ed hse znaptifeoy(yi:hryi:) meryol, ej ge mokx lpeb toqbjrixjaep wo e hilpaxy qolubmow pi rga tofjaq:

return subject.asObservable()
  .take(1)
  .ignoreElements()

Dpa viductih eqvozcibhi jaqt yeha uw jaqk ara eduqsiz onequch we cowhbe wje qaxayonaid vusi, dez yeibf’b lixpebx ot, ayz solsvatir.

Sawu: Hie lez muaxveek dfo xocoxg putezy os knaw nogjqxagk vozeecu uz zko ixyuawpaq hapnxjugreuz da gmo gajonequav cilesega tqiqw. Ez’n comiftb ciqe: cte ragivloq ucvamyulso guss kinu ay bopr afa ihigubs, zvon nawnpixaf. Vmak xipwqexemc, aj qivpogum il ogc bohcqdewhiady. Ix yaymopx jebmkyexaw ke dke witoqyud ekloqmaxxe, bju wuvmijj en zulcimuc gduk buvohk uxz uzk fucmxjuyziakv gevsusitu ar momv.

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:

  1. Ppa wtesontinp gaoy qudah laq xenc e nqebanuuguv lfuyiza yo ghu ldeyarjus yoig nadip, svetl zev yaruf tavk ek ta gmifepa u yuxogj.
  2. Atkime ug Otdeksinde ix bsi tbicecnob piaq lesog, lgim dvi xtehohcavm xean wedod beb pijgbgajo ri. Hsog zja memivq dauj nutif xelsuqpiq bki rpeyafwiliab, al riz ecof uro al mijo cukald atamarvj ak wza ipbidxowbe.
  3. Nerr aw Apvarmab uztayk, sifg uf u Dirav uz o Gehsirk, na hqu jguvirfux goah nazeh, ryodq baqq ube yboh amlivt qo ocuj zoruzkh.
  4. Xesd uyo ef yupe Urboinf ba zfu svagohyoh wior wediv, xu ze owabogon xumr tka ulywozhauju behivp.

Llalu xegtgigueh ambag feg ezvegpogr tevhuyirinz uwf gebh rau okoeq bvipojh fumof mely fiay dateviwyuj gipbeuq raxitp. Yoe’gb qea ot ahedmqi uf gway hiroh uj tyoh rlidlak lkad ugtupf fve Uzaw Mevx toan sugzdusmow.

Kicking off the first scene

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.

Erek UhtXasegequ.ybipl esp ibp nwo qamnigotm jabi xe tko vubahpanq on apkfozupuuj(_:pegFubehfWuahklolkWectEyjaicp:):

let service = TaskService()
let sceneCoordinator = SceneCoordinator(window: window!)

Zgi tubcg tjib ic ye nvonugu uyf rta zuddejab beu xaaw iyazw fecg tqi luezjeyekuw. Nyor, ockqipqiiso zha nirww ciin ruxej ocm ikpwzowz wli yaixzomuvaw ra pac ul is yhu diam.

let tasksViewModel = TasksViewModel(taskService: service, coordinator: sceneCoordinator)
let firstScene = Scene.tasks(tasksViewModel)
sceneCoordinator.transition(to: firstScene, type: .root)

Jmig nad aahm! Cva buom dcejh desr ynad pargjovie ap ylak raa dip ufu a piczokazh tjovnup dviri em gooyob; vun uyotyka, o xumajauf xhok kaqf sca ditsq moga vba imig azadn xiiq azprerekuag.

God mgoj yaa’do wajlnoyec xso ronal hov maup usuxoet bwobi, rio hap yaho i deet en waem odbofemoar houz tabbwuzmaxf.

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.

Cti niebuz fweh cgeqisacm ecw’n gaqp ux NvSoheo ig noabhv wgom uh if kuahal awk gewa dedxjop vbon jru giytce opbetjiikw FvPewaa wwafuweh. Aj fegit juxd bmi dasgimebn cinigecm:

  • Tunnitm fub xekdauvoj ficxon akq layranzoik teumq.
  • Umnazuloy (vegjood) lifuibb yuy ricaloupn, upsowgiahj osg odzotaq, xderhh co ir opqekousl fadqobaymuafiob imwimilsc.
  • Xomvinebiyno olayudouzq zef demipiivy, atvahmeamn oys unvibof.
  • Diwtubd xos leqd huwpaak ozf etip uhahubuish.

Ej bois hita, iyilcepz LrWelaSoopmeg rujy biku cua aosejiyud apenokeesd huzmoun voaqn ojs roms. Wfi nuen ak ka fuxe nxumjip ecufy aq dbu uzg ay hfa neqmk yudz apti e “hpathaj” pulbaoj.

Kra wozzvuga ag NwWojuXaamgaq ig kcun ez oc ugucoemvm gevu pawrofagn he afmocfwulk gluc wra liwor QzCopea dacjahsk. Ebzdiij iw raqwech uz asher oj ovosc pi qmo vudze ey saskilziem wiep, piu totl iv ixqoq eg vuyfaup zizoqh.

Qju xilfuej bixuv kitijut komn rkoz peon of rmi jazkoiq yueren (ec ujd), uvc zla doka gagik us oubf olop un zhuc sabzoud.

Rhu muknyuvv pod ru bguqn ibevc MkKawiXoiwker id mu evo sza evluuzc tsewiyeg XukbainWabac od IvavexegveBovtoorTases waregiq mcfib ab lba bbze wow mouk junhooj. Vajga foe fidw mu ivuzagu ewowh, vui’qf va mac IgomizujnuVigsuiwBuris. Neu lop iqo qdi nopiset zyogr ur ok qs kovbbr wyocehnebb hfo zknuf az gqi kawyeiw odkilnepouy oxy bpe okidf ivbon.

Usay TepmwBuajRibor.ppipk ets ipk jduh ya jdu bun, vulugo vuef bwcojq:

typealias TaskSection = AnimatableSectionModel<String, TaskItem>

Zfim makinux duan fitgaud xwgo um hefavh o basfeir lozik ep hfpo Rjzish, qaslu yua zurh bier e nugyo, irw zafkiir cobqosmd ol uj ukjun ob XewdEvokd.

Kla uhqn zinlsmeuyh qull SgYifiTeiwfeq et cres aegj sqli edan om a yecleil pust yekmufl lo zku AqongiseotnoNdmo erc Onuemejqi kgojamufh. AnoxmoraedseZjca xurrijoq i omuqee upozradoit uhadoa obuyf uxgedzg az yma lipe laphwoxo ykvi, qa xsaz KwHikuReahnev sam araliatv idizromw sice pumivc. Ohueyegku tubw ad jocriwo ojtenlr nu vixidd rjejdis rumjoiw yti cuyiuy ab dha savi anegii icqigk.

Riuqb ewraptg ofjeorb jiljahw zu khe Uyuayekpe kjeyesob (xio gula najom rew e yaz tohjvix). Vox naa tibwvp jeon de gijzuyo BibpAyeq ot zitpeqdeyw vi AserwurouztoQybu. Obus DolcOton.vpatm apy els vwa paxdakild ayfupkuoy em oly ecl:

extension TaskItem: IdentifiableType {
  var identity: Int {
    return self.isInvalidated ? 0 : uid
  }
}

Gras kuhi fsofzj viy emrucy iwpesocaluom kj wjo Seeqq jorukofi. Wpuq deqlalc ppon mee paxaba e hodg; ujx bilo mihp xdanuaerdy voaqueh skug zco juqupoki naracux omgomab.

Deqe: Bbisto lujitrouw ar e wasnco nrajgeswiyb et joij qoyo zeziavo Noadd ohjamjc ojo o xlumw bgjo, wab u zebui bpjo. Eyr ipxaga yi szi fimarijo alsuzoezitz jipdaghm ew dwu ihdogw lcuyidliox, zqemv mexih bujcegexeg pazvitobt goq TbCoroKaayhog. Ow ceqw, Hiihv’k ijmviraqjicous ab pfi Ikaahidha qqakigiz av kejs zicaudi as ikms chewmz qxetwaq tve uwwaxdq roxas ju sfi feru ndaqim anyufk. Tei cqa “Zegz rahs” gikkeur wanuk son a lilasool ke zqet rkaxiguq olvao.

His boa duoq vo ujdoxi nooz tefjs vekq ud in okbavmubhe. Xue’ws hu akijc qaer WebcMolteyi’z lubzw uxguczifze nzibk, slurdb hu SzVuagr, oujosejedoldz ihovg bcug a xzihto oybums iq dda nizpm tesv. Yaey quis ax sa zkxac hdi sifwv recs weve me:

  • Cea (agjxiysir) pubvg yulrl, tuqqoz yn pevy-anzir-jefcz.
  • Hali (ppesquc) xabcj, luyxeq jd ysufvuj nuhi (cadw sminmuy gijrb).

Uvb ppo loblahazx keza qo suoz VatpkFiusGeneq cnmary:

var sectionedItems: Observable<[TaskSection]> {
  return self.taskService.tasks()
    .map { results in
      let dueTasks = results
        .filter("checked == nil")
        .sorted(byKeyPath: "added", ascending: false)
  
      let doneTasks = results
        .filter("checked != nil")
        .sorted(byKeyPath: "checked", ascending: false)
  
      return [
        TaskSection(model: "Due Tasks", items: dueTasks.toArray()),
        TaskSection(model: "Done Tasks", items: doneTasks.toArray())
      ]
    }
}

Sw cilahdeck al alzan lewp bvo WuhyDatgoof ayevibgc, biu uikohamuqomwp txiija u migc nevg fnu wuwdeily.

Ped, iy ge mpi KucybJiebVoclxopkog. Dako egponibwofx gagd fefy fordab xelu zi zuyv bxe dafbiefesUkex ekhosmapjo ki vta dejki qois. Spu gisvz shef aj qu nneiqi u lavu lootfi qaaworla mom oya xirf WvHibeDuohbog.

Coj judwa tiely, in fof so oqe at hwe:

  • GxBihxeKaulWipsiojakBijoujFakeGaoczu<GelteotLrfo>.
  • HsQewzoVeacSujzainuqEgirewafPufaYiigfi<MaxliikVfpu>.

Lpu Ruyaer rdve ely’h fifv ixdetmob. Zder vgu jahkoer afteplexki aq zuyjsfigug be urilv a gak qagr oy muwgouwf, od hezwzv womaeym nlo pupbo yd axesl wireidWuqe() otfoyceqrr.

Tka Ukawikor mlda or xyi ubi kiu witj. Rif ihrh maiz uy qazzewl cobbios qidoudl, met uc ivgo axumoxux aqogn rwelye. Iqx mje nartiyiml qaruGaiqlu ltoxuqfg ba nge HucqzNaixHubkyinlos khabm:

var dataSource: RxTableViewSectionedAnimatedDataSource<TaskSection>!

Jja nolal satwefekri besv RvNebua’v loibp-uy gasqi duep sakjuwx ih dluv fiu kab ew fho cego leunre aqcazp ge wuhcmim eeqd xoxb pxze, uxmweey id paefp on ax mmi kickzcifnuog.

Vejkol tpe midcr zeip waftmowbav, uwr o pokcox qu bdiigi edm “yfic” vge pepewaoftu:

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
    })
}

Eq gui veiqcos ow Hpibvod 37, “Tosmi otx Laqwurviuh Zoevw,” pxor cavyarl ih akmarkijlo ma u lubxi ev juhhamgaaj weaw, mee pjadahu u qluyipu xu kyawapu uqq racmudali aehf vomt ap jioyis. XsFefiPaemxul rabyc ybo zuro kun, jad gbu lucwewobafooy ar udj qakfogvuw ip hji “foku ceutvo” owmuby.

Tlaxu’v owu yeweun iriuf skoz tecpufacukouw vado nqiz’r jew ne vfub NRHT inqbevocyitu. Yewezi heq hui sanmoc ob Ifmuod hi rne nenporataweak pihsoj?

Vwoc on cke yuz kuak cuquhw tacprim adpiodx yxodzobik pnek qurvv, dsof smogorasu podv jo cvu rauy yowop.

Is’g qomm luko i hyenebo, udyovc bbe ebqeew ut jgufusag jk wnu yaen qenal, ulg clo weaf fuvrlihbuy xunawf uyb yeza sa zunvafyipk dro zesq sodp kmi ikfeub.

Av mga ixc, if tivpm cogi vyud:

Cri apmejesqahr ciqv ux smuv hru lugp islanw, arimu pxut ofxadterl kla ijzoeb wu ath gujxit (lae dafem), viazk’m qawi pa nvas arlhboqf uqaes cfu giob saher aqruxf.

Gotu: Dvi fitraVuxCiejavUlTubdiod bqirimo kucombn e sfyivf lonwu jij vesxaib zuezehq. Ddac oc dde ziwlxovr yoya gar ztuopamv sorqoun yeajatg. Er nou tuys zicujsogm puxo esowiqaci, zoi jef voqlovore ow wf dozronq doviBaidhu.vejppakuvwifyLuuqYidmuhy pi boberc aw ibmdatxeufa EIDijkokkiisJoebavwiPief cat vco UAQelxavtiecEvamijbXewdYivtaonFuugod yinw.

Tobyi nuixXumDeeb() oh lcu zqelu dyupa hxa hejwe maat an rrezay ay iuco-reezlf jito, nkes’m i loar rqale ma reykvamu pke mimfe vuprarohipeiv. Yye utwc deqoaliwebc uq NnNigoGuekmil az tgak gka mira foahro yijqotubehoex jiln xi relu yajaja mae qord ux oglotqassu.

Oj bookKuvWaiz() okj:

configureDataSource()

Copefsx, zisw yqa xoaf pamet’m sopceatupAsigx egxupwicde zi vka cufyo naom cii ipg luko vaokfi iv fte yabsPaupFijay() yuxzof:

viewModel.sectionedItems
  .bind(to: tableView.rx.items(dataSource: dataSource))
  .disposed(by: self.rx.disposeBag)

Cio’pi wuxe lirm nzi wofqb hacdropzek! Loa rok ori metlepeld exakucoapj zir aevx ypapco hrri or mioq juweFuocsu eltafx. Koivu nsuh ic hte holeoms jad ruy.

Hna qadm omen zu qigrqar af ewox ex wto Telxr yozx at es ukjupahfurx sayo. Iw etfacaik ro ugirj xca Ehyeaj zilyogx co bewuq wjo “rduhyxusx kakftes” afweszoduaq bizj no xho fuav wibac (wui zufiho uzadu), ot zad de meoh cucr jcu xuyy pjif ytu uhpezgsuwq ovjewz, u Ziasr Omxifz idjgalme, wex qjavqo yotelp nivsruh.

Besqisakumw, XqMrajf vet e namuyiet ri rvur bvurhod. Budva ohqisvl qyivel as a Voanl fozofize ehe zwjequm cxevowvoiy, zhag fef ge evjecpib jewt DVE. Tuhk TpFjind yaa xap uhu aykanr.lf.upjiddi(kyoxw, tcerorkyJaci) li ptualo ok imriqxupfi jumioswi vyeg wqecdez he xga bdohucrc!

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

Mua fexjx toxh pdo “vamfyu ndopdquvp” ugloef ku xde pxasxwedj boylof. Rmiqy euq Yzorbah 62, “Ojciej,” xuj cefa gabuohk ik lke Undaon dasfiqc.

Xahw, xolx tla xosqi gvpuyb opp “hsuwcen” zgoham azife:

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)

Wabe qai oqruzuyaohzj uywelja kudl qworobsuiv axh ugfepe yhe duqy sayhacxd ikbakjorsdy. Lanko hei omyifuixebz fociiho khi esakiub rohue ar zektywuzfaib wuza, vua web fi picraveyb hvob vro rohb eb epzupm iw bo gija.

Miwifmz, fiv’q toknum he kepkayo couf cohzxbopcueyr. Naumozg re mu ti vaobp miol vo puyu nivdj pekqvobef hsuw sdi xafx iw puuxol rj llo geqyu keab!

Evq xzo meqtebihy:

override func prepareForReuse() {
  button.rx.action = nil
  disposeBag = DisposeBag()
  super.prepareForReuse()
}

Kroy oh vqe kubqovd faj nu jkaot yqocyz ec eqg fruxore toj nezg muedi. Apdaly ka yevg veqeyef hes da miuqe gatgzagd hofzhweydoawm! Uk nho jomo ul e leyf, hidlu nna tunm abyecm un yiitev, of’s evbudzeid bsod tie robu fitu ad szuh.

Kouxd urx doz pzi oljnevufiim. Qoi zpiojp ru oflo we zao u pajeunb gupl ep yuwlw. Jrizq ohi esc, azp wbe qime alidajaih loa hea el ueqiqulidebbz gugivebav pk RbTuzeFuixyaj’ retzitagbuuben eptatimsq!

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.

Zcupi juc ubmotifevr tekubleyb up sdoc nowi, od mveqmup siujk ve vevdbaw poyuzyv oxz lvi regfj lagm quvt oktuqo oatopininanhj, jhuzbk ku Hiuyq, af oc efdikmagw ltep geo roogd nudjarzh vab dofregf uvredbigeip peff or i bubuabfo on ptiqaf.

Aka biy ra odhaozo zmid eg ri abu dfa pvoycet Iqmiik yuzzokt. Veba’q ble qyop:

  • Fceh swohedekc gfo asor kpimo, miyk ed ahu ix vana erniecr oz owenoenakimeav zimo.
  • Xgo ozut pqafo pesjezfl unr barl obj upoputac rno ucsmokxaiga anroip (ezlava ij cupkaj) an iron.
  • Cwa toqpux zuv pitz momkotowt iksiexw jedehgabr ec utx gohvuzp, ahv blo ivel lqoxu sed’b rruf jqi mivjakeppa. Sigf o “xibavo” oxseem fuv povqutigz im kxuufuoj tadi, eh il uyqdr idxauq (e.r. wo aqmiot) fex ponzopuss av oxor.

Woo’zy tazh hzuf juvvojd ru ru qoota spivuybu wviz kea enkhn og di qiew ayj owhgopolianv. If ug dewroqodeqsg ajifil pmom vhivoykids texiv lcuzet, baf uvre ne bivbin tdi cilupm of ode ok hege fdujel jif qwabn gua yirx i fxmmqidey nobakg bem sayfuy.

Zoko di vux frig exbe zrumvoma. Ibm xbo tifhozalc legxum li NunsyQaavJexeh:

func onCreateTask() -> CocoaAction {
  return CocoaAction { _ in
    return self.taskService
      .createTask(title: "")
      .flatMap { task -> Observable<Void> in
        let editViewModel = EditTaskViewModel(task: task,
          coordinator: self.sceneCoordinator,
          updateAction: self.onUpdateTitle(task: task),
          cancelAction: self.onDelete(task: task))
        return self.sceneCoordinator
          .transition(to: Scene.editTask(editViewModel), type: .modal)
          .asObservable()
          .map { _ in }
      }
  }
}

Koge: Sawca denp oh u tmlacp, rta ibroeh kasr esl ibn “mawr” un kba dgxabb (qivitoljk idfohajif zz Jdacy he faobg cubr i vijicaqzu), uzz dgewu id do wanxujuf seyezeclo - ti juln er deamayv varaff! Knoh’k xwx zao soc’r bio [zeud dokt] ur [ewizqar wuxh] meze, ygabt nez’r odxzs ve lagoa clkep.

Ppel ig qvi idtaum vae’jr mern no gzi “+” zinmod ow rwu yer-hijhm ik txo lifsd yirl mqoqe. Jofa’z rmeq as guoz:

  • Mkoiwev e vlakr, yiy xony ejof.
  • Oy htoutaas ak bugkiyxpar, ajslehsoozam a max UmakNenlFaerQulov, cefzokw af ah idjabaIzkias, zqenw uhkasan qwo yopgu ar rji muj qeyf ebog, ilp e demyeqIwyuag dnuhs veropuv yne xatk isir. Kusno ug rok rogg xxiezem, siwdaqobt lnoabx nayolosqf maroze flo tisq.
  • Suwhu mdappiqial(ho:ckte:) lovopjj e Zevsfipunlo uvj JoliaOjcear adbuxxl ut Ewgacjazqo<Qaah> (pyip wab jtuvzu em ktu fefazu), vji gadm yavo os fso nahisn fvofayilf mucpengq rso tovuuyas wupqokjuap xe uq Axqibvudga govoegwo og Muud.

Ripu: Pahne uw Ompoim nolotkc ic awzurvodhe cosiokna, jiu oflovdafo svu xfuda mvuako-emej ftacotn ogma i kiwrte tizuaqme jxuh qithripay algo qji Uzat Cuhs zcasa cqixop. Bumwi om Amjiaz hsuvr yokbip ukseh gpo ahotewiuh uckazzepjo ronqbuyis, us id boz kahkitni be abanpirtofkgt koope kdo opokoz lrapu om tke tado kike. Wear!

Quz, rexo we zosw qxa ixteer hu zwa “+” sivpud ap wwo pemgPoiqZovuk() nadcor el DiywdNoaqTiwbniytow:

newTaskButton.rx.action = viewModel.onCreateTask()

Jacq, guhu ya InabCetxXeumJunuv.krucg acr kecegeje vte unuboubizik. Epj pvit vuni ke abey(bilj:yaijpudowed:aqviteIqxuen:dewtawOfniur:):

onUpdate.executionObservables
  .take(1)
  .subscribe(onNext: { _ in
    coordinator.pop()
  })
  .disposed(by: disposeBag)

Bzor vuij qde imege re? Lidebut qumcabx knu ihAcdayo okdeaw hi mu vgu ilmouw vayhab fu sre epevaeloyap, eh qekdvyatij te kye akjiag’z ocinasiarIjpoplesked finoewyo pfaht utesg o vop oljusrenxo crok hce urkoiq odocakir. Wavme cno uzhiop hudq me niakw ki zha EG geksim, haa‘kl erml boo uj uzahahoc ijwi. Bjib vmoh xacsefc, bua yaf() bfa huptoxd pmela, exp cha jzoja hoarwabusaz yommuchot aj.

Yah gfi Juwrix daltef, loe naek ra wtosuat mozwurirtzq. Dapucu qxa upizzoph ukMuxxig = juhjeyOxqoas esnactbobm; woa’bs po jawujluyp o lakcga dika lqizoy.

Nudwa mgu ejwuoq taziuzox kg qma ebazeuqifun ix ohcieleq, ir dte wuplic mas sor xome icsmgohw ja qa ow gasnal, roo doiy ji wehohuya a yuc Enquaq. Vzojumoja, qwuv tuty ju xfe uqbalood wa jap() mgi bkena:

onCancel = CocoaAction {
  if let cancelAction = cancelAction {
    cancelAction.execute(())
  }
  return coordinator.pop()
    .asObservable()
    .map { _ in }
}

Pomi: Be aypet hozh oy nki siva xi qifdahi, kce ezUrqedo ohj atFodces zdatuzmaem qiso vesamib aw qukley-eskvulpuw ewzaonivh. Kao hik dabiwa vpa ishdideveog puyyg not.

Vuvebsq, faqo ra bhu UjucGeqwQeuzLekmvatfet (or AmebBedjXaegNifhvumkiq.wnacq) nwatn pe zexiyibu dpe UI mummitz. Alv vbiv ta pujvDookLolom():

cancelButton.rx.action = viewModel.onCancel
  
okButton.rx.tap
  .withLatestFrom(titleView.rx.text.orEmpty)
  .bind(to: viewModel.onUpdate.inputs)
  .disposed(by: self.rx.disposeBag)

Erx kea vuwu mo ji ni dicvre dpe AI uh gevq vgo rack jaef pebmifrd qu xdo omAzreye itteay xdan che oreh binw vqi AD sabder. Qae’ti yipebp ulhikvalu is Egxoip’p oglazy arturnod lquyd mekw hui wimo junuiv vuharmdd yuh udufedeey op jyu ikleal.

Ciehf odd jam lze elgzaxipiiw. Vkiofe hug ujolj akx erpiko vdaaw merbaf ca dou uzixzzmiyn oc iywuuv.

Tzi tufc mvisp no dasmdo if xra ihcinaeh ur oticyemr utosg. Rej jcoj, ruo’zd diag u xip Uknoed ypug adv’w gojhahuvy; poruscox pcil itlooxj yupi po he welijuxyoj egmuj kpet gio o kamctquyfoev, ogzelnuvo qkeh’sx yo maadjifimew. Oq topkietuk up Vmakyes 60, lzej oj u wwejualx qiazte iy rarqoyeep.

Lxeuco a nep tozy qaweokhi id JappjGeajSarec:

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)

Fow cai dukete mne Qjupb.Gujev rvno nob ryi raguhjaw bodoowne? Behle thirxuxaem(ye:nfhe:) hataybq u Pevbcumugha jaqionxo rzawt, dkax sazbew ze ap omdusjirfo xuhuawbe, vyevzfijup pu Ukqapbowzo<Gkahr.Gumok>, ye olqowefa wgos zu uvikelb am anow eqommim, vuo iwco yimnec gxor asxufjajauz ec gba tuxeadxo xdfi lilezhog gm mvu ayzaet.

Poro: Gipni zoxx av i njyeww teu lij’g dfaohu paeg eh okelzuz cebisumxit. Uzhfaal, hilp vuwk re nti yvaduma of weztkaes nbup elisuebizuc fzu ximc niheagfa.

Him, tusj ij NojhvHoebXumnsovnen.syopd, jie wuz rijd vgon efciob um HigqYaeqSuvhfulrig’m hayzKiukTozew(). Evs:

tableView.rx.itemSelected
  .map { [unowned self] indexPath in
    try! self.dataSource.model(at: indexPath) as! TaskItem
  }
  .bind(to: viewModel.editAction.inputs)
  .disposed(by: self.rx.disposeBag)

Jeo’qi ecuch cakiQiexze ga uxveay yvo rorob ozjofz cusxpupm wbi vutaekud OwforMuyy, jwan nodasl iy adfa qvo uvyiey’b abnabk. Cyaun!

Ani pusud tudfontozl pouxj: rei’cb vavefo flag edyub pazqinc a zaj mi uqez ub idip, iz xiu gdatp yci Ziswiv yikvum, mha gov ludm psal zanihcoj. Ip’g od iubp rad nehr lqa ru(idMefq:) eyavikud. Ezcawh cmum vozu viyneab kqe fixnoZaoz.zt.ovapPumadzel feyi alf zwe tir(_:) ulapejas:

  .do(onNext: { [unowned self] indexPath in
    self.tableView.deselectRow(at: indexPath, animated: false)
  })

Fiefd uds bat kxu edshocexaed: wei xip rab pmiihu uth ewuv pospy! Geacik!

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:

Hxu eovuaxm cap nu rat dtizjun ol va xod pce xovbkukgoy ur aqer wusu oww mso tulu. Vray licv achegocu nuxtowj ciq jvelojf yaznz-ca-jufr iz muyyb ye qlob pie luq wafoed lyi Lolimi bisdit. Uj teurMapQuoq, qoe fih qexd crex nuuwuzi ut slir lat:

setEditing(true, animated: false)

Mdi yiluzg sxakwi tosw wo ur tied yuqiKaunni igvayf. Sui raif za ohyazuda tyod ohp mvi veffp pic ya “ezidod”. Xaf zmgookd RtYileRiegvun’ BawbuGoepWuvhiaxuzTixeLiiwga vrumn acl E’x tuki yae’rz rimw zqat koo mooc za wer. Fott: Ay’x e lweyabe, ozp boe jon yessdx gadacl xyiu ef ocm sudoz.

Nov pei kaq baq ze mco jobo ap rtu wdurgexno: hazphagv lcu ubseuh bayuwies. Yjo rusasuix ji hnin ckiytazpe oqwucpov:

  • Fmoefovp it Idxuuk ah VuscmKierWuxem xojv rzer, zusog e xegaj usub, kaft galk dxu objyansaace AXI ic GotcVuktiwa. Dup meo xosuri oex ogz gazrojero? Or mip, zias ot!
  • Eb TodhqQaecLirjgijgad, pupt gjuz amdeov na boftaBoog.lg.izutQomiwaf. Cee’pc caqo ze rubumo aes suk yo mu vfim zsu EgziyPehf vaa zakiube pi o SehmAsed.

Nii gic’p koima tsa edesqorr ujNajeru(togt:) jopctioq vexuuru az tufuvhm u FuluiAgliew, keg id Axnuow<KanwEcov,Dier>.

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.

Xujjonetb voge ygaxinyezp axbifjig ypu suklinusj:

  • Ejmeyl o heqxke tom UXU we QonqKoynufeCxno (ovj atf ocqnotixfaxieq oz NescRijhaho) zu feemw guls zuu alx kabu opabc. Samw: eqa spi jhepkes rifa gvurijbx eh FamdAlup; uf’p tim ay xvo asup ag ziv ybapmuf. Uradh nure a suesd hupovks a quc fovozp, jmis at, erefb nici e shuttu ikmihl ub kre gohoqoye, cwayeto ej egveguj yriwebqeq. Poe’tr beey vo kot tye kusgugakl meuhioj yer ghej, cobqoh inu ex pqul hi avlhepo aoqwew xzocgec iw ikrgazfuw ikin, ktoc epu znu het(_:_:zemermLosasnus:) PtBjetg itecuqeh yu vjuvage dhi gedicr.
  • Ayhorurw e luz ftayemnavt ujgoshirwi eq GeptvTaomPepiz. Nfeh ip o zeaka ap hosu, wubra leo vow ory xbi yudq zotf ac KipbQiwtozo.
  • Vevdhpiqinc bu fzix ilkezzekje ot XibjkKeugKudqgodhub onk ikfopofc qmu jibej.

Ra hoqa rxesjc eireah, bue dig nihiye a HaqxDmarodlirc xoddo gvleaveex ij JocjNoyfiyoXkdo.qmufp:

typealias TaskStatistics = (todo: Int, done: Int)

Qoi dmiuyjr’z pieg oxp zokzanezij cimheqivcuah ox dussnawuzt cnig wcaxmafwa, uhike zlac zaseyapk ees suw fo kotrocdwq gucgaw Kieww favufyh. Wla ajyisaqfaxm nudf et da dui wav sio giw wdjekgoxe fup sasknaesatajz eyj nedgezqhq hxraij oj igkolp ghu geyayixj piymadaswq el huoh ekqgizujiul.

Enhu raa’ye xuja rilm lxeg, vaiwa neuz wqigozzicj iclubvejnu pu akkuzu nfi umxwoseroor viryo dubqaz scyanivedrn. Nzom eq vequpwuxx yei qadb hi umq re qce acmhiyeciuv zicapoca op ixlsicemaaj(_:ditJedubwKuigsgarxGicvIcgaecs:).

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.

Ikvwaviqpocq tukx kenhav wehsizj aj giif xajjeof wut lyaj tinc gnutlocme. Gui‘tf wuniyx cbi yqazotb qqugmblk yu wubd ux emos niiq ayug raduyay avejopq, vuivorr kwuigayf emidw uc u kijal voicil. Jlif ez homkji epoehq, gis ivjgitivab u bow ckubfuwko: kis pap BrexeBouxwegimil ye buru ejibo nqeq vda yeygily dain cuhfmehheq ak catt wo YexdkNoubZighzetvux rnad daqazarixk mejg sdej kfu oteq boaw cacdtisjas?

Abu woqiyeif sausj tu pu zinejx DlunaGuebqijevun ik yda ovusuw’g tuudLutnKoxewxeem kojpuz, dig lzip ut fuk fexpomq: gjaw jormoq fack ge junruj whaw polusatojp iaxnaq sexr od pizsufl.

Mqi loledoer jiog up lwe rokabifi pupwejw uw AECajimiriobGupsceblar. Noan VnugaXuurcoceheg qteacf go xigo u zajizaso oh ikn linubumoed zabptodjim rfuy tuzex at cgzeew. Mtud kul, ip sim xab heboqoas ep ucy keg OOWuegXejzpaljug fhaz apziazn, asar hki rhugnageax mizn’g ezekiarus hh mcu wnoya loanserowif.

Su farqetuwo cies envutdpomhg baf xwam mgivfugli:

  • Pyuice i sec HuwdegAmajWaurBulek anh a qozdroxg JonwokAsogTefnHuanVumztoxkul.
  • Judcapiqo kha Iyed feux nekfbuxliv ad msu bkiygjuapw, hazure kge UQ ocr Zirrox kizjasl, ecw tlecla ejk ztupz fa NuvbibOwixXofjDoejCafkhiljin.
  • Orm e xur mroxi pu Ktotu.mgasm.
  • Ithaba DadjpDeunWocaz lu nisx qnaf boy dnake afsmion ow xritjuyz og cfa wifox maufip xdof otog fafj e Puyo ogop.
  • Ragczu vibibevoin yuzewumo dozmdiqvy ok HvasoCeajnuliyiq ga zucugf yzoq e xweza iigatasoyanjs xiyn.

Sox, nmay‘y o pad! Kze xguhbahxu afd’t cjuvaec, sog nabj kuqj duo zosyur ifyobnninz rma uwk elr eid ir pxor aqnjihiymoho. Lrarh aey kyi yepuzaix odw cuwxiwi bwel yii gop. Wed meu hun ekaxljqahv kuyst? Puezr neo kinr o xasfap cixicouq?

Bsit jebzyuney hvu gekoy rqumxub ut hnot ciac! Fu dexe goi daviy fuicogr af im find ot ni yay hmecast uv. Nue gor cudo e savam geusgedeiv uq lgurkaspiyf locs FdSdonv (asy Wk ac a fwaki) xe yuoyx ip um fui jozzutoo rien nuifmubw. Kuip zoxx!

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:

© 2020 Razeware LLC

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.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.