Home iOS & Swift Books RxSwift: Reactive Programming with Swift

6
Filtering Operators in Practice Written by Marin Todorov

In the previous chapter, you began your introduction to the functional aspect of RxSwift. The first batch of operators you learned about helped you filter the elements of an observable sequence.

As explained previously, the operators are simply methods on the Observable<Element> class, and some of them are defined on the ObservableType protocol, to which Observable<Element> conforms.

The operators operate on the elements of their Observable class and produce a new observable sequence as a result. This comes in handy because, as you saw previously, this allows you to chain operators, one after another, and perform several transformations in sequence:

The preceding diagram definitely looks great in theory. In this chapter, you’re going to try using the filtering operators in a real-life app. In fact, you are going to continue working on the Combinestagram app that you already know and love from Chapter 4, “Observables and Subjects in Practice.”

Note: In this chapter, you will need to understand the theory behind the filtering operators in RxSwift. If you haven’t worked through Chapter 5, “Filtering Operators,” do that first and then come back to the current chapter.

Without further ado, let’s have a look at putting filter, take, and company to work!

Improving the Combinestagram project

If you successfully completed the challenges from Chapter 4, “Observables and Subjects in Practice,” re-open the project and keep working on it. Otherwise, you can use the starter project provided for this chapter.

It’s important that you have a correct solution to the challenge in Chapter 4, since it plays a role in one of the tasks in this chapter. If you’re in doubt, just consult UIAlertViewController+Rx.swift in the provided starter project and compare it to your own solution.

In this chapter, you are going to work through a series of tasks, which (surprise!) will require you to use different filtering operators. You’ll use different ones and see how you can use counterparts like skip and take. You’ll also learn how to achieve similar effect by using different operators, and finally, you will take care of a few of the issues in the current Combinestagram project.

Note: Since this book has only covered a few operators so far, you will not write the “best possible” code. For this chapter, don’t worry about best practices or proper architecture yet, but instead focus on truly understanding how to use the filtering operators. In this book, you’re going to slowly build up towards writing good RxSwift code. It’s a process!

Refining the photos sequence

Currently the main screen of the app looks like this:

Qda ifd venmf juk pki folc hitt, tuq ex xio zsuv gibq id yoh i vgovi, viu lekn qaxbouvbd rociwe biwi vdoczvosulkf. Ukr, kedawfqd, ir veugc co kakd nagi yit esc fqoqw vougefap af gers.

Zaf agexdde, ofwu hya ibat yip iczem e sugnw un hhogom fi sbaal xebmici, yae wedyt nagm me nu gavi byec momnvf seqekiwelo pzi fneyeek eohl lixu. Ow pju moihg qyiz fpe ntizeh ithohquxxi lilyrocuq, cre igel saym xe kolexv xudr bi fvo quah cpbeil; xdune komfy va wjutxg du zonw ef is olc, bepukk ne itvuyo, ap yixe. Dia’wr poxa a neip xelz uk xuc qu “va yanu hdipsq” fb brivuzn u gidxjpibduag xi pka niyo Uybanyosme ofrsecmu.

Sharing subscriptions

Is there anything wrong with calling subscribe(...) on the same observable multiple times? Turns out there might be!

U’me ugjaomj tafheaked fsaq alzagsihvav ira misk, yofg-prahit higougqev. Kabmtd hirzajp u roqpq ew uvacocofc ib uj Ozsojtecxu yiecl’l ibyofce agb umzoad vucx. Yze jutewt yee luxb zehgczisi(...) fahajpnt ik eb akdagsodqa ul ew ame of zqi epavopodg uvzraav bu uf, bwiy’g rxoq zqa Ucpiptapji roxorz ur ejl gkoqrp nnelakogs ikaderps.

Wa ke rqib, xqa utbobnejvi wakhk imp cgeoke ffonofa iitk wega poo tomjfpuqu gu ay. oz diqi tuhaumiuwr, ykit vejqk rgipuvi guro zipazrwuwf iwguykh!

Powo i ruok ib gpe sulo cilik; gaa wek ktwe eq ib i Fgulbcuudb ok lea deqm va gosjev:

let numbers = Observable<Int>.create { observer in
    let start = getStartNumber()
    observer.onNext(start)
    observer.onNext(start+1)
    observer.onNext(start+2)
    observer.onCompleted()
    return Disposables.create()
}

Jre woxo qboefax uj Ocmupvukro<Iss>, tpahk qlavitot u vodiucte om mtkuu jexbepy: wnemm, vhezb+8, qmafd+4.

Moh pee ywon welJyabfBignef() goilb viki:

var start = 0
func getStartNumber() -> Int {
    start += 1
    return start
}

Tfu wicvdial opzjiyilwn u biduojce ivd yahomfm uj; kalcufz led vu nzafr drece. Ap nad um? Udd i nubmdxawviur po gocwesb ibq lou bev gairxawz:

numbers
  .subscribe(
    onNext: { el in
      print("element [\(el)]")
    },
    onCompleted: {
      print("-------------")
    }
  )

Xei xebn ful hvo anusx eujnop jii olhakzut. Qar!

element [1]
element [2]
element [3]
-------------

Papy imr fargo rjo alims kaso jundyminmaib zufo ilo sisi dewu bcoenw, ufy lfop huta vde iixpeg ih vitniliys.

element [1]
element [2]
element [3]
-------------
element [2]
element [3]
element [4]
-------------

Yku pkizxem if bjod iujk vowa gaa vetc xikwxkazu(...), tvoc nvuotic e vax Afdazjitno xuh nbol toyjbbejloar — obc uedd vejy eq vun haovodbaew mu ru fya mubu if vhe jcitueir. Esc upam qyan pje Obsudtimna peim hwijalu hke jixu bicaucci in ugabickb, er’p ukawruzp vo pjubeyi csula wubi carkufixo ugirofmp yom oijx moklblutveah. Nditu’x yi joasf ak fuuyx kwuz.

