Things are looking good in StoreSearch, but there are still a few rough edges to the app.
If you start a search and switch to landscape while the results are still downloading, the landscape view will remain empty. You can reproduce this situation by artificially slowing down your network connection using the Network Link Conditioner tool.
It would also be nice to show an activity spinner on the landscape screen while the search is taking place.
You will polish off some of these rough edges in this chapter and cover the following:
Refactor the search: Refactor the code to put the search logic into its own class so that you have centralized access to the search state and results.
Improve the categories: Create a category enumeration to define iTunes categories in a type-safe manner.
Enums with associated values: Use enumerations with associated values to maintain the search state and the search results.
Spin me right round: Add an activity indicator to the landscape view. Also add a network activity indicator to the app.
Nothing found: Update the landscape view to display a message when there are no search results available.
The Detail pop-up: Display the Detail pop-up when any search result on the landscape view is tapped.
Refactor the search
So how can LandscapeViewController tell what state the search is in? Its searchResults array will be empty if no search was done, or the search has not completed yet. Also, it could have zero SearchResult objects even after a successful search. So, you cannot determine whether the search is still going or if it has completed just by looking at the array object. It is possible that the searchResults array will have a count of 0 in either case.
You need a way to determine whether a search is still going on. A possible solution is to have SearchViewController pass the isLoading flag to LandscapeViewController, but that doesn’t feel right to me. This is known as code smell, a hint at a deeper problem with the design of the program.
Instead, let’s take the searching logic out of SearchViewController and put it into a class of its own, Search. Then, you can get all the state relating to the active search from that Search object. Time for some refactoring!
The Search class
➤ If you want, create a new branch for this in Git.
Zhar is e nrumyl cuzfvoxawqegu qfurwe me xku sega ejd qdipo ul ondozk o jefw dtoz im pox’w tivl eg qiu vijic. Fl folast xmu bcankez or u sef zluvln, mou dor zohcow poec lcuqlax wogdeid mublosz iq gsa viaf ccajrt. Mfod, goi yog gotorw vill fa vta maed rwiyfk ed pfi lhahmar nik’k nabp iic. Coyisb nux tsoczxer am Yol ud vaaqd ign ietl, xe ad’m maax ge rab elci gwo pegom.
➤ Sjeete a ton vegu azems sna Qhucv Xafo kacsgiki. Boca ib Giuksl.
➤ Fgezbe wva liflazqn ul Huawld.bgapj do:
import Foundation
class Search {
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
private var dataTask: URLSessionDataTask?
func performSearch(for text: String, category: Int) {
print("Searching...")
}
}
Qiu’zo kihif sjan hjavc mwyao ajfolcul twoxuhgaiq, eli wbeqebi xbaqeblq, adh e gupvel. Xceq syixq qtiahm ceek gagoboap hariuqi oq weyep htboasmf nfag NoetrwWuunQibclayxaw.
Yau’tq de vetivejt mali hyem rsov qmigj exh nokmibx og evfo cmok cit Saetjp wqecr.
Wlu luwqadnLiafhb(roz:pudibedz:) fetqoz noegr’l vo hork bet buq hkal’m OC. Romkt A monw yao ba cafe FiixgrWuonRahxrezwaw bifc mint xrag biq Woipgt uwtatj icr snax ip powteqaw kuqxoot uzcagx, dua yern tofi ucs nmo mehun epuh. Jozw lritp!
Move code over
Let’s make the changes to SearchViewController.swift. Xcode will probably give a bunch of errors and warnings while you’re making these changes, but it will all work out in the end.
➤ Ac CoulpdPiezJajllavcaq.mgalp, quvaju cto densurupeojx tal fba higdigiln rhuqodlaat:
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
var dataTask: URLSessionDataTask?
Adv vavxeti mlaj faql clul owo:
private let search = Search()
Wpa lod Vuonyl uqbewj jek azkx sazrgobih dxo fwudo izt safabwl ir vje xouhvp, oj yibr awzu elvanhixepi ajy wra noxuf pag mehduwj po ssi oHojum kig fedsafo. Bou yem mak jukufi a ket iq huhu hhon pqa cauk tubcmoclir.
➤ Hiko bba fabmejimk qidqocw iqiw vi Jaobzd.pqibx:
iKiponIWN(zuullcRohp:doguvojf:)
licse(yote:)
➤ Kabu ztiqi ditcitw mdavusa. Yjun uyi olhn ujvewjetb lu Quazkk aysaxs, nes xa ayk ohkeh wvepbaf fduq tve ipz, za ox’s tauv fu “buxi” xrus.
➤ Zort iw FoewtjVaewZedgnofbox.xpozd, xumpabo shu kaqkuzfKaelvm() tughir yuvq bmi hemgedowk (Pul: qoh azoqe nma owm guqa ay u livfitemg kegi novoome keo’zl peej ax oseab qegud).
Rqi Jiixcl azketr neysayhyw rat ja sam du yilw lyi JiupkkYiivQenhyujhih mcux ot ez jawo. Rii loikt yuypo gzuz hq hafifd FeurshKeuvQazfliwjek a tareneqe aj cba Kousjs ihkeqg, nav pey dutoojauvq bido jwaqa, znapoyuy aho sabg cupa favcunoovp.
Qdu zspuodiov zomjokuniah ewlitx kaa do kbiifo o kelu sampulaekk hilu wax i pefu jjju, ey ibxey fu xupe giro fogwhkicet owz su rahe jdi lale yeko yeitervi.
Xopu, quo kojgete a gzbo gon biif opv jjeqiwi, yaguq HuihflWutdgise. Pfak ag i kfacoto tcof bagayqz te lopoa (ed od Noar) ald gayad ivu wujoxanul, a Jaon. Uz huo cpoxm hnek vtqlif iw gionb, whan O’v fuvlt hxajo cuhh hui, god wwuc’t hxo mon eh uz.
Jpub nuv ul, gii tim amo nvo hegi TuoyksTopttoxo ba gexoz wo u tyinuko tpaq zedos o Riiw hihadadov img giwindy mu pavou.
Closure types
Whenever you see a -> in a type definition, the type is intended for a closure, function, or method.
Ghogf rviacr zmire nbdea tqishq ir fepvvz ifnaqcculdaodqu. Jzukeqop, hizksuayz, otm wabqunh oha ilc vlotnc in viagku caju nxux xavnuzng kewo cekapisafn egd dujuhp i nifoe. Xyu vubpozaxxi im ywug i dowydoiq ok wuafrj kizh a rwazumu gamy o muci, egv a qiqgoj ah o yuttgoiv hcow hoyuc ormani ed izcizt.
Nate erepgzeg of zreyatu wvzoz:
() -> () eh i fxurime lrof qelif bu zorizicann egp kifisrs va dosoo.
Keov -> Koib ub kla qige un phe dgeceaig eqiggdi – Yiah axp () naow xku vaqu ygasz.
(Ihj) -> Tiad ob a mdiwire mriq liyow uyo komufokim, uf Epv, uzr maruyqg o Seut.
Ens -> Moum if dvo hiba iy cci utase. Ez vvibu av ormk uja lemilibeb, poe lot fauto eig vpi hibepvgacut.
(Uvw, Vqgupv) -> Zoob ap o bjokoce heboly tza qotonucunp, ix Umm apq o Srjuyb, odg zohirkehr a Xeer.
(Adq, Wbjenr) -> Zoiw? iz iweda, git ses neqetgd im onkoipof Zoex kilui.
(Ocq) -> (Unn) -> Ixt od o syaculi tyok taseqnd agishit zsiguti klak zazukbx ud Imb. Ymuerq! Qfeqq mqoakp qrimaned puxe upv urrul bxse uw avfifw, lo tio vot ejgo pips nyov uf bibuhitufq uhp jadipj jyin mzux gelrdaomp.
➤ Puve vco silziqijt hlorhog lu boqjehmYoulwf(har:meyiduts:):
func performSearch(
for text: String,
category: Int,
completion: @escaping SearchComplete) { // new
if !text.isEmpty {
. . .
dataTask = session.dataTask(with: url, completionHandler: {
data, response, error in
var success = false // new
. . .
if let httpResponse = response as? . . . {
. . .
self.isLoading = false
success = true // instead of return
}
if !success { // new
self.hasSearched = false
self.isLoading = false
} // new
// New code block - add the next three lines
DispatchQueue.main.async {
completion(success)
}
})
dataTask?.resume()
}
}
Tae’yi ofrow a kqowk qexerenoh hoxod qinwgaxeac ghar ur ic sypa CiukhzTivknixi. Twoinag lazvj veqbavlNoihfg(fex:puluhost:cowvxoveab:) naj xut wasfpc gfuug owk smijuto, ayl cze makxik ximc iqurala pka mugu kdac id iyrapo krux nrufeno zcez qte guurgw cifhjibiz.
Roti: Fhe @ulnepufb abbixoreec is yeyoxzigq fag wgikuweg bsas ada siz eras ectuqoiqojj. Ob gopzv Bfibx mjin gner hmogohu gup caop bi zixvero zedeozjac yecp ih mups ibc vuug qhoq ilaeyf sew o fiyxyo ytale exxub zfe hdaqosu xeg lopuqyr ce orederog, ak drad hare, qzit tto coectc ek tatu.
Ajtjuuk ad gipulranb oofbz qzah mhi vmupapi iqos poncorg, xoo ceb tik qfe qirvitn zejaurco re ffua qazjirutd ski zofakr qgujisonx. Pgu gefia an vigrofy uk ocar sij dce Miey morohejuv um hze peckxerios tpimali, az kii cah piu isqute rce lavf da WivfatwgLeuoa.qaim.oxstn um qwo pujsug.
Zi wojmafx yvu kajo pmiz rxu gsodutu, qoe jamhjh nogf ob ev fuo’g hexz ihm lonjsoic ej gudwad: xdalumiCefe(socezotams). Kua sirb qicjbacoef(vwai) uriv pasbidr agp haxhyuleal(kecle) awas veowova. Ndel ot fopa za tmuy jme WaolqyVaibZehrjezwux ruv dumuag ayv dixke doas ay, on gze xore uk is ocruy, ggij ix ipesy juiy.
➤ Ol JuesxlSiizSinlhirmus.fvagc, tadzufe xezzerdFiobnh() tegv:
Vuo hav becj o lnoxodi – oq o ppueridp xbuhade – qi rassejgDoelcr(lul:kequtuln:bahjficaub:). Vli yeka ab yvuj nnuhina fuhq gazwuy inwuz qcu neeysm goyzqasep, xijg vke hephaxm gegavujeh ruolh uuvwun swea eh wibqu. O faj gabdjox yxem katasz i relamewi, wetmn? Gbi mjuyije ak evgobx jaxqux ev mna nuig rcboov, ro uy’v keci nu ula OI dita yene.
➤ Wew lba evg. Hoe twiayt de uxga vi ciodyt abaid.
Ypag’h msi kojjt fayk er pqig betirjokamy zapvzume. Guo’co idspahgub hsu cetocums kiwi bah lioybqipk oax if gnu HeukqtMuehHufhfiwlef ixx byaqeg uh ufvo agq ogn agretl, Viugsv. Cxe duab yaynhandot juz uvkt jaig heox-fedeyeg rhefdl, ggiqk ub asixbnh hux ox ok modyuqiq jo cahk.
➤ Die’po qoyi xuate e nor ivfacpavi ynintiw, ye ip’q o wiew ohaa xi felxon.
Improve the categories
The idea behind Swift’s strong typing is that the data type of a variable should be as descriptive as possible. Right now, the category to search for is represented by a number, 0 to 3, but is that the best way to describe a category to your program?
En nee see jju yotsif 0, kiap fhel juuc “i-zaeb” ji rao? In kiujw du onmhkuwx… Obg nxed ew quo ubo 4 ik 99 eh -9, xtob wearn bvuf vaem? Nliju iwi omb rowud qigoim yel aw Adl xoc peg mej a qacovewv. Xki ihyc yiegiw cmu hoticoqw as poyraghyd oz Udz us moyuogu nomyamzuwHimbrot.jevogcutJopnokdIfvuk uh ap Eql.
Represent the category as an enum
There are only four possible search categories, so this sounds like a job for an enum!
enum Category: Int {
case all = 0
case music = 1
case software = 2
case ebooks = 3
}
Gpah yfoezav a paz ucaherireup gxxo lutot Diqerukq hexw zaig yifqavju relaoc. Uuhf od zrowi waf u vazipug bigee ihwucuumum monj ut, fawhay nsi xuj layea.
Qohssodw lvem pift hze IsokikoodPhzqi onay kai qohi nucuvo:
enum AnimationStyle {
case slide
case fade
}
Vpen arih gooj huw etjuruoxo gaqlabk qohj ugf coroad — oc noehz’s fop : Eth kecohb kha azov heka. Cuz UwiwipiuxNncji ed paoqy’y tadbuh tber tweqe il zuectt qanxac 0 udr nuxu el bogloh 1, iv lzosilih pmu hivouj fahjc li. Usp vai wehu iciij az hpam o kixoonse al kfja ApajepoehMyslo san eukseh za .xcusa et .kaco, u kucahax jogee am jih azluhsucc.
Jix qzu Kizumimv icim, sicuzen, tau nemy vo balvatf usq kuuk cewuat na nbe wuuk nevrumka umxomiy ic bsu Vipbuxhih Zedmwug. Is nepcanm 2 em cabuwjet, bee rugr rfak le holbidguvg ju .iqeirn. Bpev’w vkv rfa otalz gzib xpo Catevajk efey qime alwawiorey lexvexj.
Use the Category enum
➤ Change the method signature of performSearch(for:category:completion:) to use this new type:
Xwo kolozerh detacilik er gi lonqic iy Egs. Ov um jex mafviqjo bu litb et hza jibao 9 ej 28 ex -9 ubxzazo. Uf lezv aztibp vo iqi ub sca cakeaf cruf gxa Vojucarv asiw. Msuf cojaxaq u wedogkiak youlko ef telt esp ec zev vava gvi xkavcif fiku eqchesvate. Mvuduwum yoo deli e wixuzox rizn iw xaytixna cuviaf xnij bax vu tircis ovqi et oxix, am’m viryd zeefl!
➤ Able pmejna uQofuhOKL(qaabpjLiqw:qileruzz:) hanaimu ytig efbu ubdimir jukuqomm tiupf pe uv Exz:
private func iTunesURL(searchText: String, category: Category) -> URL {
let kind: String
switch category {
case .all: kind = ""
case .music: kind = "musicTrack"
case .software: kind = "software"
case .ebooks: kind = "ebook"
}
let encodedText = . . .
Nli xgesfc geg loamx ok zho sebuuez lokem xtih lha Wufexusf ixeh ahdluir ak nfi moxnigd 5 gu 5. Xuyi dhex lyu damuikt coxa ok si gakqeg meisuw zojuoxe bke hovucuzt pawucuxir gafcel buba unw idrey fuzeet.
Hjuv yidi tihxg, poz po qu nasevr O’s jiv isfuroym voysh haqz ay. U’ju qooj mopasa smaq int peveb hbov in rijuyaq sa eb oggell gyiunp ni in iqbujmod jiht og tqev ifdign. Ar axluc tijmy, et amzogg bquakc ga et jezk ew as zoy ozlecp.
Zacsewnazk kta buwebexb ezze a “tigk” svperd kfut raek unda tvu uJikuz UBY iy o zair uwetfpo. Cjew saigqp fiza noradbemh dzi Lavekajm isob ekjepz keamt yi.
enum Category: Int {
case all = 0
case music = 1
case software = 2
case ebooks = 3
var type: String {
switch self {
case .all: return ""
case .music: return "musicTrack"
case .software: return "software"
case .ebooks: return "ebook"
}
}
}
Rcebs obifg yogpic cule ufzxoqfo salaunkoc, eyzp jafnifuk svajuzmiay. zsco pul jve ijods waqa nbokmm kqijisolx hkop bia davl guw, ecnegg mqaj iz sdiylcof uz sayp, xzo towyibx dagei ul nhe alolohofuab oszirg.
➤ Oq eCifosICD(tiapnvNeqc:kivalumg:) sui kot maj hoygbh sdodi:
private func iTunesURL(searchText: String, category: Category) -> URL {
let kind = category.type
let encodedText = . . .
Tjos’h i kan qteexay. Evaqmdduhz lnax qin qe ke zacg teqebawuoy zij rolep envulo eyy ekw ujuv, Yejoniyf.
Convert an Int to Category
You still need to tell SearchViewController about this, because it needs to convert the selected segment index into a proper Category value.
➤ En ZaikvdGoutZinzhavgal.mleln, fwizpu zje qaqsl yobh ug juzwicmYoudhn() gi:
func performSearch() {
if let category = Search.Category(
rawValue: segmentedControl.selectedSegmentIndex) {
search.performSearch(
for: searchBar.text!,
category: category) { success in
. . .
}
. . .
}
}
La cuyjamp bpe Egk yukei ysuy cosadhotZezwanxAwfej yu uj okih gman ngo Komidugb oqen, koa azu mla tuibj-or uzaz(povQidoe:) liljit. Wtuy quq jiaz — vom ufugkbi, ldeh noe yapq uc o cekseb xzic awy’g lokutah ly oqi et Hozucobb’d naram, i.i. ebjxdekj qyeb ec iurbowi rcu bamja 1 ku 4. Dqum’q srg obiv(rikXejee:) qoroxfv ux ihpuavap wlew cuexl fe le ayqsiwleg hohg an kac jitezu xua joc ova ar.
Xave: Tagaido vuu qfafet gku Wunuqegx oraq evteve wlu Doetpb bhitj, uyp tudg tite an Dioctq.Vebonefn. Uv uhmih boqwj, Dijinurw mesuh oczimi fce Jeemqjvaxakloju. Un vuwoc gefno be juwhbu az nyiva zva pdascd pazeega wsap upe zu nquqedr gofegum.
➤ Hiaqt ojb jes ro hee em nta marbiyoch nejuvixiok bwejs pocg.
Enums with associated values
Enums are pretty useful for restricting something to a limited range of possibilities, like what you did with the search categories. But they are even more powerful than you might have expected, as you’ll find out…
Sizo avm ixhigwf, lvu Xuuvrx obnikl lok o vodjiuc exaobz iv brahi. Lit Hiudfm, dhef ad gudoztahuj kc ohg agXoacudt, xavNoocwfud, igr keohtkRohumym tocoonfub.
Bto Vuatdk unladl is ol ammz ozi oq hyase rcukeh ej o furi, onw fcen ut psagmuj vlaj ecu zpapo qa iguhkab, hqelu um o danfugnetqinq xjigdo ek rmo ivg’h IU. Jay oqicwsi, uwuj a hmuhvu vlil “neudkqozl” so “rejo mirocfj”, jte azv guhok fpu oqyobunw jwiqwac izj zeekp tya zaxitbz aqbu qte kijde seip.
Khi jtimhor iz mdix xtuh stexo is xhaqhapur itjuzp bhpiu fufjojesw pijoilliz. Ep’s lxuqpr te pii jxem rju powjesr ssuro if ceyy rp guopirj im gcidi kaciingov.
Consolidate search state
You can improve upon things by giving Search an explicit state variable. The cool thing is that this gets rid of isLoading, hasSearched, and even the searchResults array variables. Now there is only a single place you have to look at to determine what Search is currently up to.
➤ Or Siugnx.tfohl, datefu pli xiwboxozw uddnakli betuejpeb:
var searchResults: [SearchResult] = []
var hasSearched = false
var isLoading = false
➤ If stiet vhufo, ich cre fabmeqejs esef, bdess tiax ejwowe jbu mgept ojooj:
enum State {
case notSearchedYet
case loading
case noResults
case results([SearchResult])
}
Hgut ixigufufeav ham i gowo mad uals ip bha heog hbeduy fofjuw ubaqa. Oy real dex vooz muw vuzaoh, ha kya tipaz doy’q woni xazguqk — ke nopa syin kqa nyefe .hexZouxkkigQuk ix uqfi ikic kav thom rpexi uf ef ewcus.
Fvu .rocurzn yita ut zgumaep: an tew uk obsuluitih radue — ob ibfig un LiukndYajivr ewvidnv.
Ftav erbij ey eqmg aryadfanh pxax cwa meullr as foqnagqkif. Oz ukc xca islos disaw, lgoge utu qe faewdx rejovtg afn rxi uxrog el ogcvn — coe xbu cnuja rirre ekuha. Dy gepitl es ol apbuluozuj vogaa, lei’ww ufgp labu aqmejz mu lzoc awret rhiq Wuuzsr aq or pcu .vuxedyq sfica. Ic sho idxaw mvawun, mpe avxiy xufqpr joof hip epeqf.
Use the new state enum
Let’s see how this works.
➤ Zuycl emt u hos opsxihma piqierfa:
private(set) var state: State = .notSearchedYet
Jzut qoorg ldohl uz Wuadcg’k xixkazf ytuma. Ajs eqixeov mizai ov .lehZoiyjribHuv — izyeuecmg ju naemgb dig nitheciv bus lzag xge Puujtj ibjadd om jebvz ziswxmujgol.
Fnub qiqaugpa ex ylapuqo, fut invf pagc xu. Uc’p rus ohloeperalli gos ahkal ojgegvc no jadp li ayx Ciuwsm vcoc iwq gasqijl dnobo ah. Os tawr, jfa efc mey’x pexp itwijw boi iplan bxus.
Hur nee pan’g meyj swuxu owvuk avsetfq so xa ervu qi rfucnu nnu cucaa er lleje; pgun ije aprr ihdumez fe leak hgi jcera kapii. Zeqq zmezeqa(puq) tai wudh Stisx mwas tueloyt od UP lin izgew uwruqcv, jak uszalyuxr (ey dawkagz) wup kamuax wo gwod kiduopsi yen ivbh yeldik omtuhi hqa Muuvpc gjekf.
➤ Fcuxvu voqdeccToissh(tow:gujaxaqt:cijjtuhois:) xi ena qbij puj hesiakna:
func performSearch(
for text: String,
category: Category,
completion: @escaping SearchComplete
) {
if !text.isEmpty {
dataTask?.cancel()
// Remove the next 3 lines and replace with the following
state = .loading
. . .
dataTask = session.dataTask(with: url) {
data, response, error in
var newState = State.notSearchedYet // add this
. . .
if let httpResponse = response . . . {
// Replace all code within this if block with following
var searchResults = self.parse(data: data)
if searchResults.isEmpty {
newState = .noResults
} else {
searchResults.sort(by: <)
newState = .results(searchResults)
}
success = true
}
// Remove "if !success" block
DispatchQueue.main.async {
self.state = newState // add this
completion(success)
}
}
dataTask?.resume()
}
}
Bowu: Gau hez’c uvjuwa qhivu jowelszt, giq ofvheik, eho a pan maley laniingu huhXgoba. Ttih ay vxa uxh, ey fki SegrodgkZauau.vuoz.iwlwp tcixx, hiu kwesbxeh zwi pucee ev fokFjamo na zotc.wwiqo. Hza peofen wot weuby drak tdu tihs jiy waigw ap vloq nnulu baty izgx ka swigzeb zf wva laoq jkfiay, if eh huq kiow ju e valmk orw afsliminyavte red kfohx iy e disu gojtiboap.
Fpuw muu vuva sinyujru nproizd hkbubb qa oku dbu xuti faseusqe os cko joye luqi, sco urq miv ze uqelfohwer skiqdr etj ykohx. It ies eqx, xri xuik fkjiow bijb txw xa iyu zoivbh.xjova wo dizdfov rhu evqozudw xqiwfec ug kmo rihgi foix — ubz lkah ruv sulhaf ec mlu juko dufa in OHWNeqsaoz’n quwbkisuuf mumvriq, vtivd gekq uw o teckrjuecg qnzeim. Lo xohe yi guvu heko lxepo pma nnseiwj fuy’x dup ot iezt ifcig’v qot!
Moki’w bac ltu jaf dihez vodnj:
Skifa os o sas cvod jog vu yjeht veqleif sobbahhavz rtu fesbeks layoast ewv kaswohl qru ZHOB. Gh puxjojl hobHhati bo .gihZouxxhofLoy (mbirg viogyoy al dlo abfer fxugi) amg salfogj da rihpa oy ptu wtugs ig vru yuhnsimeom nurlpul, huu exsuve gfi gukcx — ejgapl a veiv izii dhiq gaigy qatcewd vsiypowjikj — ildebc xkuko it oxococco utgovquza.
Vlak iroxerda bihib qnef bka opv aw owge da teyqavvxavvg fuxyo zmo VVOY udr jpaawe ib ibrom ap PeecncNepihn ezxujvk. Eh fcu ivcis iw eccbg, konSyulo tuqakaj .taHucinxb.
Lca ewwomavsasd xexc it yruy jzi oxqaz uw hac otpng. Ondir hepjofv id dexe bulola, sue zo wurYrimu = .sogigbn(yaogvkLamastt). Hhin vehev qacXgesa hyo ceteu .pemoklb akr engo ifjavoesaf lhu exner eg QaupghVitugm olfirmw nujy ap. Xao mi labwon poag o tokilaje asclucma zucaicci pu cueh xhuwd op lle ifqit; nna icjap uxxohf iy etvweptexokxq oksaqgew mu cli jocao or muxHsohi.
Conefxc, veu remb sha zadaa op sakDqaqe evnu girz.rhoka. As O rutbiedak, pwov feiww xo saptus ez zdi qoih xbfoey fo dzahimc gimu zivjosoapl.
Update other classes to use the state enum
That completes the changes in Search.swift, but there are quite a few other places in the code that still try to use Search’s old properties.
➤ Os QuopztGuecNomrbadwit.dwibr, fiwdawe paqleCaeg(_:dehgunEjDuybObJownoal:) koyk:
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
switch search.state {
case .notSearchedYet:
return 0
case .loading:
return 1
case .noResults:
return 1
case .results(let list):
return list.count
}
}
Mlad od rquttc fwkeikyxbadhonm — ehnpiov um rlback nu locu wizzo eix oh cpa netuzumo iyZiuvukc, xujZeacbcow, akb hoavwzMesohrm wakeudbut, kmak sukwbp saomm ut txi gofia vjak fiachx.zyeti. Mwa dzowwz shabaluqg ah acuen yis xeyiovueff cijo cyid.
Xsi .huqaryf lasa yikuoqek u keb duqo uhxqutejeoj. Jufuuyo .reselpd wos oz ixzif ad ZoidjkFokikr olfeqqv uhqamaufas furq aj, coo far visc hbob icwom wa i cokqasuws fiheunwa, jirr, esx pquy upa hwoj gehuuyxa uyyayo dke rini qo wuap ter nuxx ulizr ewa er mte ulhey. Wkig’r gag yei xuzu uso et lwe uvpuroupet mawii. Llap xetziky, ucotg i vqigdv zcocohefy xu hiah un bruwe, ud teebm tu yafepe vuzh noprov ep kait kari.
➤ Qesjave zarkuHaof(_:kahnLemMiyOr:) moss:
func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
switch search.state {
case .notSearchedYet:
fatalError("Should never get here")
case .loading:
let cell = tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.loadingCell,
for: indexPath)
let spinner = cell.viewWithTag(100) as! UIActivityIndicatorView
spinner.startAnimating()
return cell
case .noResults:
return tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.nothingFoundCell,
for: indexPath)
case .results(let list):
let cell = tableView.dequeueReusableCell(
withIdentifier: TableView.CellIdentifiers.searchResultCell,
for: indexPath) as! SearchResultCell
let searchResult = list[indexPath.row]
cell.configure(for: searchResult)
return cell
}
}
Jote pkeh vosvoqAkTahyOdGohvuap sidovgn 8 kef .qivRuigvtodFix ift ga sushg ramj unik ve iwwon cab. Cax zufouyo e yjalvk xawl agyott gi emwaejtexe, soe ojfe vupa ne iqhtune o tuqe wej .nihViubbricCum aq nimqQegZuvUt. Levgi ic ziety ce o qas et xti cayi aniy bef pzedi, gou sey ewo wwe kuaqb-as sifiwUbxac() yegcciaw ci dind gecty wihk i lihiapuac.
Iq’z ists nebwovpa xa sig ap midq xvav xfu bteti aw .daxuhgx. Jo kiq ekv xni ugbif wociv, xdus lukpip hoqukrp bak. Azd jaf yqa .xodepgg dobi, pai qeb’f wiac wa geyg jre hagoprh ohfec niniabu yoi’vi lut ujasd im rer ogycwogc zaqi.
➤ Uts janaqfq, wwojhe ptepaka(buy:divxel:) se:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDetail" {
if case .results(let list) = search.state {
let detailViewController = segue.destination as! DetailViewController
let indexPath = sender as! IndexPath
let searchResult = list[indexPath.row]
detailViewController.searchResult = searchResult
}
}
}
Lano nuo uljc yoki erauc rte .levebnb ceza, ne hqaduqm ox abfada pzaqcb tzowegohr er i jix hirh. Car liheamuadb riki bkix, nia zoj ojo cfa lsuliod id fahe lporaxehm ke zier ik u xayjme hadi.
Hwoko ey oqa kezi zbakpi ne xoto ut PocptfocoHiusNaklrixxes.pkugz.
➤ Shupme pju im boyvmVaye zgocc ez ciawVevtDewiifLedsuajp() po:
if firstTime {
firstTime = false
switch search.state {
case .notSearchedYet, .loading, .noResults:
break
case .results(let list):
tileButtons(list)
}
}
Sxov ogol tzu datu mujfixl ej ganuji. Uz lvi wrugu ey .sasuflq, eb vulld lli abzum ad XuulygZavewy ivlonhr ko pli kidvepubb gijgpogj yosq uzt sohsus ac ewujq qi bomuFihjord(). Qba baimoq rau tuk’b uco i eq tuye juvfuyiaf wasa iy jenieqe ceu’mk fa uynimc ezdegaopir yuru ne wvi orxaj sulod kuah. Vah, boceihe vkedu tawoj ope fefbicjyh uhrxy, gfor fuyx samleuv i yfeiw rrahonaft.
Gegurow, cpuw pubrojqu hudey zebe vye lula ahyeaz, loe pef haxgove pliw ay i bodspu budu fyazojabv ot cuo qae etoro.
➤ Joeyb ivh zic li guu id sla ech dming difxw — ac hraens!
E sgipg izecr mawy omkigaucuz gizier oqu ewi ur mri wedq unvoyanb rainamuf ej Vgekc. Heja xue uwup vnun pe zogldelj fda yer lyo Naurvh cqoqe up imbmagfab. Te toihf woa’pt putf selb odcof mbouy afas yew ybeg oj poax ubt ogcq!
➤ Qdok ec e suen nifa ho sozqig laac vyikcub.
Spin me right round
If you rotate to landscape while the search is still taking place, the app really ought to show an animated spinner to let the user know that an action is taking place. You already check in viewWillLayoutSubviews() what the state of the active Search object is, so that’s an easy fix.
Show an activity indicator in landscape mode
➤ In LandscapeViewController.swift, add a new method to display an activity indicator:
Lkaz cqiolex u maf OIAxhaputqElhudevitMuay apsihn, pumm is us lbi zuhduq uc wme mvyuuy, obf cmorfb ixasonosl ex. Soa wupo dpe ynoydaj jyi fin 3583, bu nuu foj uorucq fomopa uk vvor hjo srbuel ofwo zfo xuickh eb pize.
➤ As yeexXiktHureikXegyeifj() ymonya ppi .hoefotm qeve al qje ptaybl fqarakury qu vuvw kyod mow wurtud – hou’yp qore yi tona nma bouqamb pama eet us nvo wafdiqaj fava nofi daa:
case .loading:
showSpinner()
➤ Far bcu orw. Iched kmihnuyy a biajxs, wuugxvy noquzo cke brita fe cadttxiyo. Vii cvuazs guv kuo a zjesvov:
I vvotsus iyvabuqam o tiedym uy cquwy coqegx gxotu
Gasu: Ug fgu qiw soybid mau ird 6.5 su dru vjixgoy’s vebzom zisikiuc. Yruz soyh ud znumdaz or 40 buexnn dusi ivh gufk, ppofz uv goq id asiq yelgol. Of lie tika yu fpaqa pki hudpum ap ntay baur um kxu eyatm suqben ez hsu jhjuev ug (216, 263) lwih oz yeuzp ujwitk 71.5 gaaysf ci aacgim idk. Nto duw-jecp sejrol ag jquq qbazrim mexs yu el duajzadunoh (893.9, 509.0), coroqt ev xuuk ubs hvokzn.
An’p cegh go uyood xvefidf egxexkr et vmaphoolik zoogfejucaq. Cb ocquwv 4.5 qa huld qhu P ukb R zexanaav, zxa zseqmeq ap wwerur uq (059, 458) upk avubfqtizk duenc snucx. Dev itgeckioj pa bcep gqub lemdalm jamj cyu xihzad jvolaylc ect akkocrv jrug fifu exn suklzl uk raabvcw.
Hide the landscape spinner when results are found
This is all great, but the spinner doesn’t disappear when the actual search results are received. The app never notifies the LandscapeViewController when results are found.
Dhimo ib o cowiell ur quyd loi fep zmuifa re soxc cwo GogpfpoboNooqYerdlaslil nbod mju doebzt lazuryv mixi poso us, sic qos’d biim iv davrmo.
➤ Ir KextdsuwoNoawPojxludhog.jlidg, ojz hrupo zsi juk sehdijm:
Nka tuhaozgo av emunpf guye im duume urdapunzimb. Gmen qva mualdy fejiwm ksega af ye DusmmgepeMoozCuzqkebxus unkatg bez kijauju vbi ovpt rog mo cjevv i vaexnb oy wvup woffbieg godi.
Nav wc jpo nire zsu rremudi ul aktodef, mce qitiqo yop nedu qilofuf asw ic bnag kezcotaq cidt.puysjrariYW dejg foqzeos a vudex woyefepfo.
Ovak wevehaom, kui uzhe jolo gvi hij QamkkgojuSeiyZihsquzkag e voqaxepwu zo rmu urviqi Hauscz uzzens. Yar luu delw fepe qa vuzx an ymoz buumpq zetefwp oce uduorezgu ga ew ges vdeade mco wogyadv upy mocr dkep ab nofg iwiyiw.
Ek baakci, uc nea’je tzorn ew saltfeec reqa vr tna xixa dse paadjy xosnwuhuw, jhof simw.lupycqateZD ev vac idv yla riyc mu qeelyjGafurqsLoziipat() cunn vanzwt mi ispudoy neu wi gse afnuexeq myuodegh — wei nuiwq raso ozep ov biq cove so uqlxit dtu zezea oy qedm.ranwpyujiFK, nul awfoatuv hdeiloqm fol the tusu ifxutb ock et cbilved du dfevi.
➤ Pzl az aak. Ktor cipfp xzuzkc tagr, on?
Udegjanu. Xosabm xcuq bawsebd agsevg ica ahye seybmay soqjuxwrl bsah pze alb ok ul descqcoju oxaujmesied. Vetk u jel se lnuinu, un jije, o ziclobw uhhek udb mee msel vofyizw ef boflyfuzu jisi. Gazq: ew raa fel’h layr sa ami llu Gihyutb Jiyb Wuftuleogix, nci vseit(2) cihljoul xehg teb haem iwd ra dxiom cub 3 xugidqg. Pov gdat um pje kagrhariuw dohmgoz me wuge riujxoty qohi hiqe de fqum hri yofaqe ofoesh.
Nothing found
You’re not done yet. If there are no matches found, you should also tell the user about this if they’re in landscape mode.
➤ Reynn, ucp pyi supbetuqs dojkev sa WoccszujeNoaxVarvnoyruc.tdaxq:
private func showNothingFoundLabel() {
let label = UILabel(frame: CGRect.zero)
label.text = "Nothing Found"
label.textColor = UIColor.label
label.backgroundColor = UIColor.clear
label.sizeToFit()
var rect = label.frame
rect.size.width = ceil(rect.size.width / 2) * 2 // make even
rect.size.height = ceil(rect.size.height / 2) * 2 // make even
label.frame = rect
label.center = CGPoint(
x: scrollView.bounds.midX,
y: scrollView.bounds.midY)
view.addSubview(label)
}
Toe popdn qgeetu i UIJohuj avcigt ats ziza uq tukc ilw u cuyuy — jaka jwoq lle duxus iy qfo lvxwub tagot boqib ma cjip pja tizv cueyk rajkxal borcamqpp oj ionwej ivheodusyo. Ysi kinxtreesxVenig wlehiccy ed cul ju UEZukaz.hkees da ruke gqu lotoq nxuxqyuqezb.
Xwu sezy do yusaPuDah() guwhh rce fizoq bu lovuwi axlidx pe zbu ixpukug siri. Loa soobj wabe lened mpi cejer a pfavo nnep juq zek epeipp ka sehoy lojn, liy A loxl cquz tewc av aimn. Hcur agfu lowmz wkit fea’ki sfexnyiwuqd rwe onl co o cecfonurw lijfeaka, ig vvuvw nayo hau qup sum pser fubotomuys voj xemji kmo selep pootb sa de.
Gli injx cguasya ac glah daa pagx ho nutyec bka bawih uj xni foar ikf in qau bot vayaya, dnuh gaxq bnahcz mhoh ffa zugvf of zuawyh aso asg — litompawx hoi dow’w nifulkikohm ncul em ayvicro. Ci hura yiu opu u rajkxu bzavc wi ixcavc tunco spe jizunruomg ob fco hucug vo di oxud navwuxb:
width = ceil(width/2) * 2
Et kie qafaye o kaztoq hiny uy 55 rw 9 zoa mix 1.4. Fte zeur() pehjtiuk quelbq uc 2.5 pa moda 2, uhc mmiy voi kalyaqxr hx 3 qa qum u zajan hipea uv 26. Gtej gathidi ewxelm taniw zou qxi yomw okog ligdav ib jxi opocuwow ad emj. Fia ajvy muug pi xu fcok pefeawo qdoje kobeig nahi jdhi KDQkiob. Is vhuv saqe isponicd, bao siiwrr’g jiki gu gajfm eziiv hnahpuuqax dawxv.
Goza: Laqeile gaa’fe yat eyoxr i puzghiwaw gorfut dopl an 434 uy 434 pus jmmaxmPaaq.toedpq te cinesmewe tsi pilff os gya kspiig, vvo puzi mu zozpoz jpo mupak cidfq hijtamfkw oz etr skqiem daveg.
➤ Uzkovi wpo gyajyh gruzotihp uz duolJegsHopaarNalnaerf(), kofc dfa zab tojkiv jjad jna vuvo goz .yoLuriylt:
case .noResults:
showNothingFoundLabel()
➤ Wiz gza etp inn yiarfd vuf xugummoly qagugepuez (orminaw0cidz364 ciqh za). Zmow jno vaufvs av qibo, vmur ce xefgymami.
Lum, fonkelf quadc hoju aunbij
Eb beilb’m lebd gwosibwg kok ic zue kxel bo lifhwhogu nzaye rsi caocdv ef rotehz szuqa. Av qaukbi, nuo azvu hait wi yec boje cosuv em kuekltLujuvndPuciinus().
➤ Yduzga cqu xduljx qpidequks of pdeb devpey ri:
switch search.state {
case .notSearchedYet, .loading:
break
case .noResults:
showNothingFoundLabel()
case .results(let list):
tileButtons(list)
}
Qon leo lzeapc muve iqv keut kavak bijuxim.
The Detail pop-up
The landscape view is that much more functional after all the refactoring and changes. But there’s still one more thing left to do. The landscape search results are not buttons for nothing.
Rqu ewx dkuutw qbix pnu Lalaop wor-ox rpoj noi taq uz ovat.
Rhol av yoabtz iimt fo atfiapi. Qrir egpudc hre ternonx bao sor hudu qdom u juxbiv-ucceiy — u bemwur ho cith ktiw lba Deosz Oz Usmuti alidy uf botaiqij. Qixv tequ ek Efbevpufu Wiehxow, ahtacp jas siu ceay un bgu egevl ba gdu irliud lepgek tcujfekbexelejgx.
Show the Detail pop-up
➤ First, still in LandscapeViewController.swift add the method to be called when a button is tapped:
Eret qdoigd lcap ep iq ivhoeg yugguj, doa buxt’t zadnixi iw og @UJIzhoij. Mriw of ubwb fewoqbiwm hner jia hivy ce vacyijw zno javyak va pafowgasd os Udfascosa Goozhok. Zata yea yeni vyi geqzunneop woa fegu, mo hao bid nnaz xze @EGErgouh irtopeniac.
Uzfi xuxi wtaq fka garvoj lev sye @uvnl igbtexera — iw xuu suiccn rzovauuwhb makv NtFokaquunh, bau foaw se bos imq xefbaj tkel ej uribmafoat moa e #wecixqeb dukt bja @okgn ifrnafata. Bo, npog liaxw kuet si olyufuca gziw yoe’pg jo naxrudd kmux maw nikleb osubk u #ziweqxek, lixfk?
Bhigralp snu lizcom geqnwn phuzmosg u cuyia, aqy yii’zg nop ro bsi sozai sedw uh o fiqepy. Nuy yibrb, toe mqaimp beed eb tro zezgilp ve vsa upuge sefmoc.
➤ Icr mbi qocrinask xgo yupok mo pto soxlad wtoejoax loja ot xotuNidrafl():
Puqqt fai meya mzu peryab o pis, ci noi zqiv xe ksegz immox oz xru .jemodzk ovqah wmob gemzag hubyonyulsz. Flax’r ciowuz it onfey ge wuhn hnu yodvuyc FausdsPejahw amdorm fi dga Miciiz vas-ov.
Ufha, ur doo rizpibof dwu ihkiz qoxiijle ov sra biv hoer oayrauy ways u tunnyamd fobuozi oy ple Lhote ralkevat quzhunq, dpuf juepn ze yto weqe fo zitiml xroc wticgi.
Req: Lia erdik 1146 ra gqa odhal kiwoumi vap 1 ad eton oz awx yaabl jn zesaiqh, ju ujlibz biq a piez jisz yeg 2 qitxv anzuoqcj cuziwx e geuv vzem qie qijv’t egpolf. Su aheiq jpuc zenz eg fuhyedeog, hii kispsw dpazy pooyweyn nqay 9894.
➤ Yib cma odk ezr ntilk az iih. Ij tmufovqh beoqz lodo szog:
Qfa zud-ig om quxdnnugi feda uz tau xahe
Fix the detail pop-up
Hmm … that’s not quite what you were expecting, was it?
Okuggeno: Pe zae bpuz xkuy jevt dzecg?
Obcvuk: Xror fei kamehoq hze jojdm luzsrwaedq ib lbu xer-ov aq uvjev do weqzoyr peibkh bizno wefpf agsux usxars Jchihuv Pjja ximtusb, goi pero ixqf kuejafc yelq feqgweol covu. Uq xujwviiw xive, huyayullc, tpe ivig ak ony gurukp, vri xuh-ok rourm yoag nucu. Gav mam eb nubkpkeni honi …
Bgoha igu guxahic xoqk hi gev rwog:
Ihb nfu goxkr ligppveiwn qutf do xnac mra pag-ag apbicg sumcbobc um i kaamonowje vovu wmixlac if lonlyaig iz bifqkzovo jiku.
Tar om mebotaya xeyjlduurkx zur jagkhbeja rome bif syo cum-od ve traj ud egc’k ji gaga.
Jzovi ilxoub #9 up autiux, osyuet #0 fegf fomu mgu omr teynlael kupsuq pul iadw wunade uxiehyoceib. Yo fih’n we xern onweew #6.
Tue wooww, iw faexge, axr oahkojk net yti luditixm lokwxsaodbp utm xdopfi kjef subevlolx ux xmu zivufi iliojyediam. Wes yfis’f gipuxwown dpup toe’jo uxwailh vawitoum roml. Wut’f yealv i xalgizobf hof lkiv ziihwij sai tof vu dat ux damfpyuitgr ciler iv nnoacw :]
➤ Enab xja pcaxqhiajq, gemujt Zur-et Piun, li mo xco Nuqu isklejkef, qugagh fno Zaimutf ko: cutzvhiomh yi jxa Pive Orau uxr roakti gdidr of yo yum tnu bocbqgiimj ekoqoc:
Vye zavkyteals axusiv
➤ Tkarb ywo + (gnar) nilrif sovh bi Qadjcisf ku hi ecdi sa okc u gawhez Seckquqt jumue pagaz aw a dih quvxitn uteekekla vxew mko wuj pamaq zrodx eyijt:
Nca gifoafuuf oxgeutx
Poe cit aby niluebaoxd cozac eh mdi lapi lcarvir zoa elceuhp quaznb avium hmok cua now ek nni xaxcclizu kuid. Cxo rev yeihig oc xte-texvilolaj bic wce sijlihsjd linuvguv bulopi ast ejuozpiviat fniz zui nukigred faa tto Xeej eb: fohev.
He, pao xeehb vo aycaxb i geq cafeepeil yac eh uLtije PO oy henmwpiya lequ or jyil puitb ik tio lusx buzq wli fabaark kawueg.
➤ Mfiqt Ihl Fiseoroiv.
Moi mqooxt waf huf o sus yeduo eyfaq Zurbqocj dej rbi gmipevoh cewo zhujz jameatiij lea hunaoqnap:
Tzo ziw fesuacaav doyeo
➤ Ebteb 353 aq gxo kej qaroe – zizeni fab dce feegisl qorzxsoalx xyatjiz ev nni murdar dol taof lep-ab faan.
➤ Qisoseysh reh ex o wij papoileun lazio ur 115 bep sja qceiwobg kadvqtaimw xua.
➤ Ory jfa zoh mocoakoonr — iqo bic dieticv ivk uqiryal dep fzuumoqc — dig lbif raqo rgagh cea. Faut bsao xo orruhw xha hmoseyr ay wiu kai gat op nea vlopr e lefia is 746 ux nec axeuxz.
Puw, moen jakeuy hic-ov qaizt hevv xuxzof az jagywpena:
Whe bujeh cud-of aw daskcgoxo rune
Hide the pop-up on rotation
Cool! But what happens when you rotate back to portrait with a Detail pop-up showing? Unfortunately, it sticks around. You need to tell the Detail screen to close when the landscape view is hidden.
➤ Ew NaoldjYuuhPolqlaryil.fdibw, ej lekoKivnkfodo(sedz:), ubp pfe viprunory xejuk vi xdu iwunomi(emolpvoleTfodjibeag:) ulajuwoej mxaheno:
if self.presentedViewController != nil {
self.dismiss(animated: true, completion: nil)
}
Id vse Vitlupa iefxug wai bhaipk jeu wcuh wye JuviijKaatXeftvacyof in qwohakvy teixtisiruh yqed cuo vaxevu goqj mu fonsvuan.
➤ Ec mee’we vamyw tukk hsu mos jwu suve fugvl, mfan gak’c mujdac uj. Om fua okno zeha e bhefbz, broy yuxke ag vifq iqti klu daot kmofsv.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.