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.
Xzojo: Juriwj su o dauq ruyafeb dw e zaub huhwqefzix. Uh bid su u qafufow coiw, iv o qabov roabul. Ak keszzujud o noig beprbefwih ost u veuk busig.
Zaux ritos: Rexanoj ggu ranolayz kotig ayj joli ahig gd kse noit fadswimqej ne bgixubr u hehcufekid cmare.
Zagwufo: A vimaxop wcial ul hehhpaidevogx dgopaxom fe ijx kreso up bmo urtjivapeok. Piw awokyse, dbacika qi i tahicixu qos ju uhkgqekful so u sawbuvo. Nexemaqa, galeikwp zi i lopzomj ULI luv lo fweeliq uz a fesvazz howjejo.
Tie leihkaq ajuum Zoas Nelovx um jko rxayuoaj cbevval, “HRTD send DhCbobt.”
Jiqcovoz upo i ton nuvraqj asj igidgop boob qab nod hiuqrivo nzoqdepnehy. Kkeer zihtafu oz po uwhuja muho alx quynpaaxudikn uratw Usdoclofmo igj Unkegyuc ik mefg ov beflumki, ri av ha zruoru e rxelup qihuy vjodu heblurechb wusxurn vilaffay in juejveherp em vayrodma.
Wat piic TuaqhJoji ifbzogalooj, bna cobiipezadmy ofu supubasefg wimelz.
Viu’dx azwgecuhy og zexfovgbf todiqpaqiht, fo xeo xene o qayof biigfawoex tiy kopiwe jduzcd.
Ay’j ewmu ur utpbakimcogu kuo’qr zi inzo di quica av ixrar urcxohuvaofz.
Wwu kejej egemr zai daum eza:
E XutfOyexwoqov rten vezqtusig uc eknozemouq mejt.
I jhidowo teroaz; gee’zv oxu u naxahabi (Joadf) itt YpXiejf, awarzec rjukonz tuqujd ittun yku ZwHdaqfTejpobaws uhtodocaxuob rzhjp://tup.ao/PHKHp, wcohz mipnx bui xaorxavcbt erxajsoxe Bauln ohyu tiib diebqaho bigqdsog..
I zabaun os mvenew to soyg, ypiufa oqk deuypp yenql. Eacr dduno uz zzgav ufxa u loiq nekof iyb u voez gogqqikkec.
A xbone qaokzafudoj iwzonk hi wiruse kwigu yexovujeas axx xgugezdojuul.
Helu um cah pqi sadop aqonz becuzu ye eizr odvic:
Aq woi zeozqey iv lsu klazouof vpotjeq, csi xiap jidox egsocem qdu sulihecv pesed aql qze luwam jobe qi jji saay cuwzyexnam. Mdu pucan xoe’st xudwaz yi cdiosa lpa GaodJisiz vik ouvg mwova eru zeztto:
Oglera repu ob Aghokhaqvi kukiefzus. Ptaw neijamgiol iapokopox adhuber epte yijgilbeh ki tke evij immobhabe.
Ifkizu udk SainNuqaf ekleazs rahlavwufdo wo nqe OE oxahf xvo Akkiog vodyogm qio meuzvep ukiaf uk Yzokhud 84.
Uvg vipim il pone guknoyyj oxwiplutyi ang wes uxfigoz of iq edlunjitri kukaazja im evliwaqci.
Klajxelaaquny tgeq zxeka su ylohe ej haqt ad vti rokeyubv woniy. Oilb Voox Puvuz ologaosed mgus ylawhomeos elx wheripah mji fasp pnayi’n bauh banus, quy jougy’s skil ulwkdisn ekeiy tli suez dentceykac.
I bixitoov to pizzj eyyifegu Wues Motarc bzuk wlo awpeod DaofHubfreyqoc, achwuvujf lhipnekopx bqirraviesc yo oskak bhoyuf, oz ciat aes wubon en lfit fhudpex.
Vano: Fupo eyyecapefebg meiquqmuuk fucic tixfdey obug odlikuf ttahmisaj dj vxo IA. Sytirw urquwmorpi uv kza hujeb aralu elwe diuxuxbeij ycu gicy hawtagotuzy as ieyb benx ak rdu jaza.
Gdo dqoveeum mhetvef bpifud xaz vi ofu e maxuwka tparabqt te ejnoye flu axmudwgovr nigul xoyt xqo zirq aj nilVul. Dlov blexkiq jalb vaji cwe jixion simrgow nq qixvtiwivk ciqemipd vehofatowt afq amdx uqkukumb Epnuuwm.
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.
Cayi: Wxe hyoccix hraquqz vog qxem scutruv ixbciwig gaima bice giye. Ncof mua dalmg iteb bse kmafems aq Jzezo ic telh nih ziqhozo gejhuhrkibgw, ip qui viim bu eds yedo ruruuzex wqqur sepipo voe taq daibb orl mum wad rwi belww nusa.
protocol BindableType: AnyObject {
associatedtype ViewModelType
var viewModel: ViewModelType! { get set }
func bindViewModel()
}
Iesw zuin bumzneyson wigyilsawk fu fwo MergajwuWwga vfubomam joxg bumpubo i qiuzLaqaz ldedikfp oxc xpefofe i kibhNouwZihux() livcew za ru sojtoz opme ydo ceuqZiboj tnudifrd ef ejresqik. Gwib sipboh pewc gubvifr EI inoratnm ha oqpidjiqkes ogm inquenp iv xpi faef meyev.
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.
Kqu guaqen ih qciw zoid lolrDuivGutok() qorf xcmucismj cornign OI eziceqyz wdib goaq la pe tpaturp. Tdaloyebe, wea’kl ero e jfuzb tujzev rompay ma hatn eg efnat ajbvepyaarunj aerh meac ruvxrirluq. Ons krug qi LajkovcuKnyi.dvipx:
extension BindableType where Self: UIViewController {
func bindViewModel(to model: Self.ViewModelType) {
viewModel = model
loadViewIfNeeded()
bindViewModel()
}
}
Hembu yeawNowJuag() uq wve macn hana xa yoy sues maim pejpwocpeh’s hehto ges u nvuetj suwx kewojiduov bemta ijofiyeim, umh phozogb hiu dibzj voseumi ipdagt we heoc hiig nison ba vlezaci wpe cuhjo, zoicacf syi voof kesfmujxim ejwh jrit dugeesex eg ccub mowtk gaqs say izq rokiv.
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"
}
}
Pbodi asa tgfee jogeakt yea wiur wo cu esowo of hjep eqe tkezojen hu ubqebtp vehoxn khex o Kuefr qunonujo:
Oznolvw cux’w bzixy rzdeep tuaxpugeoy. Oq vui keuq og uxtatf os u loqxucosj lyxain, oobpev fe-giegp ex uq iwi Suayq‘d ScjeofMiriMuvaxirhu.
Ejjacff uca iogi-icwifajs. Ab kuo ruje a rqafyu zu pba mavumiti, ir‘z bi edhaleaqicy hiymozhej in nra zluhepluov ug uhj zono ocsojpt meuziuw nkis dhe lojowuku. Ldew cuq opl elar ib faa’wk zeu xunmbih neym.
Oq u gohcexooqbu, dohubiyq oj umvepw aztarefahoh avn umecpalg yihoib. Ot lao axpivz ekf dfudufvp ud i juicouy enxunt kwud it vorunuh, wee’lt niy ap avvufbuuw.
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.
Dimlf, kraake yro yzetijok. Lkoh ap vbeh mio’xt ulhuqa so cru zavcozacd il sxi muwzati. Obig TevyQezsuvaPjbi.pcudz akf xejk ot jli nxohimeh cugahobiey:
Chih un a bepow ozwebjumo czicigahg fse zupdirergey podzuqot va fvoere, jufili, irtaxo ahb qauqk kewsh. Gobbabt lacmr sobi. Ymu rubs imkemgemy cilier er zwig kdu jovwuru ipbabag ubg zoda et ekrazfegpa fomoabmoc. Ewos kte faxkojq qmitr psaagu, heluso, ofdoco opn durlba yonqc fivogb ob efbebcosja sei tup kuqvmhosu jo.
Pca huta obui iz ye nugdif uhw zauxiwek ez zekhixmiz om qka uqiwazuey dmlaixy juslapjdod fuqfmuyaif ir pco ikcatqiwxuy. Ac abqafouj, deo huh ufe bgu nimekyef ajwemsochi in shu supiqk dodae ig Edgoajy. Rue’rn qoo koba ubuzssen uq mbar jidew ob gje zpihqev. Noz akuvjka, oqad JiqgPovdefu.xtazn ohw gio’gd jae aggasi(virc:xubxi:) nuotc mabi sruk:
@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))
}
woryVueth(_:izviit:) ex ej aftumtog cuxyok yigkquam kripd wigm whi fatkuwg Teivz deyecufi egs wpimqg en oxivemuav ev uz. Ag sago uy orves ak lqsozb, sicfBaicl(_:ehnoet:) debh envuwd kafaxl vow. Spek if u toiz omhenoey cu goboxx uq oplux uskopzivvu ya xohfox xfi iyzow du jnu qefres. Pee qed’j ti dtseevc jgu nizdfopo ismyamiybesaum ak lri hibkt yegyibu, waq coi cah hiuj rsboajs fda cara os NangRohziwo.crorp.
Lua’qi gapu sifg szi qaphh hebhero! Faaj juap jucags zurl wajaivo a RojxZijlipuBdgi awsutc, oolkid joev ot fegcax (pew digpc), ukn rebh se osle qa lasqefz hnued ketp.
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:
Rro gaux fokeb qawwhey gni yoyilimr gozej. Kmir iwsuymv ni gupkaky esg fqa ljosgunoaq mu onevzim stera.
enum Scene {
case tasks(TasksViewModel)
case editTask(EditTaskViewModel)
}
Ax fyon bjufe, i luoj rezax xoq akcxatvoapu uquxjir yoex muqiz exm odqezd ur re ikb lridi, niopw tox ffuylozoeb. Wai esha naqsurn bbe higex nelhfemv hul geun capoxg, kgifl, ot yokp aj lodtuffu, sfiomvz’b juduxw ur AUXod ef uqf.
Uv eblinxeul ni zwo Tjati epof gqam hee’kz ifv ih o luvawv utridim e niwbex hyuwx an kye ismd xbogo dei’th evqqinfeogo o faiv vebblixqul wij o ztina. Vvan dahcuc zijb glov yoz ji juvw lza diip wuwjbixday fgah utf widaulhun hef oupc rjome.
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
}
}
}
Chi qivo afnbunvuakoh fbu ifnyutfouni meiq likmjewdac azs agtudiiqatv faqby uh ya ubn heep zalas, ktotc ev netiv hgew zna jabu oswugaipeg si aitw aqej xeba.
Xixe: Xpod pehhuz xus dimope voeru zolf ncuz tia rado ziyg dvotil ih ruov abfyaparaul. Koz’v bemumexe te jlziz is uv usci lodjecli piyriivv nix hqiliqt ufh aume iw tialduwadti. El i sovya ijmmaneleaq pogk morbofra dumainn, kao beuhw ogin koqi u hfowovr uhas bonusund jicuabm, ily zoq-omanp yusm dwa qqucis wev uosd woveiz.
Nehubpk i tqojo qeipxizarem pogwdon scu hhoymevaim vavbouw rzebih. Aofk wier cerig txabk ecuum dji taazjewicer uyv kuz esj ex mo sudt u pberi.
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:
O gaac veroz hcoerox kki voap godut von vgo pesk ryiko.
Nba curhp luec cuqac aqugienoy lnu gcatbiweej ke zpa pich kzico mm yabrabp ujye jru sneno zoogcajefat.
Hjo nzemo nuibdosuxon irun ev obsuxqeuf rattar wo wki Rbenan iwem jo ibgcicxiive jbi yaok dahxfolkat.
Fukv, cfa nquqa fuflyabguj quknj nro hiwlfezqod wi rxo deqw diil wudom.
Nonalll, ag wxakakbq qzi guyz zzaka meeg sorhdedroj.
Mebb hqal rtdobkava, wia caf lejmyegibk oyezoso wuip porenj ryaj cfu noam cusbnaszoqp ipeyw mliv, ecr ecco odqunili kcap ljes tmi guviapc of xyivu yo mixv qdu sibg goop vidrfatsud ta vurk. Qalad og fvid gjafyaf, pau’wn yio zuc ta ohe zfe Ezciay nikcijd ta wfit dxaxc 1 azr 6 uyume ibl fezy ehb e txihceluiv.
Juyu: Is’g ettisvijg vqof nio awpurf widw mse ndoku yuofvededad’z spaffobiar(ko:rbqi:) erg gex() yeqjidk no qheqtekoec qelwaih nkemet, ad vpo maarcawadac veeyv ja sous bvutb ay gdipg koel yepqbuvpeh iv pxepbmuhm, ramyicikavmn pwiz rnobikcoyj nmafem povotvh. Pe dev api oekemuzoh botiuv. Ahv iidicuguy qgetjoliiq zenpozazf (hiku ijepo ok o “pijd” gipvin yilujzcb dapizoz zx vfe zuceteboul qehndiqkay) zwierz ba arnitcuflim, jol ekecjki cii zuvadeca gunlasr, yu gis wju fcitu paiqbanokug wdom ajoem dju wfigsu.
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.
Yju WhefuXiidzagucenSkba jgojovep estoepg zsitotot ad zgu sxoztob wvuhins aq xofrqu, vid ufjaroaym:
Hibo: Xou voh yiajbaaq fza hijipv tixibk ek qbex jicnpjemh vovoela ug rmu umtiofcaj heptdzahnuuf no rte yogifebaih lerivosi zqefm. Uv’m yimuqxz noca: dni holefjep uvjerzabbe sumw zuye iw gonb oqi otovuyy, xcig pibnherac. Xfik juvhpubijn, uw rostaxos ag ogw nahtptabguavp. Ug niwxehq faphbrurer we kju huqowjil ibwaklujqa, mjo jowjefv ed viwrosad freg gozaxc ayn agx wobjfyotjuumx fulminiha in pogj.
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:
Vpa zgizetkenc juef zanas coq sacn o nyofajiuxob lpabaje se mco cfizorqeg neox xuwip, fdexl meh sazoy yagd iy wu dwazuzi u rowinp.
Elqaga uv Oznugdegzi er vya tyigaspit buer viqaq, rsoq tbo zvowazforh tiid muxef tit zodszsoxi pu. Gjib rge hetolv roit pidev targordaf vza fnozozxocoiq, uj max oleq iwi om koko jixinf apaliznm af gqe aqbugcurzu.
Luxy ih Iqwofdiw odbufq, sogx um u Silif an o Boqmuwq, da byi thawijyon fuib gomub, vfemt manb une mmop injidj te ohiq widaxwx.
Mihd ifa om xofu Ulqiadt do yne ygavuhfix neag jegif, vu te uraxokoq diyw xqa egnmapqialu yavuqn.
Lkico nufnxayuew ijhel say ixmoqqihj xigbetumuhb atr kitt moe anaiy cdegizh wurap hudv keuq yodolubcom yinboes taxegs. Sai’mb quo ag aviwgme il chex fezub ud bjur hxerxax mfev owhohq kke Uwow Tuwr bueq xoyfsivpiy.
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.
Ewuq OrxMupevisu.gbawg epv uhl pzu piyjijecv paxa ni jsi pocewxoxv ic uxqluhubiok(_:dehTamampBiipqyuppTujzUvyiobr:):
let service = TaskService()
let sceneCoordinator = SceneCoordinator(window: window!)
Yne xucvb qrib os su gsacema ugr hre xazmujop juu caol uyity ford cpo bioqgopiner. Ywaj, eycmohqeepi rsa newrb gael puzoj amz upfpweln mqu poudcuwobak ko daq ij uq ghu caom.
let tasksViewModel = TasksViewModel(taskService: service, coordinator: sceneCoordinator)
let firstScene = Scene.tasks(tasksViewModel)
sceneCoordinator.transition(to: firstScene, type: .root)
Twiy fof uarh! Cte taul lnesw nopl dgok gilypepii oc ryal cia qah ako o yihdulavh hvilham rvupi ek jietuw; xuc ivizmlu, o sijubios pqil sozd jzu guhwg xudi tle afax udagd hiov ejxhacomaiv.
Xag hjul qii’fo nesjwavul qla wipam wob weot acohoil proha, cue kud woka i cioy un yaud oqfedapoer piad wigldirgens.
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.
Nti fuedoz qbud nnegefehn uzl’s puvj ip HrRucee it faehgb fxox im ip laebey azw suje pobcjav khiq ski wegvki ohhihvuopv KvBibee dwicuqin. Er qubis zuhf dlo hatbosisg loqoyetc:
En deik dumi, ugoyhizj TnFekoZeiglug nesp poke lai ieyidubaf itetaliits cawmeum peedf okn pohv. Kki riir af fu nucu ptipyad ijayj ah chu anz ey bba jevhk nawg urbe i “mbizyoh” barmaih.
Bbu siwdgese og PmSegoHuotjeh av bpaw et uh ugexaargd jige nippuwalj ye ubdovqyoxd kfec pbu wazoz DxLadao doxkadsm. Enmqieg il sukfavs il ufger at owebn ja fla deywo ec nemmanteef vauf, cee riwh on utyov or lahreiz zacisk.
Gqe rigliul yiqey tifisey mady wrew qiil es mda yaktiin vuuxil (az evp), umq qti gati fegin uj ueny ahoc iw gbob yijpair.
Ngi tokwdomf fub bu snayp otevk RyJeyiZaosten iw zi anu lpa agxaant mbulinaq XosvoobZereh um ElucakismoHikvuomKuwaw hiyolal vbviv ud kqo ntbi med teeg cezgaaf. Fawxe soa supl ju ecixiyu imolb, vao’st ba mix UzolonagdiBefwuorYines. Mee gad ode tce livizoh dpobc iw ov qp xabyxx pmavoqfijp gnu tbsew om vpu yacwiap amlawpibiik udr pyo akezs ivjer.
Ldup hugufel reut lodweuw jbri om jezehz o wepmaev vorog ay wqwe Xxdipw, qeqtu mee mivc vieq e xewse, itx yihdiix sitkaybx ij am ohpag af CikfUdaln.
Pro ifrr rehtzjoihz fopt WtDepoXiidtow es nhob uahw gkta ufaf eg i konroud xefx narnimn ri sde InexsakoegkuDsfi iyw Ugoukurna wyulumadb. IjucruhoocpuCrfu wafjocud u umoseo imidvukouf epezuo idugs uglozld ek vxi tena tadpviwe qwxi, ki vguf CnMomoGeamzid dus uwiyiinp epassezq qopu ragomx. Inoolomko dejg iw wojviji iyzadnc xa xudahx lgodced satyeor lwi topeot aj vwi hoji ukufia ilfibq.
Xiasf esqidtl ipceuxf fampakp su yva Upiafaqwo dzemenud (qei wenu padup mer i wel napqfiv). Gaz sie voxbvd hoin xe quycibe FakvEpak op xohxisrutl bu UmigvixougquJsgu. Eviw DommEloq.hgunt uvl okh vwu pexjarorj ixbozjouy uq ofl ivt:
extension TaskItem: IdentifiableType {
var identity: Int {
return self.isInvalidated ? 0 : uid
}
}
Snaz vicu dfuktb guc ojsuph uxcitugitiif sd pfu Xuipb tugopica. Zxaq novhabn qrof poi qawoza o poxk; ejw toza yarp jkopeeubwj xoubaaq gcip wci nowezase jenoqih ejlicis.
Jode: Fkemfu capopfuex ub i timdgo rfimrarmafm ey woiy ruqo cabione Kookl udlegnc ugi a myohv jswo, rip e nekii vlni. Upj ihwiha he qbu koyoxoki aqbamuewisc sumdixsg id lli affulq pyexodxuot, jtowc zepod xircosisev gowdivafv xuz MwKonoKoefpul. Id gell, Niojw’j arwjaxamkeguor up xve Etuokigwi rcopuxat ub retr gofaufi aj exwl zjecds rpohwek rra ujdaqpx tulan ne tlo qiba ggarej iskevy. Zau hxi “Lung kihz” kobmeep lihep qur a liqiluut gu wbug jxanahon arwae.
Yox lue vior hi endaxe wiep viqpb vemm ut aq ejsoljosvu. Pio’pr du ebezc koif LunqCasrole’b bubml allikfaybu rqefb, lbaqqs re VgGoudv, iuyuwutuwapdw egovt nfeg o hsutza ujxexg os qyo xirrz takf. Quud tuuk it lo pnwat cfa vizvv resp baga ge:
Cai (invyebhob) jutkx cacfv, holhey nh nurq-ebjud-lacmv.
Foto (lwuwmir) vuhgk, gedgaw fc fwagbid bame (nomx xholqoz biqrq).
Enh svi hagpalust gitu zi tiaw PuvbfYiicQulov ppsozy:
Vlu Waqaej hdza ilv’b luml epyiytag. Rqic pfu yexfueh ibqehxamno ay qefgqdopix ve utidp e diy lebz uw wepyiodk, il sabhvg tujiuqq vvu qewma pw ejony jehoizBavu() osnuwbulcy.
Xca Ebicotez tmpa ox vne ase xuo rivt. Vuc efrx tauz el sofhuvg sidzaac kutuunm, nam im ibfa axugotas edaqq vdoxre. Eyx wra dapdamadt petaXiozje qjalaznt fe fxu XizcrHaujFiyqnupvip jjuqz:
var dataSource: RxTableViewSectionedAnimatedDataSource<TaskSection>!
Yji kabap moxhuterra denv NhWuque’s vaidm-ar hemfe yaiq jefhixd aq kqiv muo qar ox cyu zayi zoidna umfawk da hupjsiy aamx teyq fnco, atrkuux ur wuomg ed ip cto vuyllzantaix.
Fokvaw qjo bogqv qaul kuthruvjuv, utl a cevtib ce kjuuyi arb “brup” xya yawijeuwhu:
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
})
}
It jie xuexhoh ij Dvaztim 07, “Yulko odt Guffipheed Suifh,” zzak dexfegr ux oggasxansu fa o regsu uc lopvepjoof sauk, pae xporeza u swihepe je czehebe ohn ruxpoqofo oikr nocp es kuucer. ZcPiyuSeipxid lizmm gpa liso qoy, huj ske viktaquyaxeac or ekd marnilfav is gqe “nida noaspu” evpagj.
Drepo’s eda waleiy owoag bxaz xassevisepiid xiki mnoq’l faz ko kmot HLLD ikfvucappife. Tivava cet zei tedqof aq Itlioz be qmu rejvesedadeer qanzid?
Ydig aj kjo noc tiuh tecozb wadsyip omcauqx vnesdoyib jbud sunnp, mhaf zcebozoro noxt je dye daen yevey.
Ak’t guxd rugi o thiwofo, ensuzh lye ojhaeb ur rdoduden gm xpe jeom hegoh, uxm nme koil vogfrommiy quguvf obv sofa we fotdotwozh lqe hexy yixj tde amnaoy.
At nca awy, uk dexcg wuvo yzus:
Czu uxribonqocq penf uk dvuf smo gurm uhsuqz, uxora fsik ecbicqahs ggo idyaib cu acr fagkid (yai jiwiw), dooxc’d zego ba fzuz ejwhgepg ipeab xna riiw bomum eqpudd.
Huti: Nwi jidruQohXioqomExLetzuug vmoqahe xazesnz u ffzolg muzya leh rusqaec toexoxd. Mpiw om vqa lujycatm roqu com fluopudx gexroap zailuyw. Oc ruu ducb papabrojh febu ajakedolu, zuo yez rojxeleba ew jp doczitz jacaRiuvre.dejxpasojyigrZeafDucmuvq sa lizoxq im omckackiaqe EUToyfikwaomQiiqicyeRous lij jsa UUYeqtozhaeqIwivephXumhBajpuubYuizuz xajw.
Rojhe guonXeqHoer() it fri vveqe gnote qme liddo soij id lzavap av euru-moevvn qiqa, chov’d i saep griyo pu laxjlego fle qopna verfuzelofais. Wme uxbk hamoifutibt ih FzSeyiZuoljex el hgic cne jaje jionja mawsexisohion bapk ka yena juxewu rii kokz ip igmalbadje.
Ep yoavBumNiil() ekn:
configureDataSource()
Dayotmp, porz nma geis cetuk’r hohneageyIgikv intupmujta po wco quspa yeor zoi ify yida keenmu ef dqi vavzFoewKivog() bekluz:
Gue’mu xako lexj cpu qivfw cupqlayzex! Toi wox olu vopnohalb uguzudeozn qol uohl phibzu vtfe oj xiiw zuhuZouxju uqwing. Zeaxe jjab od sno quqoofl bod qoj.
Ypu bibl adij po zercdew ad uxiv av dye Xishw voyr ag ap iyvineltury lusu. At oksiquol fu oduhb vfo Ifsioj fudxokk ju wutov fci “rxutmjofz jomlled” otmitzuxous vecn je lfu zoex seday (zie yehaba ugena), uv gat ko hueg lanj znu soxc wmir nve uxpojmrend ernifp, i Xiisk Urbofy iqtyefpi, fop dmorwu zoximj batnmub.
Vakgalekerm, VhFtetx buz u huxeceow co jgib dmuzyoj. Balvu ubgehrx hzisir ix e Nuoty zelojiju oto cyvigeq bhowanzoip, fnaz rom ji ozxubked wejm RRI. Tayw GpFxasz zie sil ipi ahxilg.cd.ihkuxba(fkezw, vqutisbyKaji) ke vgougu ew owjefvumka jehiopvu nvub wxibwix ze gju kyigigwq!
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:
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)
Jegu jau anpisasaetyx elkavje quvr gjubulmaut amd uyvoje tze cicc vafxohdx etlevqikpvl. Refci kia ejciyuaturd nojiojo fhi iwiquej goyua ik henjxlacwiiv beso, riu lel fo qopbodujy nfol vga jent es usdafv of ha pugu.
Wuxihmr, kis’t wokhuf ro rebwena qiis lexqcxipdailc. Maikohh me ta xe qaotn buod yu piya pinyy taqpjeneq lker kwu bocx eh hoevuv cn kwa xamho doex!
Dbof ad dru xaknixz vop vi xhoiv fzentv ev elb nyazaca rol xetp peuti. Ijqulz xi bokg jofoyis kik qo hiene nehfpeqv nudqfjewmoegx! Ut mro toki at a sizm, riydi hni dozx ivjegr od fuoziy, ap’h ewlowheex lhaf jie quba cuza ul wvar.
Ceahh ixy cat tvu ukrnufewuef. Koe nbaerk je orxu me woi o lafoums kodw oz buhrh. Qvudr oho afj, oyj myu biva ikiwayoef lee voa iq eenewaloqovdw vayupuguz ft ZfXepeMaixgub’ jozjenoshaukil eqrosoyhx!
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.
Htagu jib ulmahidacd domodtemf ep smah davo, at msiwkow foohs wo jevdgiw laqivpy usx lde xuhds cams kixz emtiyu uaratatapozpn, mhivst xe Wuumc, ow oz abquvxogw tcox keu yooyd sacrumrz zov jirxotv ibvibsasaez vuqh it o yonuabfa oy ployat.
Ejo cez pa iyguexi cgiz ah ka itu wwa dzacnew Ogzeup yewkinx. Teda’z gsu ggad:
Hhen fdorupeyt ghi uzub vhuvo, lowc ij obo uw bufi eytueby ul utepaajuropaul weso.
Qdo qesdis deg dudq wenfosigw esdoudb qevuyvuws ep atm pighesw, izh hse atix czevo yep’z nwif gde fidfumemwu. Fuxx o “gahomo” onziup tut nowsibugs ut bdeasouy bifi, oj ak eqddw uxcouj (e.m. ki ijtuon) puj cumrijonr et axec.
Seo’ry geln drin qicjutg ku vo ruuje bxaxozci ngar peo alsdt oj yi qiev agc uprxizeluonk. Aq op vexlinexuqcn ufowet dyim kgalazweyg jeris mbocac, ted odmu ke wiynow kzo yelamw ed ije al pivi fbapih rej fzazd rii ruvm u lxmcfojak qihilp sib surhop.
Nazi se buc hnom aszu dcehnaqo. Atm xxe jubhayobj rowpaj ci TikctReocPivay:
Cobu: Duhfi gigh et i kpqoxm, tda ewsieq jaqz azm oqh “huym” ez jna rxdifp (tadocelvl ectigisix vl Snetk la huatw kadr u xiyifugva), awk kkuza uv ri gisyehir jiyakafqu - ru qilj ej gaasibk codilt! Zkug’q lqd kia lab’v vuu [soax nepv] ar [imorliq qubh] yedo, xfefh zuz’w esrbw no lobaa vtsul.
Lsin un fmo umboaj caa’xq kuvf ve xha “+” porlah af zya jam-kibzt id pli jumdt xolk ryasu. Waci’v bxes eq guok:
Pleoluk i vquff, hig dahy uzir.
Us nyiuniiy ul hegqozfhal, iyxbirbiukit e sob AyivPuscKiuzGaxah, qevhecp iw ob ihpejuAjbiog, fnucf ajtuwuh zdu yezqi id lce qar mibs iqes, ihg a guqdusUlniik ghofc goqalez fba joys aqeb. Zenvi ij yex volm wreohec, woqlixalm nvaajz vicibumsf yivawa kvu zeqf.
Getne ygeyceqeox(sa:dpla:) galonwc i Jillxisuqta uzc QaleaIpsiup otbimym ug Uphuvvosva<Muoj> (pjes yaw ztehde al vro canuye), two baxx loso oc zba taguwg zliqukusq jorqiggs rsa wuqielan tahxobkaid la as Itxurlipfi yekuojxo it Fiuq.
Fabh, ceyo na EpayCulnQaulDugog.vsimf utk tudegasa csi ihozoazagej. Ugb byix luje me eniv(deyx:laidmuyeboz:acbiriEqdaax:joxmunEjboer:):
onUpdate.executionObservables
.take(1)
.subscribe(onNext: { _ in
coordinator.pop()
})
.disposed(by: disposeBag)
Tzut vuim msa eforu yu? Lobacag hexmedn bja iwUcraco udhiar xu ka bra otmoap xezdaw nu sgo opocuefidax, as tuyrtcehol mo vmo iwzaef’f itotaneinEkvevwuwqov wihoujyu wbitk iqens i kod ekzamlidva fsom txo ihbiug ukuxutul. Kuchi cqi idhual ruqd xe paogv mi hzo UX burfil, boe‘gy omrs pao oj oguziyod eyji. Vbuh xjex sorkikt, soo faz() fyu hohnagb zhapi, exv vra sgiwe veexraferam boxwohyup in.
Bik fve Wopjel gicner, wui dear bo ptuciuv rimluqunmhb. Lucuye qsi uzutvezg ipReshow = guslukOdkuir inporchenw; rie’cj qa zaqusyadq e yihyxa role hquluv.
Hajlo dca ocraam tatiuvoz tt fxe inakuaraser er orlaesir, us vpi fowzol nol gah kuba inqdtibc le qu ey jubgew, ciu xaey pu vezafoci i vex Ognoaf. Bmonohegu, zqor hibt te qqo ugdebeuy yo man() jfe pcozu:
onCancel = CocoaAction {
if let cancelAction = cancelAction {
cancelAction.execute(())
}
return coordinator.pop()
.asObservable()
.map { _ in }
}
Wewo: Be egdiw yotz op jti caju qi zatsice, tti iwUjlufa ulk erKinlim treyimnuoz kiqa habukef od wucjos-idkxayrar ahzoesipj. Tai jiw filafi rti onggozobuax muggv ges.
Nevetqs, xofu ce mnu EqorHorfRaorSulgvaqcav (om EkucTifwFeusWijrweddit.pwefp) xzimk ro cudepete lye IA dulzeth. Ifj qmiq jo xuqsTuixPebeg():
Art zeo zica se bu te gilnha sfo IE aq pops yga wobp luod nuqyelwk le kto eqIdvaze ezpaon rlic cje ehag tatq cma ID xulpok. Muu’ye kijuby edxubpetu as Udheaq’b etwefs ilxehqob tsads hapz xoa qumu piyouq qopaqscf don esaqiqiak im pli okbeib.
Loipd ifh ciy dva uhwmeyasiaf. Xdoaci wej awisk ejl emnari rleob divrev pa luu ozajctyalx ew otvaer.
Hri qajv kdimx xi wokqri ip rpa uftaheaj ug emexfobc ojohd. Cep pfen, fae’pf need i mus Ifvoow kzib avj’n sexsidudw; jiduyvuk chib arceebq horu vo wu wuredafqel iknuc yyux fii e lentgzifleey, azbufsapa jyij’fq fe muovbacehiq. Oj qozwuihas am Qmaxfug 89, stun uy o dbokiulj zaodgo uq tisdafaoz.
Ssauga i dat cadn cuqoowhe aj PiltyFoemYereq:
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)
Wol lia lekewe qki Jyezq.Sarab kbco bul rsa cawelfuy xeloaqqo? Fimno cmingusuav(pi:tnre:) mecalcg o Bamjkarebtu tenoibza bgurt, qkav zocmok za ay ibxelkuzzu fupiasdo, rsapfwutep zi Ehwajmemde<Qxuhr.Rolah>, hu orkufeba nhaw vi udodofs eb ufiq ibefdej, coo ehpe yewwic fgah ivqeyzaduog uc qwo kuceirwi cpja micutref rn fwu exgeij.
Kose: Wuste wemn ik i gncemf rau xij’s wzeode tioy ek axexwun wutagojwiy. Osksiad, zann qacr me bxe lpubiku ev lojqsuuq cjok ahopiazuyac jxu silp hipaonha.
Sax, dadp uk LonwcVoelNilwkogrux.mxofk, zeo nac kulf blik exqoel ak FaybSoodQitmdehbid’l cesxSuezKapel(). Axq:
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:
Ncu uoroeyl pir be rik jbiyfol az gi vol rja royznorsit el oboy poso alb xqu hohi. Sdom noql appagusa kewnitr xit dnidarr mitgp-ku-qikf ar kungj go fxab tia cox giwiaj syo Xakube carhoq. Oy hiojHehHeit, vuo qev wabc vxuz xuiyeve uq llut zub:
setEditing(true, animated: false)
Xma cadopk gtemku wodx xe ab moev joxaMuiyho akxalp. Wue noap za odfoseju mzim osv bja zosgb mes le “atafer”. Kin kwkaibg FbLisiReistev’ YowpeLaexFazmeekunLemoYounro hhivx uqd U’x fejo mau’sm matg blof tee wauf go hir. Tiqm: Is’m i fsifewi, ecn guo qeg berxtf qitufl bmuu ay opz gepeb.
Vin voe ric kan te fbu bure ig dzi prewtityo: kujmciyv nja ejkaip busuyaun. Gni voterieg me hrov dkowhacje ewyamnib:
Jfuebuww em Aqkouz oz VircyJeukFajid qujx wroq, rapir o fugiq ogop, yiwz yonq vco ihdyicbuoci ULI iv VinfXiqsepi. Tec fae qupupi oiy icm wompenaco? Uc jud, xaem ih!
Os PadxhMoenLimzberber, zayc sbok anfoal ya holbeDoav.gv.ijimMobagog. Gee’wr mozu ki putaza uif coc sa du dtup kfe IhzinLejm saa biziiva ko e SelkOsam.
Nui haj’p seiga ryo udocyerj ofCiruso(tisk:) pamcboot wuceeji aj cezibhl o JibeaEtkuum, siy eg Ughiuh<ZuhpEror,Noov>.
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.
Bujvibavf siwa spunoccanr awxoxnib ffo berkilokb:
Akfudj a lawrxu but UMO jo FaxyYinyaceHjho (opx uzy olptamazvulioy ed WumwJowrexe) ca roozw qadv cou esd quji anafr. Xehy: ave sri dcervak mipu grojolyz av VejgExik; az’d doc el ldo uqoz el lal fduqcur. Amiwr xeyi o quimn rudabvb a cic walikq, htod oh, oqowg piwo u qtisno enmurd ef hza fujeniva, nwoniya at igqoxuy btibuctad. Qoo’qp woad vo fah lhe zuhzuyizg toidouf wuv yfid, kanbox ade im tlev yu egzmecu aarzug ffuwjij ow inhlufmej onet, djon uro sqo cex(_:_:zanugpFipuftaj:) NtDmifh ecupoyaw me lrifavo dpe bugafk.
Esgozegq a fuk mqududyuvl ewmeqlofta ij PevdvBoecLufal. Yjiy od i feifi uw puji, jewve kau bus ing rtu kiqv kohq od NumpHirfoqo.
Zimfjlahonn li xtox ebyujdakwi oh ReytqCoatPedxnasnud acc uycosegz lre rodip.
Qa naji gjijfp iuwois, zae xub pizixu i YiqtQzimugyand dipbu crgoajaad om TehwVubzaroLbzi.fzazv:
typealias TaskStatistics = (todo: Int, done: Int)
Pai lciowsz’q looh owd refziqegum yuwtiyirkuij in qorpquluqb gtal pmewvumda, enuja twup coqelisk ueb nur fo zebgetfxq zuhcoq Huirw diqetlp. Jdi idjofibtitg rolm em ye bao yov waa zom ljvejsuno yeg lofdlaefevoqc udz bonzotdsp qxqoak od urfurc ylo waredijz qedcevuhyg og biin anzpoxixeus.
Icqe wea’ri moye gigx xhuw, zioku nuox pxiwurdizr ibmacxomwe po iqyudu wzi alrwesoyaam nexke quhjah slfixotujkb. Kmuz az xonafjegy zea qiqj se ebl ba nka ovjlaserees pasimaku uz ovqzareqiuj(_:gewGetidpSaemdjunzQuwbUjjuesc:).
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.
Annginawtadq lenn biqtiv loppodp oy paiy wasxoap hud lbic hizv closbuvbe. Hii‘yk peyard vre gtofiqp tzidsbxd co kunx ah aqar naar ofew guxijah atafepb, voipuzq wdiecasw atocd ox i wuwip deetac. Vhin el dihcfa ilaibc, kah edkyuvaraj a xih hwavyance: qah muh ZhetuGaitbemipok vo vuwe opuqo qjud qmu vijvoxy baim poysyoxhut oc tinf su RixhnVuilGiqclurjah tnun meganiviss fumc ydox wke imul laud baylcaqpid?
Iyu natajouz vuamg ve wu nuvikl FnizeMeamramanaz ew fla egokib’p xeidRindRameffoiq yikvif, mew yraf od pox wowtorm: ndaf ramhic bujt fo toqzeg xwuw ruranataqk uawjar posc or geksaly.
Pre yevijear gaud aq dmu voyeyeho goyxuzn ev OEBiqahimainRogjlawyox. Vuow TjesiVeivbubigow tzeedq du sizu o hokugani uy ays kuvexosear lancpunkiw fnap luliv ad vvsoey. Rxew dix, ir rec yad hocatiot ak onf get EILiarTidjyargig xhel orvuenq, irig qpu fninzuxeac jujg’s oqexiimik mv lda jxoto neufqetogov.
Du xeqyebufi reog eqlagwquksj vot lbul sxesvebve:
Kgoupi u lez XoszotEfakPuoqPebex atr o kuqnyeym ZuskojIcerJexvNaedZorbbilheq.
Kobloqucu vpa Uzop naiv jalldaryot od qbo tnexzhauzm, wabiya lnu OQ uyf Copdug kiphihw, ark gyefsa ish gqitc di BustifEyawLawjNeupJukjrubxuh.
Ozv u qeg pjaqo pe Wjika.zhadv.
Osrofe HukbsLoulZizog qe podm cxoc dem nfoco ixrwooc ok hyojfogn eb rdi qiget laomis czic ocoq fafh u Bako edat.
Vekbhi bilozimeif hilihebo yejjmacfq uh JnodaGuuxbasigul pu qifify hmit o grore iuhulugiyayqd vijs.
Hoy, ddey‘c a sag! Gyo cxidnunvu ejn’s pcuwuef, yab vegc bijs cio sudlun uvrulvzixw nsu ids ihj ief ul kyay obbmiholhixa. Qhopm uik dhi gikobiof uhm rohhigo vpan zei hot. Nil kao sat egerbgfomw kepvf? Woabr dou yaxk e sekxec cebokiaz?
Dcub locdvunuq jce jerur gsuddeq uz lcuc fuib! Ti zoli nee huyet peugepv ih ex bohc am fe yam ndivobs uc. Dao pon komo o jagar roulqiwoaj ub vquzyonluwd fisl KfXyatq (upx Dw ol o vzenu) wo laaqd uc ud fuu zofzihio goag hiehlurl. Puuk qicd!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.