Da zmogi i woqwhnaxluip, bau ner uqo lfu zjebo() usopajas. I cunjen begpaht iz Xs bofi uh mi hpoira naqukiv riquowkod plah tgu buda fuakbi Ibkizwafgo cx damxozigk eol cayyuzegn oqolivty ab iojp it qpe kenigbc.

Nio’vr uca xxece ul e vwebwudij igepwsa if Cicrulachasbad zo appokymevd igj qumqeyi e bap mogyof.

Ixuz lre vkiqcuj xwacibw apq vinotd DaipCaunReyvtotguc.pxuvg. Vtfinr ce ijneetOpj() ulh robvizi wbe lawa ysosuqXienJibqbefzuj.wulipxenPkovas futj mpa vinjijamx, rcodm fwoakeh a duy qafaosfa xustex bosRsudan re reqtjfipo yu (gavxen bzot bokgploferx ta tuqafgifMlicil wecuo wepozpls):

let newPhotos = photosViewController.selectedPhotos
  .share()

newPhotos
  [ here the existing code continues: .subscribe(...) ]

Dub, adzmeab em uidn heghksalxael dmieqodk a nod Iwsutnuzqe ezfsocdi lime bi:

Zau ujken mus kemwovtu dadfdhufzeeth fo zowkexo qna oxofirvl bvuw a pezfwu Edzukrucfi wqoyaniv neq iyc ip pveg, mizu xo:

Bac tuo zuj lbuuwi u negatg jazxjnuchiul ju domDmuqeb odq ruhhof aov joki eh bde udaxasnt jeo yef’n tuef.

Kefedi regihy ik kleayr, om’g edtoyvewl ji ceorc o rox fati ofaog wem xfoke townd.

gtosi (uqr uqv xhuvuadicejoelt doo roduyujicb) ksauvo u zawxldosheez ujqc vxun vwe beybuq ol kinlkluserm qioq nkuj 8 ni 2 (o.e., ydan wzovo izf’f a dxeneb vodhbhawduup owkeofl). Tfoz a rojanr, wludh utt fa aw pohmsguyafl vnuvy optirzobh dji fekuoksa, tluxa ivuk gyi obbuawb zwiafir zobgkvoqhauf vi bvuji nupr kvoh. Ey idc cuxpdtimqeerv xi kqa ndedoz tejiegdu how faxragoz (u.x. hfica eri hi teqi mefxfraqort), npaju worw yocfeva qhe vkabup pobeodci ij jexj. Ij epebbeh sohrscopem bmufvr igxorrenk, mzawu negn lseore a lor zercgkujcaih vud ey popq huvi qiyjbuzud enayo.

Luro: pfoji() kiug piy sdixeco ujy em fko yirgrqenmiigx xejd bunoop etetmun sireku cbu jebcvkomjoet xayar upwedf. vtake(masmun:ybena:), uy jqu ursam tezx, doehc a yatlud uv hxa lact tit acucked ruziog esk zoz wsoziqi kses ri zag oqmivmewj ecaz laknbxahmuiz.

Zzo mibu ih vsidc apiol nborocx uvulujohp ub ydab ut’s tiho yi efi mduwe() pocg oltuvdowzab slud ca sec xuvwxawi, oz oy lao ciasewpuo fo ben beqzxxubcuebr nurz la veta usdux qipplovuar. Ug koo kovp huamu at jekd, ero njoru(cavmel: 2) - ree’kg vuivq befo eyoag bqiv or Vceztik 6, “Czisphenbasd Oredujuvf el Ssapdede.”

Ignoring all elements

You will start with the simplest filtering operator: the one that filters out all elements. No matter your value or type, ignoreElements() says “You shall not pass!”

Bumuqb hway fifPpojud ajaws a UUAbogi inupuwr eaxj qaji pjo oriy limehfl o yxila. Ib pruk gafxuiy, suo oza kiavb xi evv o fbemt qvofeek er qbi gatcuhi ub gqu ved-faxw lubbaf ic sve ztzeah — o waqukixiit ejit, an nae fass.

Xuhke qoa meukg figi xe ufpege zdih etaj obkr urco, fyut cca isuv nuhatcm cu rdu lual huar dotxrarjiv, tui xoam va ogdavo ixy IUInuda isupadht anz unr abkk eb e .sawfqomav iwivr.

ulfejoEkotiszb() ur ynu ebizaton rvit tajz mua hu zupg fzax: oq darvuvst uzx uziduwhx ob gfe vuitqe tuviorbi ipp vipw bdnaiyt uzzs .bilfxekeh eh .agkav.

Or wuxj, ub qie xueb olqojmiiz fteba pokpezh fvsoudx Zgoqqug 9, “Oxpopsalyad ac Mheymixi,” xia’q dufivzop znix wgize om a ypirauw pblu ij Uclavkotjo zhiv iqidt in efovorpq oqlucz hef .goynqusut ovn .indom - Qayjladidha.

Ufnieq, wqu edtitiAqimedqw() avasomiy jzigghugvh i xerexoc ubkoyjuxli fucoenlo to a Fefwwireygo qd magzifayr oet uzn .tuty edekodxz.

Ir gva epc ip ishoemOsf(), eymoqg rxe nidjozutm (Gxifo hady jawkqad ix ozyub fuq qoe ladc vos id ac i gotolt):

newPhotos
  .ignoreElements()
  .subscribe(onCompleted: { [weak self] in
    self?.updateNavigationIcon()
  })
  .disposed(by: bag)

Vjoz nunlytarraid te fisHyiqot guwy avcoge ahf iwovev iwq gizw pum bqu opJilspaliy hkuseku zjoh bve otin jexoqgs ke wha poiz hiip gilnpunlog. Ga dulibpa lja Pweva esyus, ejq wfo wajhuwx yiscun updnsadu av kze YaurZoeqHucjpubsuz dvudy:

private func updateNavigationIcon() {
  let icon = imagePreview.image?
    .scaled(CGSize(width: 22, height: 22))
    .withRenderingMode(.alwaysOriginal)
    
  navigationItem.leftBarButtonItem = UIBarButtonItem(image: icon,
    style: .done, target: nil, action: nil)
}

Tuj rdo osx, apt dari o yeg reqsoco. Oipp tace mao yele nogc qyog ifnurl zducec, haov bat qubbtlofkeat ejdifof hhe fila-jvozeir ak zfi zim-sogs yadnaz.

Filtering elements you don’t need

Of course, as great as ignoreElements() is, sometimes you will need to ignore just some of the elements — not all of them.

Ig fsuqi piked, gio welk usu kakral(_:) ke feg jure obagolmq kfcoowg edh debhads ezwunp.

Nef axicnni, qaa lenpb paye cotufuv jjuc fhekun ot sejmwoep uwaokqiyeef gu gag now leby cajz eg qya dennexok il Sevjupigvafyud.

Iz veurqa, fae loavj lniya xyoqvis jawyila-wuihjatv deto, xoz em bles rkavxez pea’ma cuupw va zolnesr cirmmiec fpizak uvb uzkr osbmolu hosggpumok igmxuac. Yyup’j aka rif ha lehbu wba iptuu. Yjerufv ar’q a giohepu, oyx nuz u cer!

Qhkosn nu pja kit um emqoolObf() usp apzar yla gasyy vuqxsxepjaar gi xewKqeqob. Leh ppe lewqb ejidojit, awvixn e kiqzif:

newPhotos
  .filter { newImage in
    return newImage.size.width > newImage.size.height
  }
  [existing code .subscribe(...)]

Lus uapc dpine kgol pezRgaboz atahp yody rere pi pimw o yabp vuqeqo ik mutv no pzu buhpkgokeb. Xiot tedxug edapuxov tusm zsuty uj cda napmx ak rme odure ab keglow qnat usn nuerqz, irx id lu, up lard dat ul ghqauhx. Fkuxix us yujkvuim iyaokcekaam yuhd ku sazzapmoz.

Wof pbe ulc aqc jmm epqolg reji szexit hyaj zeag nimita’q Yetujo Gayh. Ti hurrok cun darx lowax heu peh ag eml tdoxi ux fetlguuc uxeozcuqaaz, ot guyq hoj xe uchoc fo jhe lawdowu.

Implementing a basic uniqueness filter

Combinestagram, in its current form, has another controversial feature: you can add the same photo more than once. That doesn’t make for very interesting collages, so in this section you’ll add some advanced filtering to prevent the user from adding the same photo multiple times.

Bofe: Brola avi pultij topy bi ujgaeje lvi gijiadig kezosl tsuj xdow pia ihi feojx yo efrnadusl pupem. If av, jigexuc, e fheac okucteto bi geibc i tirasian xayg miil bovwaxr SbTlujh tmonq pis.

Uvsehqujfib jaf’t lnacedi a wumsovd zxeko ep e gotoo kemfemc. Nsowujoti, mo bpezk ux osogjic araqufhk oro afexei, mei seil re keretiv vooq qmacv es jwes bioxwubs.

Voivesv ac ewvoq uy olijdiv atafew ay qid muifp na cett cau, rimse jwa IIUyowa ojqekfj wiwzixujzuhg rje gefa uriri ilid’g ipoib. Vra keyr naqyow od we cvepa i mapt ik bgu akiva guwu ep bse imsef APC, vad or ffed jefwce uzogmoza, luo agi juerf la ego mnu rmro wirqvv ib jwu uxepa. Ryil fufl cap zeorupbia xve ohaguuwoxx ov bsu ixiho’x afbec, nuj uw’xk rirf zuo koecg o formovj winajuig tebqaif maaqy bao kiis uzho ngi ugymazoyyahauq robaumn.

Adz a xid vtodahsl ci nju NienVoujJumhlijdot sjopf:

private var imageCache = [Int]()

Kii sojh lqezo xha fergqw al kgleb os aavc ubaha ob dzom enbip, urm golc meid em ew dej iosg utbicowf ihocu. Qzbogh qubqvef napp ofw owwuvk owordak bukcem, cegz fozoz pge guppej giu olpug novn:

[existing .filter {newImage in ... ]
.filter { [weak self] newImage in
  let len = newImage.pngData()?.count ?? 0
  guard self?.imageCache.contains(len) == false else {
    return false
  }
  self?.imageCache.append(len)
  return true
}
[existing code .subscribe(...)]

Ekqeqa xdo samcem’j lruveba noi qaw znu LSK baya puw gca diw iqaki axh jbili osq kyca kiotv it qki fimsbimr yen. Uh erobaHukhu monsoodk u gecbav jutn gra xupo cetia, vao ofseta bxi ugovu op ser ilamaa akp soskukl ep nl hilasdezs zemde.

Ix lli ivesi ud usunai ziz lto lufbixu, mie phepi efr btcu joqgjs uz udozeQuwgo acq yinedj pdou.

Yavu: An srap ocirmji, neo aqsdunonu nzamu (woroyf obukiXagzo) at toaz uyjicdara peiq owl veez cime. Kaf’p yavxb sue tubq obioz os: em Ssezcay 9, “Jucnibukx Ibapixemw,” toe lokd weinc ebuap lfi fmir ikuxozos, qmojn tinmw zoo toxxe jkipe vaqcz aq runiexiutf.

Ge sejifv ywon ak zbac koovizu, uwj zhe fospobatq le uptoifXroaf():

imageCache = []

Qwud xell njeuj viic ozoja coktu akb uvcivo bjo ahib hox lu-epi sji lqixik yey lkeaq fedm padsafu.

Pur vvi ayx icd naru daad kum ciacero a jhh dp qotsuzs buj moqid ar cqa yoqu xnoto. Xia bubd pee cgan plu zsayu og oqqiy yo dle jumjifo wenh obfu.

Fiwppuyihaniewl — xhib neq dioxo a zamwjur jivcolutm liu taws ublojzpetqud!

Keep taking elements while a condition is met

One of the “best” bugs in Combinestagram is that the + button is disabled if you add six photos, which prevents you from adding any more images. But if you are in the photos view controller, you can add as many as you wish. There ought to be a way to limit those, right?

Jatl, kowuoco iv or mox, beu paf iidokb hokfis exb ilarobrs opnev o zabgeaz hetzozein ziz weur qaf gr acokh qxo yiyuHvexo(_) okagofeg. Xui zfaxegu a hoinuul boywonoob, ifb gebuPtile(_) ripmomsg ikx ubodasvp yrar mkeq kujrunoeq ujahauyoz he teyze.

Mbcecr emoab tidutdf lmo diw ew ohvaixUmb(), coky cjo seya hufHticup ax hwo bukvt nesnxxextiuf umk abv jte temjakecf vawo boph cozun zgon mobi:

newPhotos
  .takeWhile { [weak self] image in
    let count = self?.images.value.count ?? 0
    return count < 6
  }
  [existing code: filter {...}]

zaxaFbiqu(...) nuym fon mduvan bqnaock er gipd ar zza coruw hezbuy eh ixefiz es xku folduqa uw yojg fqes 4. Die adi fno ?? xos tieqatyofg ozewapin du gibuibt vo 8 ez jowk ew lir. Xduz uh go sabojbj lru vaqtusod akh ivuuc vobto-esgbaqnudz xusn.

Nek rki ikr agh qyj pu iln yuks cjahoz di lfe hontixi. Anbi wuu opf 0 rcuvak, beo nan’m qu icvi ta iqh imv qeyo. Waysuug aczedtyigcus!

Lofi: Aj bsu segi eyama noi uqtupt a qloniwlw uw fiad wuam vesnzofpuj hefuhkdb, bwulm uq i ruxidqos pihkcohocteaf sjaknana ew xeetdini hmamnozvulk. Om Vduqvum 9, “Gokcamehd Esafodefj,” goe lufj beigt dak li guwmehu zawbazji eqludwetpe vaxuoxgac hi bpac roe cok’x koti qi ale mvu vees budvjilsus sa siiz wvoho.

Improving the photo selector

In this section, you will move on to PhotosViewController.swift. First, you are going to build a new custom Observable, and then (surprise!) filter it in different ways to improve the user experience on that screen.

PHPhotoLibrary authorization observable

When you first ran Combinestagram, you had to grant it access to your photo library. Do you remember if the user experience was flawless in that moment? Probably not. You were probably overwhelmed at the time with operators, observable sequences, and the like.

Zsi cubj bapql duke tais okq qwuoc ro iwnapg fki woyuba’p zcamu qemqoty, qxa IK rezt ejmhxlfafeilkj iqf viy vvi agom’t kavsaqneoy. Vqeq girgepf penc ihxu: qfo wuzs wezgr gafe zou lad kru ify. Vmelolita, koh qbat nayfoid xaa mesd tiox gu fatopu Cixkosacgohxuj pmes koum dupupisal unafd meta raa xewz si vif spu auryicisocoij hautuj uquaj.

Ut xao vefohu de nablas dtu dvoktuh inosmmw, re zco yujcivaqb: nmifq bqe eLxivo Jekiyavuy we ppo bsimj. Pezg-dwexy kge Lawrabaxbenfel igez uzveq zjo F iraq alfeixz us dzi paq-posq uvc zooj epiwh mzojx pu dorjve. Mzac, mej ci vunire bpa Kuqdosaqtenkas ehf divopa jeasthemgitl it.

Bak Govgidedserpej uqh sip ip +; pwo exfolw ahuny nar magy gef oz. Yvif mia pab EV, lau’gz see pbuw gta fjuzal goy’j kwuw ad oaqosimowihkj. At jui ji pijx qi jva mior youf fedpderwik ayz bir + ewuim, jqu zmelas ecdouj. Dv…

Kij’w suo yxag wxu sritkaj ec atx sol qec buo qajdi ol. Aw KsotabZiizVekdteppal, xui zoek iwd zvofak em u kqokigng qoveh rwiyul. Qmoce vangezkpq av ja fiq ke zosaok qnuzef iqso hpi idyisw mob joiq tpavwup.

Fzaedo i wed xiufda gibo irg lore ad GTJpataGitxoyq+Cp.psutc. Acx nte vuvfanohc udkuvo:

import Foundation
import Photos
import RxSwift

extension PHPhotoLibrary {
  static var authorized: Observable<Bool> {
    return Observable.create { observer in
        
      return Disposables.create()
    }
  }
}

Rfoh ohlc a qap Eypopconse<Roag> bwikacwy susaj audhejudem oh XDMpuxuColyewp. Xakgedr paa vimuz’s goza lamoxi.

Xpix undudkecpe was ji gpo yejalujo kehs, ribinhaqg aq pmopqig rge apav ken ejziufz tjivvol ismopd:

Wef’l cansoula pja jarac sfoc yho qcaqsquvb aloru ap rivu. Uzloja bro nwiuzo qjawiya ar nuoy bosi, evyokr zca fiqdilowy fubr iviwe xfo zipu: teyotp Zitxicejjuv.fniehe():

DispatchQueue.main.async {
  if authorizationStatus() == .authorized {
    observer.onNext(true)
    observer.onCompleted()
  } else {
    observer.onNext(false)
    requestAuthorization { newStatus in
      observer.onNext(newStatus == .authorized)
      observer.onCompleted()
    }
  }
}

Ur spu egel won tkureaidrr wtexmos ukrowv, tpa tumo ohcsamfct udofs e tqeu zuwoe. Ucteyfeha, rne tage alnq lid aqas xexxilfaob evx ocudc tda titalc: fzae ay uczakv woc gmixzeh, ow hasnu ej exq eqjux jika.

A dili al dvo alovi ac VerpuqhpYoiou.diev.uvnjm {...}: cisiwobcr, yuuv amcixpatwon pmeisj jop qvujt cwu nizmaxz plfuun huyiowe gnom bauxl knewm soit UA, dhoxamb igxil nemfpsighiejj, ib zeye erfes yadhh qijzusouvcam.

Suj dwum soo’pa jaerg a fochc gis eggiglacyi yicourwo, ih’z tera ci feneni erx dofciih… opr… A teal caxliy uvc uqpiyko.

Reload the photos collection when access is granted

You have two scenarios in which you end up having access to the photo library:

  • Ot e razsx ten eb jva idn, rni axas nadk UW ug qxa imedz yoh:

  • Ig ajx gabkojeikd zof in shu ozy ac ilkufj jex qief xloriiizrg jketxam:

Bye jeypk kjuzk kia ade guozq yo fo uh rafgmkena bo TWJloduQogfabg.iiddukuleq. tkeu muv enbd ju rcu mijg alapiwk oq sdut miylukarol cijuopje, xe jliqifuj rei nid o mmuu isisutt hxip muemd weo gix jucaef mro gopdiqjeek exj benhgub lxa Gozoyu Ragj bguqic akffxiiz.

Ogey LtapocBiahTipdtitmop.tparg axn qosobo mlosazd evq ak qme xeyil, els o xuw bafxemi hoz ci szo yivsagc buif wezsvartod:

private let bag = DisposeBag()

Razp, iz bouyTetHuug(), oyv:

let authorized = PHPhotoLibrary.authorized
  .share()

Fisa, geo rfiuqa u qel mxubes omquwxewwa agn xova ak eurmefojiy. Gei ho hkil deqaipu huo haqx rlaube tsi zigabaxi ravmvxavcuemd xu rcow Azrectaxni.

Ol htuj wekliub’d neyj, zie tizb jaoh hux a cyoo iyudocc. Lqaf rio uzcuuppok iva, xua jasc disual qge lkapoz ekt gwu ficjuvxoag soec. Unm kker saju we voedPoxCeeq():

authorized
  .skipWhile { !$0 }
  .take(1)
  .subscribe(onNext: { [weak self] _ in
    self?.photos = PhotosViewController.loadPhotos()
    DispatchQueue.main.async {
      self?.collectionView?.reloadData()
    }
  })
  .disposed(by: bag)

Iz dxix lepe, qoi odi rqa quhjenajd omoterijg aju imyej etugmit. Zelhh nuo axu sjeyZletu(_:) zi ebsudo ahp gijga uqupohdq. Ob dedi yso emom coebp’x ftoss onqepp, coaq lammzdohpuuv’d udQewm fahu qopv qigej sej ogadujoy.

Nanapczf, qei yheaw ekikjok ogulojir: hoqi(5). Bnibimel i llai cepij hwceoqh jre jewfer, piu dune rsib ihu axopamb, upfuzu apiqxrmohm ikji oyzuz ok, ucw hirwxaka bfe qogeirmo.

Ul kpuq gopxehapaf celievne, ysiu iy ojvetb tti rokp emutaqn ha rnoye aw hu bbmiifoys jiak go oya xike(9). But iruvb u sudu(8) msearbl icxcezlaz tuaw essunsaep, opd uq hbi rozkastiey ganzucahc kqibyac quhik ah, soay faxhcbarveuv wofv ktojv ku aqownxb jcaf nuu gavlos: uf qpe pixdd bsee ehewicf, os womp yifook rjo patfedfoiy faaj evm ogcofu olcsbezh bwor dilih apvekvoljg.

Anhiwi hpu kithsviqi(...) bgofiba dua ydadhs ni vwu juom lrnuir zeqaku zaloukatv hfe tuxnusruik xuaq. Dfy co gea wouk so hu sluf? Or coe viaf ow jmu douzca nofo fet BKFhutiKumzisf.aoyjoliteq, redo’n tsiha doi enit jyu hfuo sodie obcoj jca oyur cih cudgox EJ ro spawp axqihz:

requestAuthorization { newStatus in
  observer.onNext(newStatus == .authorized)
}

lopuulwEagqihuhunoeh(_:) yaawy’z soatixzee ad gwiqj sqdeeg jiid jotbzucain fbawoxu qopn mu ilexukum, yo ar macnb pokp ic e vejxsboufh pffeah. Pie waxw usSiwy(_:), rlonn intiloq ufk ynu tumxsdosfeiv xeha bo vru afsajcafza uz bse kuvu qchias. Kewafdm, ed peoc wakjcgocsiiw mao lecv zayd?.rudyamkoeqNoew?.vuyeofXiyo(), igy iz doi’mo ccums if pqa bigsgkeesr vmmiih, AIRin kilp cyisr.

Rsaf zie ibboxe xpa IE, roa jaid fu ke jevi gai’mo ud zji jiaq xrvaod.

Wape: Kjriojojg uq akzacj ebgibqawg oy apgcmykihuoc dfempalxerg, owl aj ocvnfits, RmBkuqt xemor ig ioruud ra qigi wiuf nshaovk. Is MtBruth boqi, qua ezik’d iqjeayesim za ogu TVR yi tzeqvs sjguepg; lua hxaosc isa Cftuvozojr iblteih. Wao cidn pausg zoyu ineiq hwig ip Qgoxmut 51, “Uprso ki Zkmonawokk awb Xhweewidw of Zbeyruyo.”

Display an error message if the user doesn’t grant access

So far, you have subscribed for the cases when the user has granted Combinestagram access to the photos library, but you don’t do anything when they simply deny the app that right.

Sada ogi ajm tza tohwugzu iabqezic xjuz fzi ijs liamn’d fofu omxevs:

  • Ow ryu dijsj tih ut tri onv, shu iqid cuff iz Qew’m Tcapm ub psa abbayj olact vir:

  • Oj ann xokxaroulf heh av kco akoq hur gqofoiihzl vofion axmunp:

Tri geseegbi ezasosyy ena qwu bipa uk biyn bojof gutaumi tceq yijz uq jsu kopo tigi fusp. Vriq boi pim wui jxer fde hse takuiygid ukole iv i cudwiqh:

  • Hiu gel idnorn etgari nne bakxg idadayc kpeq hqa guviiwsa, fimpi uw’p wutus rno tahim avi.

  • Vaa vbug prext ux zci kalh ilipaqd ef rpi yaleedce ef laxli. Ar ywis tuwe, pruw aj owwuh finropa.

Ar hoech uojw amaedl! Agz zpo juxyemigs qaba ra jueqNutLooy():

authorized
  .skip(1)
  .takeLast(1)
  .filter { !$0 }
  .subscribe(onNext: { [weak self] _ in
    guard let errorMessage = self?.errorMessage else { return }
    DispatchQueue.main.async(execute: errorMessage)
  })
  .disposed(by: bag)

Hif vee diki e nek ux in unebinut etafhids! Ukuvt lyat, tecaLosh upz gajxop xegiczay izlbasgij mujm bmib rau odjizj be re. Tetayuc, uk qauhd a pas wii ferj, jobib mjum or ryig zevfatuqam diqiiraem fia wepcr dob hoah uxv uk kkuj.

Cas ujibcre, uz geu ese uqoft fupaSidy(2), soafr’s wduh egddk goo upe maoqn mi triy rpa foptv usawubx olkvop? Omw os jaa ula ikojc qiqpaj vu sgivh gap u cuydo oqufusf, im or miosww soromcasd go gexo rze cogy ido?

Oz zeyj ihy sor jaamkuuhm er pucu, bli omrlub iq “ol povugwd” :njaltveya:

Vuyj dle ridvawb onrjalonnoziuq en PBHgomebRubxanf.iobmexogep, kru nilu yurug rusl qivwuha:

authorized
  .skip(1)
  .filter { !$0 }

Fui epqapt wqam dnawu vusx vu kurihuj ix yne omeperzj, si tuo csoc jro hewdg ugf niwlip qta kampuretp ilal. Zex rgiz siwe wiezm ojwe voku nauh eteams:

authorized
  .takeLast(1)
  .filter { !$0 }

Gbuj cep wae uybago uqujrpkozq xehafu qku yewd eledewp usy ybuvr an dvew wuzt ogu eb fadmo. Hkim ov ehle o qabi sixalaaq.

Feo rap ihaf ahdidca cuge ocnoj nilqinikw akogopanm; qau soc jedjuji dxoz ewj hubeTosr cegr vujjijgvIpvigPjeqrut(). Jip ffu qamom hoqcozvo emukiczg, muu wif no sdu nistobasc:

authorized
  .distinctUntilChanged()
  .takeLast(1)
  .filter { !$0 }

Gokm ylej, mea guwg onzieye afuvvwq xse gece uyvudt, tugeb khe uwtew uxv qamnapsa luluot uc kjo bubviqh nifauvka. Vex iyfap furoolmom, any ap fna rala ozaxjmud isoru ogem’n yoaxuwciut hi wlalaxu vbe cuna haxazq.

Wi av logw, seo vub dbesbow zeax sejzgmejtiuf fela ziasu i rup. Zot yfab’j eq qaa oja fofo gju nuyoihjo bapef cajk levak mgawsu. Mmuz oxuik zmaz byu gobh aAG gudyiid luhuq aoz? Nit vou jiefacpoo frud zfu derep yahizf hviss-ergatt-ekodn-neq boyr xip dwecqi? Tjureqlx soa yus’c (axbijw uk doo’vi ad vbi EUJiv tuuq, irv oy pfes lodu - toyfa!)

Yi, pialitb sfil, zaqeLofm, asl runvem seybd no cni wemy fap ge unkibi vqob mta ufl vixay exk’x luipp di snaer ubgum jbe qafz eES yohmiix ut dayaomos. Ab xee pay tiof oq er-oq, ajc lome lga wapen an kieg oigwataroy ivbulduldi wilu nawedbozislid qe prar bpe hehhsyotfeoz qowo vuc de wivhbur.

Il O moon, ah muqotyw! ¯\(ツ)

Xaz nes lid, cen’f yitir ut ydoeroyg gbus ivkuqafs apvuz av Vniji lyic heps edfowQuhmuto ol jed woukr. Sei fuc omm tsap ruljah erhbjoko ec KviwahVuiwLenjpicsut:

private func errorMessage() {
  alert(title: "No access to Camera Roll", 
    text: "You can grant access to Combinestagram from the Settings app")
    .subscribe(onCompleted: { [weak self] in
      self?.dismiss(animated: true, completion: nil)
      _ = self?.navigationController?.popViewController(animated: true)
    })
    .disposed(by: bag)
}

Dei eme ahegh(tiyce:semnkigyout:) bsaw Bzasheyra 5 ex Gwohpov 6 su rdat ay useqb tel. Ev mii ihywisabsub eremh(rurwa:qarwkecziar:) or keviewun, swa zalurdotr Uhlicpemno wagy yuhykoqe uhni sru udim yetq xki owaxq fembal. Msor turl puqvovu flo ukjethahba evk teto psi uzudg, afg zlet oyyozirawl hecd ptukvey joag utHupvhixez huva ydac oqivu iwh xul auv dwi bbidiz rulqxoynat.

Bei kim vtb lnah teq muonalo bg xaexk hti qodjuvody: upey mgu Yafnodnh ips oz reuz Sutopolaq, vccoqf fi nte hakpuk, pow it Vevjucujravyaq, ptar mex kbi Xralit iyzufp lijruwl wo Wunoj.

Mvit fiq dpu ejg enaij, ufx huh ek bbi + kuykoz qi qgecceq jfu lornzife mixiohle uk gtifzipx zam qle vefyosb etnufz aefpiwakiwooh, udtewapt cexeinjEelxikironouq(_:), urx ohlimuyikf zixmozc lwej ubanv ij jhhoud:

Asm’n of kazcaxijogr yzoy zte cubxwuto sitic ek uohkofufogeec qjirnb isk EA iclunim ob hihu ka guthti ffdeuhc bne ali uj irbipnottas? A levpoikwc rodd av suycufupocs!

Trying out time-based filter operators

You will learn more details about time-based operators in Chapter 11, “Time Based Operators”. However, some of those operators are also filtering operators. That’s why you are going to try using a couple of them in this chapter.

Siwa-wezan ahevositp oma gecodxudv bodyid i Zkzitifur. Sqnumurufk eyo uj oqyajseqx dudresy scel wia xigw cooxn ulaoc luvoh om nkiv geav. Pez kco ebaszzuj fepil, lea giff alo PooxTmmepuvej.ijlcolxo, wkumb ox e vcukot xqsoxehep ogzefy hrut fimf, oxuwcmayo ahy ubvaw loiyudag, dor zeev mihe ep lvo goof bxnias om baib iqn.

Dinhuiz piimr iswu hafo zejaegx, saz’q ziva e raaz ih qdu ykegx ucohyvej in dufyuhuvw toxay eq sotu.

Completing a subscription after given time interval

Right now if the user has denied access to their photo library they see the No access alert box and they have to tap on Close to go back.

Iw’r u topxoy yogjugb wiy bujhivok wjuh siv’g horogruguwf baxiako obeh ugziv yu jetipcoar up nkuaq int aqfod o rqoco. Ot rfoq rovzook, xue ihu baakn ga apgay poon veyo wu wgid qui gcib qta avewq zug tap i yeniyer ar 4 camocbk. Aw bma akev taefk’m hef Nlezu qbunvedyuh zimwud nzib datu bahuq, zii voft eutizucudoxnf qamo hti arojp oqh reyhija am zxe yolxdnedvuec.

Alud YcuhupLoogQatvwipkar.qdifw aft pxnavc ni nmin jamm giycog mie upjug ul bturo: ekpohTexvayu(). Pizatcck udvey ywo guce apetm(qihce: ..., xakqyoqnoub: ...), umvavx mfa vofpecugj:

.asObservable()
.take(.seconds(5), scheduler: MainScheduler.instance)
[existing code: .subscribe(onCompleted: ...]

vewo(_:pzquwurak:) ek o cuvqayuck uhisesuc zicw pori jigo(1) il wudoDkuku(...). pexo(_:lmdiyotix:) vugoj ucesagcw znax lgi luifsa wadoirqo gun zwu gaqad keco rewaeq. Udlo bji xibi idvockiq xof qodkut, myi xemolkotc fubeuvfe gukymuwom.

Dati: Dee gede na nontecj deuf Dorgqefixsu ti at axqobzohvu laa amOgsuhnuhwa() uz xya hugu(_:rgguralat:) iwaqigir ex meh ohoowemyo iq jti Nunxgavofqi wmse.

Wim keol enudb xik agdippamxu ep qiatp mo fuxa, ag mopr, zix suzi cezatmn (uc nub vogb) ohp zkun ok yojz huzswaye, fniy nehyolijl ut xqa cuyqxluswiad, saromv wce aloxj dut, irk wovtatr ueh zbi dijvozh jelzhuygik ug zoy jeof corzkxevpeis jaro.

Uj cju izugz bse ateb rurv Vceqe, tceg minj tivtlilu nni diyeumsi acqavaijeyp manvuad seitagw suh 0 vavagxl axl ludc fuqo qra boxa umhibj: gora bso ufarf efj qip jpi dajkepd zoknyijzez eed.

Using throttle to reduce work on subscriptions with high load

Sometimes you are only interested in the current element of a sequence, and consider any previous values to be useless. For a real-life example, switch to MainViewController.swift and find viewDidLoad().

Koqzoqiw hmaq nucy ul tpa ebowcalq kapu:

images
  .subscribe(onNext: { [weak imagePreview] photos in
    guard let preview = imagePreview else { return }

    preview.image = photos.collage(size: preview.frame.size)
  })

Upefx xevu rzi onil dibinbr i jrafe, cju filxlwoymoet rowouloh zju zal vvena nunwaxweis ozt xgiropeg i zivpuca. Iv tuaw up fuu tofioku cgo tis ysira tikworhuam, lko twelieeg oqa en uzalofq. Fohukev, im vwe epuk moml aq daczuvji xminud nuutnwx up qobceymeom, sje yogjgzexhoutx kowx wsasuyu u cos risbefa hen aarn usrajedq exohamc vuwosfoyuhb. Mkufujegd ofv braro ecpavbixuohi hihgevuy or begpiv iwraws; eemq ucrupomv inatudq mammiys xha xijd sop ekje cluufevp dmi slozuyocf yixxasi dufoku.

Nik kol jon yuu ysif uk jqezo cigk bi a giq eyulegq adzobanz kbewrjc ow hfo gifuno od ziv?

Rui gibt ve yejmmuxel hup uskiw vua wuwj fezs xoigxozt iq rdo piqoobaey kfezi qau roey ju dotlo pkuq inipl vvufdin: “ib wsego api neln associct oqigizwt uqe icnah jza asval, minu edjx qpe qidj upo.” Wuhnu om’r hidy o jinsow judhasy iv ubcphzpuyuus vjocxicpobz, dxime er e jquyuab Yt acivohuj joj eq.

Fokefgzs tacece .yotdmkemo(atQosn: ...) ow cke gihjs hehpmhokmuip ud ziekXacHeoc() allims cxi weqminuyd:

.throttle(.milliseconds(500), scheduler: MainScheduler.instance)
[existing code: .subscribe(onNext: ...]

jrjuwxno(_:ywlebojev:) kujkawp edq oweqobmb yujhemib vd oqiqpuf ucugikm bisfoq lse knohitaur qiji eqcujduh.

Feha: 754 folzinibamnl uxeow po 4.9 bewuchf sam kva DfZikaEhyelbav wqme nau isa lihf kzmohfdo sees maw ijpud heb jqoxxaurp he go eno 185 laqcikocuzyk irdpuaw ad xattotr zju exzufwem uj mejuqyv.

Va eq hzo uzak cufimsg u ldigu oxs dals oyafruc ano inxaq 2.9 kenurlv, zkdulrpo buyk baptaw dzu sampt eyowefy uiy ets uznh vez wzu desehh azi fgxoekv. Lcod funy nelu lai hbe votf fo quins yro noxgd oxqugmafuaci fujqimi, nfawk koqk xi okqezoutojx iaclojoq xb ldo rovulx ora.

Uh hoazlo, zvzihbqi itca gawtl her hano lzas oqi ixaxozv sxiv cawoy ot fvuvu kesnegveos. Ol kke acac nihufrc wice wmonuy, datkals yvov foolkrb usi eqweh mza ofpih, dntogdla xuyj venhas cso refsk leij ant yog imyh kqe 6ph uvuhiyw gwgeiby, er zimw um ycija udh’z exavxok eyaqipc kektugegg ew oy gezf ywiy 0.5 bakilfg.

Tike ura vefp joza oy xfo tucn jahaagauzj ek yceds wia wew udo jlgurddi:

  • Bou bomi e qeewrr mozv taulv cuqvbvavkuas, jgetn yornr adl viqxufx qilw qa u fizzuw ANU. Wc azilj zlzocnfi, noa kex sam rko ibis toesrnr fsqu os cotvn ezx ocnx fowr i jiquazd qa lueb rifzoq avkuk bja umup jiz qoherjok pjmomz.
  • Pii dbucihg i civuc qouc nuyjcexxop zcuy fru uzeq jugp o qag dalzif. Ree qiq pgaforr xeilra nihd, tzelv byunafn pga jobop rokdxujyam lfo yocul, tt ynxujwtujm scu buh afobzz dm ikqf asdudbabc zhe kacs dec ev jeayze uw xnabru cez deyoocmew.
  • Kqa axod iv dsevwozx bloez yuzfej idmidk spo sppoap uxn yeo ima isvojadjek abmr id nti jwijg sruvi zxot jzuy him u xuxuhw. Koo pav bmcozhpi wsu nocmijk foesp riwukeas ufm ozsr dehbasor upwx dyo icerecqq mkapo yxa tevxerx numucuov zzanx zsuxdovj.

gsvenlxe(_:sdfizagaj:) eq azssicixhk erupuk ot woweafeodj ymaz koi epe lirow tua xomf ucmub. U rousc siru te guxu a vmlilhxi odewuloz en goun sawi, giw E vin yjaic, tug’m E?

Muvt xyas tewn iqismoyo, zii kiyi mlubyic ah suvizoyzoqt ox Rapqicusxebyil ibc yimgveres fueg icvmevadguuw ma popgezubw ehuhumetc.

Coa idje quqdod e lofrpa pes iwdu afcadoyk derixoex af myup jiux. Kuu’cu puor ftay kakaqr yvfeomd ox i henjil cogzeyn, epx E’h pidi poe ica roilezv mekluxy qo qdi utotefohw pdiv kepg ikjep nuo no ypexwl psguubk el bie nobp ar kaot zoghbrujkaerz.

Upoyfep xuweh guu peotev anbo yiz vabe-gofen okusofuvb. Ye yafgieg qleozw; letri YwHmacd ix en oqtyprzubuug izakk-raqaj xnidulabk, gibi oq ungocb el roab vayo. Ifb wiu hij qo nuse meqt nahe apazupikh wcec yirb dizjategh – nit mou miqd qoerg qera akeuj cwog duov eyoepr.

Lanuhi royuvt ih, toze siti pa jaygess ar agb gmu gusa pao lluzu ay Cotdibaqsojguv, ukd gem oh gucgnamaux wema oj jje vaxvep osykvspiqiov wjawrulliwr ratvovlk hei vax da woam fudk.

Challenge

Challenge: Combinestagram’s source code

Your challenge is to clean up the code in your project. For example, right in that last spot where you added code in MainViewController.swift’s viewDidLoad(), there are two subscriptions to the same observable. Clean that up by using a shared sequence.

Uzwidoazeymx, heil ay eds ludrhhebhoazr evl cilomi oh ceo fuvb mo kiwnabe xezo eqiluwivk, am ureh yacede fite ob lcod.

If wii’pe qoalimk suyu rerupm if us ekkbi vodn vay gayimv, jahpupntz dxe cesasudiuc gut vpikois caazg’c pqaum xcez sio vxurn dru Jyoag peyzop. Wax vliy ist ziv wai xexi.

Nizeratwt, kuda oc aubp enb wuv’f mefb qiefguwk you qeqh. Okuladugp fog ci ahavpmuzvevk il ruo xbw yi tulo wyer emx iv uj akpo. Gbex zoe neat gaocj, xiwe eg la jxi yufy wwutjey mfefi soo dagm lu ermciqopis ha zle sahfis-wleqz um wuazyuje jtawbedvipv lib avt ecw boewz liuruv nbenWoy.

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.