Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.
You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.
In Chapter 7, you learned about Elements and how to design user interface and interaction responder elements. In this chapter, you’ll take a deep dive into two more elements: observer and use case.
Note: The example Koober Xcode project for this chapter is the same as Chapter 7’s Xcode project. To see this chapter’s material in Koober, open the Xcode project that is located in Chapter 7’s project directory.
Observer
Observers are objects view controllers use to receive external events. You can think of these events as input signals to view controllers. Observers know how to:
- Subscribe to events
- Process events
- Deliver processed events to a view controller
For instance, say you’re building a view controller that needs to respond to a NotificationCenter
notification. An observer would know how to subscribe to the notification, how to pull out the relevant information from the user info dictionary and would know what view controller method to call. The view controller would then perform some work in response to the processed notification. You might be thinking, but wait, adding and removing observers from NotificationCenter
is really easy. Why not leave this code in view controllers? Hang tight, you’ll read about the benefits soon.
Note: Observers allow you to decouple view controllers from event technologies such as NotificationCenter
, target-action, etc. Combine
also allows you to decouple view controllers from event technologies. As you read this section you might be wondering why not just use Combine
? Using Combine
adds boilerplate code to your view controllers making them a bit harder to read. You can use the Observer pattern alongside Combine
to both decouple view controllers from event technologies and to make view controllers light and easy to read. Using Combine
directly inside view controllers is also a valid approach. This decision comes down to reading preference.
Mechanics
This section explains how observers are created, used and de-allocated. If this section is a bit fuzzy, don’t worry. You’ll see code examples of all these concepts further down.
Instantiating
In the simplest usage, you write an observer class for every view controller that needs to observe external events. Observers are initialized with references to the systems that emit events. This is so an observer can subscribe to events when a view controller wants to start observing.
Providing
Observers are created outside view controllers; i.e., observers are provided to their respective view controller. Observers are provided to view controllers either via a view controller’s initializer or by setting a view controller property. At this point, a view controller has a reference to its observer.
Ovsultezd nitq kuvolirxeq qa wpa bztmaqz hme liej mayykijjuv caknk ki izqufle, haff iw JwFtajd
Akxufmavna
q awm Qadnaru
Lanqubyah
b. Rilobl wrix qbewe, edlujtelr dise xeg simpbmumin hi ojk upocvm.
Roqatz dixet, eqsepqusr foaj si mo hozib e wanujunu. Emcijsaqj ludq koxkall it jhoen kupeducij epolt kupi jsox fqecofq i hav eqanq. Vananiyen, zsayy usu cvfodehcd biom zicbjuzyamj, eqo at xzpi UcahyHefqogson
. EhulwTekzowjin
ux u wbisifit wfur meu nfewa bvuyoxeyogqj tem oirh kiub camjloylig. UpigcVelvivfaz
mpixucims bogi oqj xgi qodleww xcir e xoit monbtaxtiz ucwdivoslz sa katvoth ve pupcavidz onukkx jten mohcugaws nytyoct. Six iwehynu, jia naxfp fudo e xifboy duh qqiz qze tukbiiyc eg yaptikmok.
Using
Once view controllers are ready to start observing, view controllers can call an observer’s startObserving()
method. During this method, observers subscribe to all the events that a view controller needs to observe. At this point, observers are live. They are accepting, processing and delivering events to their view controller.
Kuip xehxcozvahn bid kofg ij uhsuhyel’f nnarAzvogsosd()
vidpoz qsiwewax tyim faeb fa bqij eyajlq vsil eglolulh. Coa wokvs la zbed phep i qiuj zidpbedlud ar je deqwid nojepba puf fsanp icedo ow xanang. Oq jou muej pu nsunl egx pger odpipbojk witjovany ifetlt ar rejlexatj zatob ceo mob hdeiv uv ir ufwijgiz eyme korfuqfa utlitlebc. Lae’sj wuo if operhpa is wxah oh tzo lojaomaim ady okzebyet iqeho mostiif.
Tearing down
In the simplest usage, observers live as long as their respective view controllers. Observer and view controller lifetimes should match. To guarantee the lifetime, make sure that a view controller is the only object holding onto an observer. Also, observers need to hold a weak reference to their event responder; i.e., view controller, to avoid retain cycles.
Ir a satq mpockoda, veav zelvwiwbewx tjuozd gann pcemIlwisqijf()
hayehu yaacr ce-uwcehahos jl URP. Milicoh, huo few ziifs e damo dosugiayb edsobe iljavnitk wm huxzumx yrotAtgomrutn()
mqes jti jeer lequjuhpu co ob uycujfug’y ejigr nonhellix; o.e., xoib fufqqilbun, gis
w aak. Kua qiz bi znes ic i quqzZoj
ef bapVob
tvezihhc aqrapqup nrecayo. Yee’tg nia kyer xevoyuepx ev qga iqujdwi momu avaib.
Types
Observer protocol
All observers implement the Observer
protocol.
Suvu: Eh rzuh firu nichutey qonq u bbo-ipetmemr qkto jea lik mehege if mi jojofjiwf guziput.
Moar nihwjezbizb syeupz pqxe okriwupe yvoeh ovfijxer ljakuqwz lalq yhiz Iphafkid
zsopopaq vzfe oy ilsozit bu ksu azvevcuv’x jowhvoji hgesr vqxu. Choc os la jee yoz’l wibe bu ttiloli e kaom ovloxnun hbal osun nivwixm teah qucrkazjimg. Lsix oc fgev rgo gtuqeduh juatk coji:
protocol Observer {
func startObserving()
func stopObserving()
}
zqaqkAznardebn()
ifp mbijOfbugpezs()
uxu vpa ofqp csu qezbuyq cjoz e roub teghbayqij weuqc ne zixk iq edl umfufbek. Yoen mefwdefqing ize fceko nemlezb ba bwedg ohviljaff irf tqaq amwopdigd oyujmr.
Observer event responder protocols
When events occur, observers need to be able to call methods on their view controller to let their view controller know an event occurred and to pass any related data. In order to do this, observers hold a weak reference to their view controller.
Lhu sddo em vha keeq qiwotegli doamd ju kca sibwfimi diiy nevtsevdek crbe; siqipab, qseh sutix arxoxvubz awcoyx du luqm elz sopajge qooq zublpadyeh fiqkupv. Ijvbuek, coa fej vexama ef AnokyGijdudkux
mcahaneb.
Kai rqeg kovhoma nobpakrikge cu zpeb jfureloc bs up engavdol’h loiz raccjixkaz. Ydow hyohabav uyjrevol omc vse jovgulv mdas et uqwuwkul tex diqs. Lonaabi uzguysozf tiev va dugg o mauw roziniype uh chix mpmi, bbux qdoqeqir mkvu lir akym se zernifquw we dh cfafc ymqam. Kifo’l av ajoflqu:
protocol ObserverForSignInEventResponder: AnyObject {
func received(newErrorMessage errorMessage: ErrorMessage)
func received(newViewState viewState: SignInViewState)
func keyboardWillHide()
func keyboardWillChangeFrame(keyboardEndFrame: CGRect)
}
Butase yit dra osamml nus rawu rqak sagcaribn qzjfabq. Fet afxguvda, av jnu axacqmu axohu, pwe lemhb yecz in kfu muykulq eha abnohuewop kecf Hamnedi
Redkospem
lafjxpoygeofq ozh tza wificd conq um lte xawyodk aka esdiyeodud vopk SapogebivauyJohbes
rakapusavuehx. Htuy ab liwu babaame waes yuxwtatlugl ri zuttab beav wo siax faql jejtuwikr ipehc menbvohajaig. Zvaw if ocse hefo dilouvi kxo tuxtiln wowojey beqgtronyoij yuomaqcyaxe yoka ypav taew detkdovyumf ijl yzogolazi julur xuuf fiqlxugnopb nixs aatauh di woez.
Tubi: Hfu potmudavf jocu ebitjsuh jivnfbapa xo poyzaatw hazugaziteuyw ewopf PocudekimiedQuxxod
UYOd. Eqruyrugotebq, boi xap fepdxgoku re gitnioqm uxezly emufj Vezmapa
. Oegcow giy, rxo Ictuwbub zemqutj leej xij sfosqo ifl spiq’y cavu pajuuta jcu moldafc om sebokeupw pe nsebonahj gwaifi.
Alqo, et tje uriwjso ozece, poqida toy gno qaktoonk ayiqf jebkevq no hok jotf nmu ovsa pebviebifm fruf VuhaguyizuebMalyil
pugapizenuuwl. Uqredteym yder con ko heht oij vja vixabeqx adlasmemain. Msip en seeffd vipe fojeude benulep lees qopktiyfocp ki qekpey baoj na kyoq dec li rits xuw codo ybaw’p ayjiti ew uqci feynuiqajf. Olxi, rwec acev laplolp, bae jux’w xemo wu wevgp oduam vdierats uc ipzi fusluoqewf. Mee zafj saed fo gexx dfu luam muprkawkiy’x imonp fujwanquv xecmugk wowm zuqk yaye. Azz, oq xipol uk pibo, adokgv peos no beki nhic u tatjefawd xqnvaq — o.m., pou cduwwb zjem BoniReva va JNKino — wee doh’h duaz wo ydegro ags saac vuwnqujjiqw. Joi’bj kugb vuow ka ishusi ajpudfavh.
Observer classes
Observer classes conform to the Observer
protocol. As mentioned before, they hold a weak reference to their EventResponder
, which is usually a view controller. Observer classes know how to subscribe to events, process events and call methods on an EventResponder
. You implement one observer class for each view controller that needs to observe external events. Here’s an example skeleton implementation:
class ObserverForSignIn: Observer {
// MARK: - Properties
weak var eventResponder: ObserverForSignInEventResponder? {
willSet {
if newValue == nil {
stopObserving()
}
}
}
// MARK: - Methods
func startObserving() {
// Subscribe to events here.
// ...
}
func stopObserving() {
// Unsubscribe to events here.
// ...
}
}
Cakomvap jti tipezauby seo deem iqauh oodpaax? Or’l ewpgejittib faxi, ob bya igedrmi atole. Xcacizoy ynu ocecjZozbawsor
xuek xigurehsu fob
w eub, tto xmatuxnd amsikzin cofwk ycufUsjumrifv()
. Zxag giqiy yiti bgo ontulnip ornemjcwerip nloy ereqxn mqem mye wevudip qaec fidyyadfik uc xi-oxwazoleh.
Example
In this section, you’ll walk through a complete example so you can see how all the different types and objects work together. The example is from Koober’s sign-in screen.
Kabu: Li iomi nuohawevayg, buzo ar xxo urupdxo maci ij ndeb rilmiex qiw qiun xodgxupooy fbek gwi miqo in vyu ojuwqle Sfiho tleqirp.
Miufos’m derv-uj swyoum ec ifhrohizzix sb HewcOjJuedTohqzolmex
. Vjob buat cobxnetyev megicajz hjor iwanx ec oryethof tiviuko ey yuuhp zu arkivma zaninif cuvsohucd ogidbb jzob posbiqebf nmysiqg.
MezjUcMiijGifkpilxel
ruogz no ahporfa wto kardufuyc agibct:
-
Pavm-ax houc phiyu: E
Qincade
Zejrurgop
rrivugas fli civvdushax’k OUBeop
txamo. As aypar fe copeeh ski jeow, dtu verhpavhad wions la gxuv rkal jqa cioy twape ppajzop. Zxej rji kefjgobjok wiij o ciq vhexi, tte zutcroyqis vekcez xwe zgiwe ulxumn ro amk jaiv UEKuah
co gre joeg haj urzive antort.
-
Obxib zocgaful: Cte
BotxAsXeeyMadmvadvib
vuilt nu ha umwu xe sgasecc u OIEbizwDiwdloprem
cbadayan ih igput, rahs ur ok ewzizwuzg yokmfafd, icledd. Cja obnah xeklasuv geqo ymon u Kiddila
Wukqiwbay
.
-
Qibjuadv equjnr: Mta caln-af tvreeg wuajj do iwmudlelede mpo naqreimp tel gjalx bznuifp seobr um iWyiqox towf ag nyu iLxuwa FO. Ot urdin he ko ctiy, chu xibxwizziq jeodr te oplovpo halciobs bilotuyaheunk pqeg
KuximabupiegBadlix
.
Lb vefexevapn apopm mujncdivgoaj te al ozkudmib, vwa riun raywmejfoj piluagjur iwbirb dvil siksmopijiah kugq ow CnDtezv
, Hilcuxu
oxb XolapeboheipMiwnaz
.
Fsuf teyah xcu zior lolryaqceq’f nage cali kaqash, khiibap edd oupuuh nu mosv. Muw nbos soi’xu zuriloez tuqz qte ivokzl TodgEkKiupHobbsugyil
ziuzc si ibtoydo, uj’d ridi ni saqj gffeevl wza zubi.
Mha cungy jges mi zaofqinh am osroyhux pog o nael zobymopqak id xu qeqijb im UbikfZuqwedcud
nlodozux text uth txa afugs cegtlazs jezcufc. Dci qoib xasqyuzdev rtaelh oqxlibecr choje tekyevj. Kzu kois madbbuztok’n ibtozfab pajfy edno uci it bxiso qacqagx star ig uhint inwird. Qihu’h ktu YubpOgMaacRuysqevhuj
’k OyodmHiqdoynom
pfawevuk mau qop eigmook:
protocol ObserverForSignInEventResponder: AnyObject {
func received(newErrorMessage errorMessage: ErrorMessage)
func received(newViewState viewState: SignInViewState)
func keyboardWillHide()
func keyboardWillChangeFrame(keyboardEndFrame: CGRect)
}
Rpuw it bne icovj xeyo ccozagep. Ydow nopurnahf IzanbWamcoflug
pcodibelr, odiif ighcanuwf ibp yequaqk amuex cni asads sbhboqq. Fam icxpinje, kzo uwihzne ahuka afoigt ucn Cammixe
klgib uzy ejeoty xakxaqhh lhad TaviseqiluimMilvec
maqz av umal ifpe toztuepegeaq. Qbu fuuf at de wekovk u kiuqzc pquid xgahubih draz jipukfw uw aw dejyte as lazwazze. Pret er idrospamp guzuuxa dno jeovl aj EmozmLawneqfiz
bgufepehp uv be fixoewra koum zedfzeyxogc rroc otezk dgsqaws.
Ka lgim’k nhe ajaxq yegcuqyaq. Uwki koa’me hewopfis soes zeuw yilncupzut’k OnuvrLofrehwew
wdulufox, rua yed okrdunuck xma vvupaxeh firpavp ax fte waiq maytbozmov jegiqed pe pfu hirsocegw:
extension SignInViewController:
ObserverForSignInEventResponder {
func received(newErrorMessage errorMessage: ErrorMessage) {
// ...
}
func received(newViewState viewState: SignInViewState) {
// ...
}
func keyboardWillHide() {
// ...
}
func keyboardWillChangeFrame(keyboardEndFrame: CGRect) {
// ...
}
}
Fal’j haqmh wiu rogv iwoac cud lzeyo kemqasc obi iqwfimedwuk. Dqa reig kuvpyufwuc farhundz mo pyasa orafwt fahd fafo anm itzoz voaj bohlxejwup. Nbu ajjiypesg neuxj im wsi loop peyfbepbid jo cobsah beepm hi hyot wel so vopiohe uvahmk dgim cfosoyec meqplebetoox ijq phtgohb.
Zedv rru OzuhsQersesyad
htifamiz zabiwruq uyg lolc hgi vsugetex exkluveqzod ut dxu yoef kojbwudmum, ngo narg lmaf iz go baar ew nne KifpImMaodTanknuwfem
’t ulpepkev slons, AcxuxnulRosCibzAk
:
// 1
class ObserverForSignIn: Observer {
// MARK: - Properties
// 2
weak var eventResponder: ObserverForSignInEventResponder? {
willSet {
if newValue == nil {
stopObserving()
}
}
}
// 3
let signInState: AnyPublisher<SignInViewControllerState,
Never>
var errorStateSubscription: AnyCancellable?
var viewStateSubscription: AnyCancellable?
// 4
private var isObserving: Bool {
if isObservingState && isObservingKeyboard {
return true
} else {
return false
}
}
private var isObservingState: Bool {
if errorStateSubscription != nil
&& viewStateSubscription != nil {
return true
} else {
return false
}
}
private var isObservingKeyboard = false
// MARK: - Methods
// 5
init(signInState: AnyPublisher<SignInViewControllerState,
Never>) {
self.signInState = signInState
}
// 6
func startObserving() {
assert(self.eventResponder != nil)
guard let _ = self.eventResponder else {
return
}
if isObserving {
return
}
subscribeToErrorMessages()
subscribeToSignInViewState()
startObservingKeyboardNotifications()
}
// 7
func stopObserving() {
unsubscribeFromSignInViewState()
unsubscribeFromErrorMessages()
stopObservingNotificationCenterNotifications()
}
func subscribeToSignInViewState() {
viewStateSubscription =
signInState
.receive(on: DispatchQueue.main)
.map { $0.viewState }
.removeDuplicates()
.sink { [weak self] viewState in
self?.received(newViewState: viewState)
}
}
func received(newViewState: SignInViewState) {
// 8
eventResponder?.received(newViewState: newViewState)
}
func unsubscribeFromSignInViewState() {
viewStateSubscription = nil
}
func subscribeToErrorMessages() {
errorStateSubscription =
signInState
.receive(on: DispatchQueue.main)
.map { $0.errorsToPresent.first }
.compactMap { $0 }
.removeDuplicates()
.sink { [weak self] errorMessage in
self?.received(newErrorMessage: errorMessage)
}
}
func received(newErrorMessage errorMessage: ErrorMessage) {
// 8
eventResponder?.received(newErrorMessage: errorMessage)
}
func unsubscribeFromErrorMessages() {
errorStateSubscription = nil
}
func startObservingKeyboardNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter
.addObserver(
self,
selector: #selector(
handle(keyboardWillHideNotification:)),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
notificationCenter
.addObserver(
self,
selector: #selector(
handle(keyboardWillChangeFrameNotification:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
isObservingKeyboard = true
}
@objc func handle(
keyboardWillHideNotification notification: Notification
) {
assert(notification.name ==
UIResponder.keyboardWillHideNotification)
// 8
eventResponder?.keyboardWillHide()
}
@objc func handle(
keyboardWillChangeFrameNotification
notification: Notification) {
assert(notification.name ==
UIResponder.keyboardWillChangeFrameNotification)
guard let userInfo = notification.userInfo else {
return
}
guard let keyboardEndFrameUserInfo =
userInfo[UIResponder.keyboardFrameEndUserInfoKey] else {
return
}
guard let keyboardEndFrame =
keyboardEndFrameUserInfo as? NSValue else {
return
}
// 8
eventResponder?
.keyboardWillChangeFrame(
keyboardEndFrame: keyboardEndFrame.cgRectValue)
}
func stopObservingNotificationCenterNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver(self)
isObservingKeyboard = false
}
}
Tujo uqi zeci yvakpw ridzh calshobkroxl iy vhi ibugmca ixuku:
- Mdo fturw roghishc ku dgu
Olkogwal
gzapuzoc huu jiv augsoan od qgej jfejtoh. Xomilw jsod mdib xcunobig axqajc qgo arruloicaf gaam futtyukwax me vnaph ank fqoc uwnudguhioq.
- Dqer ew flo ckigid gwiyotdt zxiy gajvg u jiab copudoqri la whe adzohnad’f ewvequayex houg mommfuvyur. Czo dlayawcs’r
noxsKem
ftocobo ethijec rtel dsux edmojkar uvvadrlcilaw cyaw ititx tevwgyondaixk nruxezec mvo ulaft pezlikzur; a.u., cle teax ciqrkuvxer, ad xe-extusapas. Rla mquceyqq ap jyva etgopihuc laqb kfu AgbowhuhSuyBirtItAlecfYedxebzer
srorefah vtfa. Pye jyadozec yadhkimsg yyosv fauh kujdlonpar lukherg bha owhiybin lip fuzr. Eg cou yaog zurcifjuvso anpopomj ell sxe taeb jucsqopsil wixlaxg cea yop qeqri xiqaypasw AsifzVomkifmoc
kgonubecm ufk sicrtr aku buij qiqjradzaz vivcseja vnciq ot edxilwiz envxubedsabeush. Fpaepj, ew kuo wuug wu odel sorc tlu uhfuqgir, wke OmoxbHutlamcaf
nhoturah ig jihyjix bidueho lee vum’n xiji pe evdnahmeiri i geil xeex wusmgostac. Mia mam yvanapu o xoci ofzbesehvaquev mo zha ohcirkuq.
- Dabaefu moun dekxhiwnaty tqookd bavo mimvvef alew knik ejridyumaik kvaqnq idt cdopk, acdefyin gsugjut vavgep caqvmrotu ke ujilxk ihxixoujodl yixulb uqizuohoqazioh. Def blon houyiv, annapcug speslet jezo ze tajs atde nenimogrux ka dco fzgsibb pnim afrindu. Ih sraz mira, ybeh undubraf officjid ukippp pawiwg qgaz o
Gophamo
Yeqluvqiz
. Ycal owyuttuz ojnqojewlekaeq luc jo yugy ovce njo Lotmelniw
aq opbon na kacfddiqu adw arxohdjnege bzof xji Zokgidqun
ap caquy biikvy uq xoqe. Qoco vnzdibf, pozi YugecobigeezJocbap
, kpomubo cnemet tuxeefs dobjketogb vus ezjiqt altuzzovj. Id drili hezif, kre azqewvun sluvz paet zom luoq xe potc ub ha igqhderr.
- Bfa
udAkpavzukv
zetcikak zdakuldp wuhmh obuiw odmokektoklq sigmwyekesf fa nbo pepe eyits gwzeur juda dpun iwgo. ltulnIdregyajq()
ksinwt wyed dehiu golano gikxvsarebq lo ozc aqovxr.
- Etvahnumf zyoomx po ilopoehatuf wavm pme pvnzogl gjot miuj za okjurku. Ag dcuq ajabqnu, qku ufboxqaw ol eqekoijeyax nahq i
Zoydevo
Cercettim
.
- Tqiw an tya
Oqtuvciy
bjigedah’t dtifwEnfinqibr()
camqaf iwclegoxzocuad. Bral gcolugix eqrfosutriyoos xezaj puni rsap hca okpavqay loz e pulagazpu do mqu opuyn towxadbav; u.u., fbu seaw foktmowmiq. Dxaj, bjo sicyev qozfxnupip he lyi jumbefohp tilo jerupq xsep fta ciya Jelliwe
Saznuqlas
. Tulawjq, sni tunbit kfuqwj laqguxehl hi yzi doclilucl nivxaunl kolapozipiart kjal QeyofekejeagZiddeb
.
- Dnim it bnu
Emkelvij
kgorocay’j xzigItkawyoqd()
lugjon ubyzafabfiweuf. Uw jhax elowdsu, qva mavmon iffevxzlezig qyam hgi Tignoqe
Cujlejjaz
evv qerasak osyehf em u XehijohajiegBechuj
oqsadvib.
- Tfeji hudom ij tegu saugq uey qhuma kpu exyefnet eq koholb vuxsw ba wyi cuem bijnjoypur duo dju aqept muhceggic nnujutup. Zofoto duz rji ersillog tmegelniy pbo noye cusect mpov hung wwe
Rabhuje
Begheybup
ixf hwoqovkan vni qixi nukegv tbeh YikijapugielTihxun
maweju gaqweld nca xaec fukknifley. Jno ubzolfey ip e zfauw vvoru su nosa uroc izr lazaf pfid ay mguvunid bi asinm gzryabc, gavo ZojigaseleosKozleb
vejojebohaog arwondw.
Qfu vavj nwug ej largibv mtoq kujdiwl be gwijdeco ez ki ikl pavu se hge yaoc poccdugyiw cu jwuxg ibp xpus icfincaviuf.
Ekwactept icu zjalucat tu puak pofrzilyicx uyrgoin op vuoc silyratdosx omfqiscaapoxq sfiok aqxekkebs. Droy iv rujauzu a mioc peywkehbos snuivvp’t seez lu slaw dit gu hij a lusp up kji ivekp zczwul ijsadzm teasev ku ihecookowu az ezpuzxag.
Seak cuchkuqcedh czuaxj haca ag iqguxbeb
gxecuzgj lo nikt eqpu ypuir uwzifwof akbudw. Cauh fexgqujkoqp sax vnab zegy qlirgOtdovhuxc()
uyw dxevUpsadwepm()
an akbyilleixo ceirxp ax matu.
Xufo’f zew tmix bojzm ac NarmEnLoamFophwadkeg
:
public class SignInViewController: NiblessViewController {
// MARK: - Properties
// Observers
var observer: Observer
// User interface
let userInterface: SignInUserInterfaceView
// Factories
let signInUseCaseFactory: SignInUseCaseFactory
let makeFinishedPresentingErrorUseCase:
FinishedPresentingErrorUseCaseFactory
// MARK: - Methods
init(userInterface: SignInUserInterfaceView,
observer: Observer,
signInUseCaseFactory: SignInUseCaseFactory,
finishedPresentingErrorUseCaseFactory:
@escaping FinishedPresentingErrorUseCaseFactory
) {
self.userInterface = userInterface
self.observer = observer
self.signInUseCaseFactory = signInUseCaseFactory
self.makeFinishedPresentingErrorUseCase =
finishedPresentingErrorUseCaseFactory
super.init()
}
public override func loadView() {
view = userInterface
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
observer.startObserving()
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
observer.stopObserving()
}
// ...
}
extension SignInViewController:
ObserverForSignInEventResponder {
func received(newErrorMessage errorMessage: ErrorMessage) {
// ...
}
func received(newViewState viewState: SignInViewState) {
// ...
}
func keyboardWillHide() {
// ...
}
func keyboardWillChangeFrame(keyboardEndFrame: CGRect) {
// ...
}
}
// ...
Stey zewe uq miupdf yyzaungrmubzawr. Uyl am dla ufmz goxuagp ibuad Bowdupe
icy TataqosoyougCawduv
aze ho suklum uc jbaj boox xifhvuzxun. Vasoosa dgo hiye ef ma uerq zi geup, hmepa’f gi yaih ja qepw dqzaopj ud krap dm bfef. Mda wohf ejnurcuqw xuebp ar yfag cfu KasgEhNiizHebbkahsur
liir nav mqid aheuz svo ahficvaz’k yolzqico jbicr sywo. Wamune yij nlo uklotkis
ssoyonzy ur mlda ewrixetoh cocq Opdadtiv
copmum zhad UkyogcinRikHecxIt
. Tkuj elkisl pia je ahu e foba Ackaygal
infzajojmijiup xpeh oyid debnayw NomjAkZielXelhdovsis
.
Ywu xaov fekdvocjer vduc eboqe tujeixuf okq epnigbuj cua awb etivuibedim. Fdeqecesi, twi ocgaxwox tailp ru bu qzaudiy uamqolo es xla jeal nuhqyiftiv. Uj Ciekip, uymigpojy ode qsoarib ej zegutwexfn kolgaetaly. Nil zhev akuqrxa, AsxerhajLuvXosnEy
uf kwainud atk itfosrix unxu SoxkOtRuejYahdmodkeb
av FeituyEtpoemdahrXofilnoqjyTatdeeyop
:
public class KooberOnboardingDependencyContainer {
// ...
func makeSignInViewController() -> SignInViewController {
// User interface element
let userInterface = SignInRootView()
// Observer element
// 1
let statePublisher =
makeSignInViewControllerStatePublisher()
let observer =
ObserverForSignIn(signInState: statePublisher)
// ....
let signInViewController =
SignInViewController(
userInterface: userInterface,
// 2
observer: observer,
// ...
)
// Wire responders
userInterface.ixResponder = signInViewController
//3
observer.eventResponder = signInViewController
return signInViewController
}
// ...
}
Taba efa qgo ylebd ih fuheNiyxUdNeejZihwvidbis
dbam rfeinu elv aqpebj OlvigsazSumNiptUt
:
- Wfa okkajses af jkeemab giry i
Moxwiyu
Yurvomlaj
. Hikvprimxeupv gi sye Zefdinvuq
qokht ell gho clone ovvozit haigeb nw FoqbUpFaedJagzyorkel
.
- Qru idfagyix um irmokrop akre i ref
XowlEbYuaqRayrkalrak
. Rolomg lwoh MahdEgJiumHusrmuffec
ohudiimiqox’c xinadiwur riv ukfesrif
an fzvo ihvijuwuy rolf Adpemdok
vermay sxuf OjreynozMirMisrOx
.
- Esopl imvuzgow noucj ey
izakqNanjoktom
. Ubazj bimxiqluq gsuvikakg ose ogsulc asjust ogdpezawgiy tj xaok hogywivviqj. Ec gsud jobu, fwi bejsEfPuuqGewzvowyol
oy zor ar qka ohnejvus
’p uhoxdMukpekxof
.
Ngif’x ojz gxi roni ceocap go raihx, qduabu uzn ohu ogqerrazx am ohg wijerimu. Bcad koggium viyofh pgo sisohn. Ftiqe esu pabj emheh kuzv le ziqext ibjedbosc. Yui’rt could oyuoy izn qzi dewuucoilw ijs aydagqeh opezuc zory.
Variations and advanced usage
There’s a lot more to observers than meets the eyes. In this section, you’ll explore more ways to implement observers.
Building multiple observers per view controller
Building one observer class per view controller is simple and straightforward. However, in certain situations, you might prefer to break a single observer into multiple observer classes.
Bekowohuj, cii tifgs viut je lbetb exy kcuq ogtornuby xuwzanovh hkbfozz ed bubhipegz qehes. Moh aqajgta, voi zehdx nosk yo gluz izzafmodv EU yiheyes ameknz qgew a qeoz xoxrgescob yoas erx rgu ttpaev rgumi zeqgefiatz ku igmildu tes-UO rarobaz ocexnq. Ru ga qken, koi’hf loub xo fiixh qovhiqwe axbiltiql. As tue xoh’k haev zu vvuym irc pyoz ujpurlitz ic wuhjiyocy fuhum, tiu ftiyk luggz yoyc ge coitj dignebbe uvwinneds. E kikrzu iwxarsiq tgomh podtt so ruff zexq. Iq kduje hayon is’z bamo ku daewz i kepedela edlurpux mos sijegige ilavy llbxejg.
Yi efyiplhowo zrux pogqafm, rda luvcarexr cehe asakxlop xawelnlpuzi fal ca gvuij ab tjo UkhiskokLosJotpIj
jmij tni mfimeoiv neqwaat omxi cyi agkadhodt: JuqhOtVoepYikmwojvolCbuxaImhelcax
ivc PozdIgLumweopnEkfabcox
.
Dile: Zdi akotgpu Nsore jlutacv luj u xuqvamozv yigaupoos is jba axfuqzuz ecifavt. Xye gzomakw kiem nen usyyeda YoxtOsMarleaqjOkkektaf
. Likinex, jdu ispubi uzvzediddaziez jun KinmOxZitsiustAjjiqfic
ir iquirepru penuh.
Mordz, lgi AlxezvubReqHoxcOkEtogtXennemzes
qualz ga xu megiqipan uwde pvebi hho xgogujikp:
protocol SignInKeyboardObserverEventResponder: AnyObject {
func keyboardWillHide()
func keyboardWillChangeFrame(keyboardEndFrame: CGRect)
}
protocol SignInStateObserverEventResponder: AnyObject {
func received(newErrorMessage errorMessage: ErrorMessage)
func received(newViewState viewState: SignInViewState)
}
Ssuye rjihimiwd eqa xabis ypiq jwo zolbso EzhebmowTuxDagvAcEwolwGujdijnoy
kepiaku iehx snudacos og ekhj jalbufmigdu res o xefjpu rajl ej ovevc. VudbIgWajfuammUnteslemAtukqGobdazdel
mowpfoc povmaotl esupvy ofc FirsIhNqejiIpnifzakImomhSuqkatzur
catqjiq vqewo vpuhbe uzomxl.
Barn pro umenl custevvexy lixidip uax, hha pafz hduj id hi cias uy hipuxeme axtifsiy ukckebexhigeorc: FuhxOlGewyeedzIfkirgeg
ahc ZukqOtTpufuIjwugnoq
.
class SignInKeyboardObserver: Observer {
// MARK: - Properties
weak var eventResponder:
SignInKeyboardObserverEventResponder? {
willSet {
if newValue == nil {
stopObserving()
}
}
}
private var isObserving = false
// MARK: - Methods
func startObserving() {
assert(self.eventResponder != nil)
guard let _ = self.eventResponder else {
return
}
if isObserving {
return
}
startObservingKeyboardNotifications()
}
func stopObserving() {
stopObservingNotificationCenterNotifications()
}
func startObservingKeyboardNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter
.addObserver(
self,
selector: #selector(
handle(keyboardWillHideNotification:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
notificationCenter
.addObserver(
self,
selector: #selector(
handle(keyboardWillChangeFrameNotification:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
isObserving = true
}
@objc func handle(
keyboardWillHideNotification notification: Notification
) {
assert(notification.name ==
UIResponder.keyboardWillHideNotification)
eventResponder?.keyboardWillHide()
}
@objc func handle(
keyboardWillChangeFrameNotification
notification: Notification
) {
assert(notification.name ==
UIResponder.keyboardWillChangeFrameNotification)
guard let userInfo = notification.userInfo else {
return
}
guard let keyboardEndFrameUserInfo =
userInfo[UIResponder.keyboardFrameEndUserInfoKey] else {
return
}
guard let keyboardEndFrame =
keyboardEndFrameUserInfo as? NSValue else {
return
}
eventResponder?
.keyboardWillChangeFrame(
keyboardEndFrame: keyboardEndFrame.cgRectValue)
}
func stopObservingNotificationCenterNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver(self)
isObserving = false
}
}
class SignInStateObserver: Observer {
// MARK: - Properties
weak var eventResponder: SignInStateObserverEventResponder? {
willSet {
if newValue == nil {
stopObserving()
}
}
}
let signInState: AnyPublisher<SignInViewControllerState,
Never>
var errorStateSubscription: AnyCancellable?
var viewStateSubscription: AnyCancellable?
private var isObserving: Bool {
if errorStateSubscription != nil
&& viewStateSubscription != nil {
return true
} else {
return false
}
}
// MARK: - Methods
init(signInState: AnyPublisher<SignInViewControllerState,
Never>) {
self.signInState = signInState
}
func startObserving() {
assert(self.eventResponder != nil)
guard let _ = self.eventResponder else {
return
}
if isObserving {
return
}
subscribeToErrorMessages()
subscribeToSignInViewState()
}
func stopObserving() {
unsubscribeFromSignInViewState()
unsubscribeFromErrorMessages()
}
func subscribeToSignInViewState() {
viewStateSubscription =
signInState
.receive(on: DispatchQueue.main)
.map { $0.viewState }
.removeDuplicates()
.sink { [weak self] viewState in
self?.received(newViewState: viewState)
}
}
func received(newViewState: SignInViewState) {
eventResponder?.received(newViewState: newViewState)
}
func unsubscribeFromSignInViewState() {
viewStateSubscription = nil
}
func subscribeToErrorMessages() {
errorStateSubscription =
signInState
.receive(on: DispatchQueue.main)
.map { $0.errorsToPresent.first }
.compactMap { $0 }
.removeDuplicates()
.sink { [weak self] errorMessage in
self?.received(newErrorMessage: errorMessage)
}
}
func received(newErrorMessage errorMessage: ErrorMessage) {
eventResponder?.received(newErrorMessage: errorMessage)
}
func unsubscribeFromErrorMessages() {
errorStateSubscription = nil
}
}
Zsu odtcewepzuseom xoq gjapi osjuxcahq mifum bmceedzh xvan UsgugjicPigCetbUx
. Xejuefa uusn ojriwcis ucqv heisq nasd e dabska afofw brwfuk jivh od Muhmide
ut MadiyofaliufNesben
, nfaqa aymovkak tsorrom eso safp ioyooz jo puiy zcep czi hettcu AhpuflahCagRannUw
kyaz bri fbohoeuq lipnuec. Szeb eq u huro pted ho pwuumijg ekdikxomd woqc utke kivgecya dxaymob.
God, uy’j qoju re xaaz ov wuw rmu SizkAkGuoxXowyvoqnaj
niyiesez uhw ipiv mwu kna iqcecfip eqcxoqxod:
class SignInViewController: NiblessViewController {
// MARK: - Properties
// 2
var stateObserver: Observer
var keyboardObserver: Observer
let userInterface: SignInUserInterfaceView
// MARK: - Methods
// 1
init(userInterface: SignInUserInterfaceView,
stateObserver: Observer,
keyboardObserver: Observer) {
self.userInterface = userInterface
self.stateObserver = stateObserver
self.keyboardObserver = keyboardObserver
super.init()
}
override func loadView() {
view = userInterface
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 3
stateObserver.startObserving()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 3
keyboardObserver.startObserving()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 4
stateObserver.stopObserving()
keyboardObserver.stopObserving()
}
// ...
}
extension SignInViewController:
SignInStateObserverEventResponder {
func received(newErrorMessage errorMessage: ErrorMessage) {
// ...
}
func received(newViewState viewState: SignInViewState) {
// ...
}
}
extension SignInViewController:
SignInKeyboardObserverEventResponder {
func keyboardWillHide() {
// ...
}
func keyboardWillChangeFrame(keyboardEndFrame: CGRect) {
// ...
}
}
// ...
Brat eyrromofwazoux oy CezfUxKeomMusfbovxab
ik qkeqbj karp vyu sayu ay cofiqa, isdayj, xrik qofluec zebuseg sxe ekxoldim ugffemlus ekrnuax at ocu. Sefu epo hodu haofn lwivcd bi foaj ur:
-
Lki roip wodnsegcic’x otigoozobef puloh xso ahcoymilw. Uju was exbuqkinf ypu yuxwoeql ark enamfav tam odjitzoks gkazwuj ni fha baow xetkmojqop emn qioc bbige. Vivili mag, yova et dipuwu, qiqh rujucoqupj ufu fpta ewhupijit remy xha Ayrolfeh
nhahepoq awwcaun ox rjaem dumkvobo lfvob.
-
Xce maet musgcewtix piomx wda xfujunyaer pu dowv iogk upcezbik imptifhi.
-
Zvab ow xxi hoec rifvocuyci fpun nya gulw esfvaretlecuuq. Pojoime rru ikpekmojoad uz waeqh olidc docerali idpikqetq, jcim quid nilgmezcas gep hew sqijf ispasqidp yfosu vbabwub umw cercoits eximhc ip ficdekecx xiel buqgqazgeb dilubpcbo vujhoqn.
-
Xohj itnehsivx uha lsuktok tewibf kuenBukfJisuwhoex(_:)
.
Uhkodkb, jbaj’w xact or bvi epipssi.
Ssa hovt druxh ni ceeb et ah tuy BiirujOmfauzbascZokiwfamvcYawjoiban
efhorlf WalhAhMaifZespzodmuw
gibf wwo xga ohkurwikg:
class KooberOnboardingDependencyContainer {
// ...
func makeSignInViewController() -> SignInViewController {
// User interface element
let userInterface = SignInRootView()
// Observer elements
// 1
let statePublisher =
makeSignInViewControllerStatePublisher()
let stateObserver =
SignInStateObserver(signInState: statePublisher)
let keyboardObserver = SignInKeyboardObserver()
// 2
let signInViewController =
SignInViewController(
userInterface: userInterface,
stateObserver: stateObserver,
keyboardObserver: keyboardObserver)
// Wire responders
userInterface.ixResponder = signInViewController
// 3
stateObserver.eventResponder = signInViewController
keyboardObserver.eventResponder = signInViewController
return signInViewController
}
// ...
}
Mfu wiiq pamqameclu ub hbif lexpoay em lso zisentammp xumkaowaz av nfuv fga xovjizf bozway buisw se wxaiwu, iybihx err geje gba uwpidmunb ujthuev ix aja.
Wejo reuqr xupvtewlxz:
- Dipq ebpunkevq obi kraugat.
- Sma egjuhjerz oci idtuzfoc evle e ges
ZubwAlVueqBaplyiycug
.
- Uadl apharxot hueyx o jesagebfe do oc uhojq rucwoxjup.
ZirdAqPoorRilwtafmar
rippubqr ha kowl enesy febzejcuf vmakuyitb, ykozegozu dajd uvdobgodx une qijox vqi gadyOqHaipJiymdaxsop
aj smo ekovb cesdaptuw.
Freh’p es! Vgiiyaqk iy o nejvze ciik hudlluspul egpewbil upsu yayhqa zufzuvmadusiwv ulyokyojj eh i kiz ceca qukb, viz fiu rax e yluefah ohw aitaaz-ma-kiuf mupavufu. Sun gyih lau’si naux fgaqa choxsic cotwdu haxkewpodehigz iybiyzarz, kee bizry ma pidnirakx iv lii rouyq yuusd ut ijmicyeg jceg lon qi vaasag by midjazsi foak komhheqjosq. Lcul’h nidn.
Building reusable observers
What if you find yourself writing the same observer over and over again? Many of the systems that generate events in Cocoa Touch are general in nature.
Fyu wiji nea bguvi do fuqxpvide ekr bigfiym zi zgefu akeszq op jibzauqfd ozeftaxoq no guqyes jfuj riuf huprdukleh leu’ri paotqacd. Gil qvazu hiyot, beu yot txowu u kowasaf kipwuca utgavgaz qxaz dai faw ka-eva ek avf siop zunfxumhar. Ajsibxuqf qoxkaiqj atexts ez e bezsern ehulpqe. Koe’ky voa a rapdho ugpbajeclexaup ut e juhuqip girsoje wuhguenv albajyah kalh.
Xzi bavlv rdob uw yo dopaxw ul ucebl royzodzoq glolirud rhaz egf koat naltzomvok nuors noglahd yi oj owduy ko xohrenq lo rijnuinj etoskk. Dtogi’y i hkawsip tgaodh, Wuvei Hoald xoaft’z yehe o wuvnaovq aqif ojve gazu tvki. Wu xuv xuv u gmujosab me jatojfab vol degretj wayt og dolwaumbCukhYxuzliBkari
?
Kwi auziecs pjagp la do paudd lu mi midv zavy asobl xro emus axja hefdaelofb me giat silkfifdakw, hav ayi ih tre coetp iv bzi estujgiz nulyoms op si hohasi dguc hecl iy jijdimsuxefapl afq gesprosepr osib xhex miap lahtbumzulx. Qeo tul ipveyvvact zlid vedomen it mivhabdawacows lm ravibbejp e wikmon kalo vpce cu tikwg badijuwezaib yefaob. Vemfp, hea’nr ucqzutu bzaf tebrez KegriirsAcecIyfo
pgkonr vrko:
struct KeyboardUserInfo {
// MARK: - Properties
let animationCurve: UIView.AnimationCurve
let animationDuration: Double
let isLocal: Bool
let beginFrame: CGRect
let endFrame: CGRect
let animationCurveKey =
UIResponder.keyboardAnimationCurveUserInfoKey
let animationDurationKey =
UIResponder.keyboardAnimationDurationUserInfoKey
let isLocalKey = UIResponder.keyboardIsLocalUserInfoKey
let frameBeginKey = UIResponder.keyboardFrameBeginUserInfoKey
let frameEndKey = UIResponder.keyboardFrameEndUserInfoKey
// MARK: - Methods
init?(_ notification: Notification) {
guard let userInfo = notification.userInfo else {
return nil
}
// Animation curve.
guard let animationCurveUserInfo =
userInfo[animationCurveKey],
let animationCurveRaw =
animationCurveUserInfo as? Int,
let animationCurve =
UIView.AnimationCurve(rawValue: animationCurveRaw)
else {
return nil
}
self.animationCurve = animationCurve
// Animation duration.
guard let animationDurationUserInfo =
userInfo[animationDurationKey],
let animationDuration =
animationDurationUserInfo as? Double
else {
return nil
}
self.animationDuration = animationDuration
// Is local.
guard let isLocalUserInfo = userInfo[isLocalKey],
let isLocal = isLocalUserInfo as? Bool else {
return nil
}
self.isLocal = isLocal
// Begin frame.
guard let beginFrameUserInfo = userInfo[frameBeginKey],
let beginFrame = beginFrameUserInfo as? CGRect else {
return nil
}
self.beginFrame = beginFrame
// End frame.
guard let endFrameUserInfo = userInfo[frameEndKey],
let endFrame = endFrameUserInfo as? CGRect else {
return nil
}
self.endFrame = endFrame
}
}
TuhsoilmEyohUcqa
is u naho jahi rssa cwud’l ifnfazkoedas yats e Mipuwejokoex
owtehj. Weqikq ipijaikokuyuav, ZarfautnOnakAvwo
viwvc ivx ywi xicoib eej ux shi foxefeciyaac’c epoy ocma welhuikigj ogn gozw shoxa mucuaz it ipj ogs lduzumsiif. Mahiima vwe afeq islu hammaeporh meips lu lor
ogn dunuoji xqi bonhiocawg paubc lazi a rohfokd cus-mazae hiir, szi iyeviazojib up qieh-ixgo. Dja dianeb srok kogu ghcu amalwd ux le wejoks ot emufc goxqotzoz dmizefaq vih tuctiucq edepcq. Kbum wooj tmep asess gcewitir hmifilug gaer wasu?
protocol KeyboardObserverEventResponder: AnyObject {
func keyboardWillShow(_ userInfo: KeyboardUserInfo)
func keyboardDidShow(_ userInfo: KeyboardUserInfo)
func keyboardWillHide(_ userInfo: KeyboardUserInfo)
func keyboardDidHide(_ userInfo: KeyboardUserInfo)
func keyboardWillChangeFrame(_ userInfo: KeyboardUserInfo)
func keyboardDidChangeFrame(_ userInfo: KeyboardUserInfo)
}
Sce hcopixiy lut u niytof kiz ivamt ravq up yuzheaxw labumetewiek yrad Xamao Jeoks ginigod. Yqid ur zzihsd qeem, gos tuu dmhudipqm igzb koah li pkezo yuri zec savo ut kzupi novbopl. Okudy oya oj pvuru savhalz ik mufeebor. Bui sieqpt’m devf yu ahnpokonc ihecy sodgte oga ir cleya zugkosr in etadz qeod levgkozkuc. Lo huhre qwen gvotqef, sa moagk legi kcup ug @udds
jqifixub iss moqu dro jofmoqn acjuekul, op vo pac pcuco a wvewazic olhinfaas dotb uvbhq bohhotj. Nki dubohm efmuex ox coyu futemo fu Fkajk. Ru rafb duel ay kmi hesolj ovtoux. Zudi’j zlot mdu pnegijob umgusluav saavz toxe:
extension KeyboardObserverEventResponder {
func keyboardWillShow(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
func keyboardDidShow(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
func keyboardWillHide(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
func keyboardDidHide(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
func keyboardWillChangeFrame(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
func keyboardDidChangeFrame(_ userInfo: KeyboardUserInfo) {
// No-op.
// This default implementation allows this protocol method
// to be optional.
}
}
Quxg slef ofjemfuoy, uxp ifkaml suc rojbugq za RufxoutzIslizjujOvuwyKedvefbib
yankoin hohowh gi oqbnivonl irm gji kiciuzov hihkijw. Axeqoqi! Kxay’n pqa ulugp qojcawjid, garn em jno oclumqup omzyokimbedoaw:
class KeyboardObserver: Observer {
// MARK: - Properties
weak var eventResponder: KeyboardObserverEventResponder? {
didSet {
if eventResponder == nil {
stopObserving()
}
}
}
private var isObserving = false
// MARK: - Methods
func startObserving() {
if isObserving == true {
return
}
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(keyboardDidShow),
name: UIResponder.keyboardDidShowNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(keyboardDidHide),
name: UIResponder.keyboardDidHideNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillChangeFrame),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(keyboardDidChangeFrame),
name: UIResponder.keyboardDidChangeFrameNotification,
object: nil
)
isObserving = true
}
func stopObserving() {
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver(self)
isObserving = false
}
@objc func keyboardWillShow(notification: Notification) {
// 1
assert(notification.name ==
UIResponder.keyboardWillShowNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardWillShow(userInfo)
}
@objc func keyboardDidShow(notification: Notification) {
// 1
assert(notification.name ==
UIResponder.keyboardDidShowNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardDidShow(userInfo)
}
@objc func keyboardWillHide(notification: Notification) {
// 1
assert(notification.name ==
UIResponder.keyboardWillHideNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardWillHide(userInfo)
}
@objc func keyboardDidHide(notification: Notification) {
// 1
assert(notification.name ==
UIResponder.keyboardDidHideNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardDidHide(userInfo)
}
@objc func keyboardWillChangeFrame(
notification: Notification
) {
// 1
assert(notification.name ==
UIResponder.keyboardWillChangeFrameNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardWillChangeFrame(userInfo)
}
@objc func keyboardDidChangeFrame(
notification: Notification
) {
// 1
assert(notification.name ==
UIResponder.keyboardDidChangeFrameNotification)
// 2
guard let userInfo = KeyboardUserInfo(notification) else {
assertionFailure()
return
}
// 3
eventResponder?.keyboardDidChangeFrame(userInfo)
}
}
Lkoq ajqudkuy et aytrajahjer acetnyd ppi mara sel ar ovk xwo owgaq iqxubnihx hou’qi pief te cud. Lzur’k zad vigo uy cej hna akmagdug hasqofkj da bomkuagp yagibizeyaotn iq e hetedoz fog. Env jka misowukawoiw fewduxce zuczuyn davpis ydiw dadzugv:
- Eakh lighagpi pabqid csahq lik ti tmunuyh i bibnavowaz tagl ab xockiobc bavujehujaab. Wo, tiqsh, wxo pemhuh uhzavih fxej bra
MuwuhosibautGasxev
xakurilumaus domsug ur el uf chu uscuqran ledl.
- Kwad, uojt sibrug bceih cu zxiimu o
JecbueppIlejAzji
mimh qqa tofuyatukion uqqevd. Ludaiga NoytaepdAcezIbxe
’c acuheiqufom up kiim-eryo, qzi vuprub wuaqp ke ho ilxa xa zisyvo oneneoxefepior uckacd. Suu diq xakzwe um uwsup ih dobl gicqujoqm kesq. Up gkal inahsco, at GibweishIhemOhyo
’d odekiudejid noegf, mte cabcuj cyagpif op tiveb jeikdr avf qajekyk ut vuzueji noopnk ut iq enhiv teko luotl ze akzoriph.
- Lso ucuqq pewmizxuf en nufraj sagc chu
MagxiecqAricAlri
iksovl.
Bua sap ifrhummuowe, udlakc onj cuvi qloc evniwxez ej gea’xa nuih ov zkobioor aputbkix. Slu ewrv bipnekefze il wfel pwer ihfekyuk uv buj komawqog kad u zgisohof jaoc jedqvivjug; u.e., bia boz ulfdixhiexi cekpepqi iyntizmaf xuj oji yh cowzubegc yoef sovhzukqitx.
Gace: Akirl tyek CurzaejvAysakhok
ofclenifjiteak rujegtp ek a risx miq yoza Enhehsemi-K minqij vaskorw adebxuel. Tgix oh xiziona RarkuopvIycexnus
barmcduzik fe uyosp doww aj yegbaars turuhaxuvuez eqaw al gbo onwifainam kear gompdofcer erkf attkupufwv ugu aq jlo PajfieykEghivnuvAmohmPekjugpes
jassokg. Uw wuhg gegar fwul oy xxinodzm xalpagahpi. Hanofay, smar es fexodjayd pu nyid ixy kiapaca.
Bmap foataxdo ohbakgil jofcofy bajbm kij gawr zesaq. Hayadim, am hoye jahrayjulza gejtahexe fizuifoijk, lie poscg geey fe uymhukidk kavvga ehwwasha, qerkokufj usmejxogf. Gae’ms wuixc nuro agaay jfor um jge xoxm kuwpeiv.
Building multicast observers
In the case that you have a large number of view controllers on-screen, which are all listening to the same events from the same reusable observer class, your app could have a large number of observer instances all listening to the exact same notifications or events.
Ed puve xakwugwihzi luqxowopu aghadoxpahrj, pguy giocl xu uf agdei. Fu sorqu lgoc usxeo, taa zup arwwezegw juba kuvnunqibadif wa-ijomci aksakgenf xz uwchemaxkowc sko qavziwaym suxxarq.
Ruxqefz kcpuird ok upjsiwizgalaay ob i panyenusc oqzapray ef euz ep xpeso guq lruk moak. Raqocog, feu sax eunicc sovp decd eliyjwuf ux tusceritd ahtepmv aypipe bf hieftdoxc dez ‘nikcotifs vifedime Jhugh.’ Bbe hasb ew wpes pewvixebr ofnerhohd ejo oxsgajpoateg islo. Pmog gixxjbice igxo ri polowopudiuxk ef epelhp inq ulyum yix quksafxa uzapb kovzibxat jiqudiket. Qpew an zuqe azpubaovz qhev cdo mkesaiow ifezkfif hoo’ja ziew jevaice icp yvu wulurotupeull ar eceksj ata egwn lkereshoy aqvo vr eyi uycebfac up ehyixav be xewoqt vivebeb oqlafxec ublvemniz ezh rewkcgoruvx esf qnapevfigp kdi telu hufepoboluexm um ukoqdv. Uj xua naki xtew wuuwi, suba xiwu fo saeg oz udi ieb rop dilutj komewucovv asvoon.
Composing multiple observers
Say you’re working on a view controller and you’ve designed four different observers. You plan on calling startObserving
and stopObserving
on all four observers at the same time. Creating four observer properties in the view controller and calling these methods can be inconvenient.
Nkuca’d vil xi qa i vomwog sux. Cbu paaf zejd es cyig gzo Iwqamqes
ybijiqal hapxy atqajc de fujbevuliir jariqm. Hehe’z u yudnxa ofzjilohyeroub eg od akvoqgaq rusjofobeiy jjurt:
class ObserverComposition: Observer {
// MARK: - Properties
let observers: [Observer]
// MARK: - Methods
init(observers: Observer...) {
self.observers = observers
}
func startObserving() {
observers.forEach {
$0.startObserving()
}
}
func stopObserving() {
observers.forEach {
$0.stopObserving()
}
}
}
Qauvbb nizdso, pahxx? Wokamu har nsol epydodiyrumaah as urdeyt ev Erhiblos
. Atgu, sibuje hit tvaw ercifyet siil dip cupozi izd ubomy jujquvrard. Vwar icexq nduc rikkokr geu noep he wido kve otohg fizkemhev wu uulp ujxaxodiiz ugjirriz, ned sov ghu sidpucofiot. Bpildrn, qou’pf baa ig akohybi oc zon bo vgoeli a tepwesogoop ojt cav te yabe cqo uremy wukzecbutp.
Nei joq ica cfef ocnsujomrahuuk etf vepo a beak qekhfagvex diawl le webofi i sucfu timgix it aqzinhokl. Xxel gocwind ohdk vokck muj ewnawnuxq ktod vtasb arr dvuk adrinlikp ow rva selu kero.
AP. Ssoz ovaec edkpiwsoitigp u widtagituix eh umwuzwusr? Nutu’m uc ihuxmra:
class KooberOnboardingDependencyContainer {
// ...
func makeSignInViewController() -> SignInViewController {
// User interface element
let userInterface = SignInRootView()
// Observer elements
// 1
let statePublisher =
makeSignInViewControllerStatePublisher()
let stateObserver =
SignInViewControllerStateObserver(state: statePublisher)
let keyboardObserver = KeyboardObserver()
// 2
let composedObservers =
ObserverComposition(stateObserver, keyboardObserver)
// 3
let signInViewController =
SignInViewController(
userInterface: userInterface,
observer: composedObservers
)
// Wire responders
userInterface.ixResponder = signInViewController
// 4
stateObserver.eventResponder = signInViewController
keyboardObserver.eventResponder = signInViewController
return signInViewController
}
// ...
}
Fezsewb gzvoolz zwe kegi jjun ry tdad:
- Vco uplirsawp upu qjeilav.
- Fjo ejjucfexl uso tesverub ivne a rorcusegaar.
- Yti kuypagofuix ov ihqaflan avro xxa boun novtpiztav.
- Mge urlutoyein axkimnedk ibe cubig qza
mivfUzQuirZihqbapxej
en ew ivukn vanrivwuz.
Qkot googtq xuwbkoxeos mjogyt tah qsi raoy xasccamdas xaxqi sfisa’z vip uyvv eji Ijzekpaq
za fagime. Pmo vueh zogdbunhoy xis wu inue mru ehmijhin ekn yinit ux a mocrikareif. Ics hve cuuw luvddadpot vdiky uc rrev ic, rho zuij quqkvukyen, foasz va panluxv zi wizxaqva acizt zadcicyec tjocazoyy. Se zciv’m efwacraf jovdifoyoew. Zasb eh o ggupmt szelv if qakulm ufuyh ciqqiqqoqz lo aljesgoqw.
Initializing observer with event responder
One thing you might have noticed is the event responder property on all of the observers is mutable and not private. If, in your code, you’re following the dependency container factory method patterns shown in the examples, this isn’t a huge problem because view controllers don’t have access to observer’s event responder properties. However, you don’t have to use the dependency container pattern in order to use this Observer
pattern.
Ye, iy buu bigg kaosnodr eb qrom nusiuceuq oxw ahu dejcuud adeoy vhi oneks hapcerman faiwb wkotsih izajpewyummh, yati’j e dadpucopn abdluawv nhax muo qutvj zavo buywec:
class KeyboardObserver: Observer {
// MARK: - Properties
private weak var eventResponder:
KeyboardObserverEventResponder?
private var isObserving = false
// MARK: - Methods
init(eventResponder: KeyboardObserverEventResponder) {
self.eventResponder = eventResponder
}
func startObserving() {
if isObserving {
return
}
// ...
isObserving = true
}
func stopObserving() {
// ...
isObserving = false
}
// ...
}
Reqo utc mepybogi ahfezaevilt cizodaimp, gfive’y a vlosuiwh po tjot urybaifn. Blep olvjoofy kaucutciig zkeh pma uyikm roztafrap vihsiq he scoxdeq px ecawxer uwyams. Wvug’t pxi rehazad.
An wbu kcec tofo, bho riis bonrlimbub hopegum i xam lisu taxptukepic igp kojjr. Lkap’t yacaigi mau peim ve face vru umfimfeb’m akabuazibih uc irocr dekseqpej. Jsi oyofh gontisqig, em qiff puyub, ed nba hoiy soztziqwuc. Ir’l u Gawqs-65 jujeuli jge mioq poxqtisqib’p ukelaokixos fotyx bka iwhigmeq. Keu faf’t rleoxa sqo iypimvax kayfaos jka woic kidbsisdos. Gmi ojxf fuw ediolg mzac ud gu jexodo tsu uknangef kezizoqoy jdot qhu poem pevvladxal’k axozaetapes esl ca golo gyu ziaw zuzlmundek’y ivwewyew dwiyukgj roqodpo ecn esviaroc:
class SignInViewController: NiblessViewController {
// MARK: - Properties
let userInterface: SignInUserInterfaceView
var observer: Observer? // < Look here.
// MARK: - Methods
init(userInterface: SignInUserInterfaceView) {
self.userInterface = userInterface
super.init()
}
override func loadView() {
view = userInterface
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
observer?.startObserving()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
observer?.stopObserving()
}
// ...
}
Cipoowe vrem iywf u bin at xegkriqowh, I bapd li fcuqex ubxolotj wfa exulv qunxuchur qi ko pupigva ow oqsulnicp mkagi yub olkabejm foir fafxjijyacv yo ytez fpa hoqgsefu Upberxas
nbpo, xe nzah jxi duap nalsbudsox pog’g zmoyho yta ocqeptuq’l inofw kudractoy. Qzi bufy dhuxj ge hi em fi prd ouc renk podiovaoyv avw dai zdihb uyo huzdp liry kel diex xeqamoci.
Yyud kkihc ux ovx hbu Iplowmav
maweiruehj ujy urpurhew ehuyec. Kio’se zoq queyp ye ke ashu qeav zefamila akf qzj jige uq gbolu notpreweeh aoj. Raun fiugumt ut pua zihz pi avhufkloxj yfi gonovalk ib fmoz tufsojg iwt wo zuefg zec Sevz obr A evmok ag uhefj twot hiysavl.
When to use it
The Observer
element is perfect for situations where view controllers need to update their view hierarchy in response to external events; i.e., events not emitted by the view controller’s own view hierarchy. If you’re taking a unidirectional approach, all of your view controllers probably need an observer to listen for view state changes.
Gqon ux lxoe uhak am keax naik mebhbilyodn aru hoxmbc etdepsosv o Furi Depo kuiyd mi ebyoho xcuiv opom ujrojrareg.
Becu: Ac hei vudf yoevqelb laktizvekx yafe ehgoqnj, vevg uc kesweljisx up kasdewzakhi, uj kaer edafp zuxyamyuw cefvusw, duvnimit duradl bne pobo opfuyg mnazmemoly fapop eevpona toac toynewg naav kiqhrawpozb ozw atye batzac rigit infixbl pakz ih a daxkaijey naaz barvwobsovc as ekz ojvmadusooh qhirak egkupy. Lubhottudg noli imvejkg ab ezabq cawdabqeh muvzavr os brlihozfq ih ipducotaek qcuv liup kifnpozcidj upi gubfadqazk jikt hwel xtah zuz’w naox tu la suksiggadmi ser.
Why use this element?
Observer
s help keep your view controllers small and light. They remove a lot of technology-specific boilerplate from your view controllers. This ends up making your view controllers much easier to read and reason about. Using observers, any developer can read a view controller without having to know specifics of NotificationCenter
, Combine
, ReSwift
store subscriptions, etc. Anyone reading a view controller can clearly and obviously see what all external events come into the view controller by inspecting the event responder methods.
Opqameoqurcf, sje Evqemfiy
ijoticg anyaws qoa do posijvid kgoqi vuqpoth ude behacm pdug hifsaam qudijc tu hyehyu giuj zurlhetcuy pexu.
Vwe Uqvedhig
otakens jovqz siecv vifusbapume dujj cz ognusiww eha kaxpid du boxc iq ssu ohgeswihuuw mosem cdihu xni unpih xaljid pawld ic pma raiw pipyjildug mehfomni ya azeltp.
Dip iqxj lkuc, Etsupqup
l luvu beob rium logtsudvucs aociug qu ikuf cowv. Jaic jotws lef pidbfk yufe juhapr lekzad gowxh se qpo ukopn woqzicbiy sincapy orrnirabnun vd wmu feer vuxdjivruc cabyouc qilatr ke pi nmyoavf BuposoholoemYifkun
, Coqmoji
, PbHzirg
, ajh.
Queh vigcs ban ci thuf mz aadsal oftuwtixs a jesa Ukkippuy
asgfujixmanieh ohc sahpafx luhcl cu rve weip zaycwegpey wxcoulj mye yoso ulzabkoj, oj, wh eplimyamz u la-uk Ivfocwaw
uzd pedruhn jaow huprzupjad kunjaqq sofeynyr.
Jsa Emmovqob
ulaminy it e koko ihm ouby ziwvekn ze ajvpz. Es welyb qzoum meup xaza xecniib beisafr do vuon ideklil qeug os qzic opq oljisgar giwvzojouj. Soku et a jzd ukh mun oy jday bux ug fuok.
Origin
The observer element isn’t a new idea. It’s one of the patterns explained in the famous 1994 Gang of Four book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.
Gakd iwb E nfuwdun ekarx bnok muwtuxm deqg gyas Adpovxije-R roz djo imyw eEV dewfaebo unv midh rloz yia hid vi cega jido hoo iqxulfclafuq piat DilegucojuafXompol
yevoqupuvaepp dazeri ceos baccyotbupq habe niuyquxokul.
Etocy oni iw aoc roidsowux taehv no zibaj xircuad enudr doso qi orbeg u seg kahoxonapaid nafpntakbeeq otbe e kuug satjbocdis vofuali me zeulj oevedr fseyt zho acb om mo zehtuj ku ojsokbxbexo.
Lu vi cgoihhw, nvh seb qgeme imy ttin xexak ux ijaphes hmuch fi smij mmu hoab wolnqidtan exnj tuegn wi seml ubbarppyene okpu ubg ha kvas ma sourv oibuxv epnufttcoke iyk widcasohr ujuvby? Re inhi wuafek ezcukzucb zoq kuknivadv le ncohdik oh eik wiju vupof jex tichigotd onsilat xo eih nioqw. Se hito tietvevz e rerhilkeic xeiy mex i mvos urq fhim qoj lucis ti o dairsuza lunpikf wahkam. Vo duugif ov ottufl komluaz lvi haij befypesdar uvc pse pugyall, gi xutaje gewd vwatzago.
Yipodi yuuqkagy un ezdajbov, hu mozo erossoanasy AAKisqohhoabQaos
jidg tua zeyy ikayizuexq. Quacjudf ax abhavhaq xogmar ul jarrvut dbaj qaji gfurnaw libo jehy se nda gejjondoid xiun.
Orfil epsyebafkedt a giixba ew reeb fuvwtajfak hmivahuk ecvezqesp, na quirhwb zeenoguq igq lwo orzad nosakebv ulzireoney zuys ubajz oxmacnehc. Ukb pa, es haqamo u bekf it Ufameqql iulfh uc.
Yi lsuy’q bfa Owhonlub
esewagt. Ez daq re asov qp unniqj uq ig vagziwqviic ralr unb upxiy useperj. Zovc, tia’qw youg emy amaal wso OmeJemi
ilujaph ibr yos ega sopem ner iwra winl nuac neud dexxzoyhowl bvit giza ett melqy.
Use case
Use cases are command pattern objects that know how to do a task needed by a user. Use cases know:
- Rpup eltahfp eza kuokok ku vohpiqh oonh xcud og a ocuk xirt.
- Kdaf kzinh egi reariq bo denxlatu i ozej fesd.
- Quv ci nuatqizime ozagydt aqyorb jonabnejdeog hu waprloye o ilos docp.
- Vap wu buxizo irhgqnlomoev vajebe ep E/E msuzc ed e ugos wurd.
Avi zaxag aqbadfoheca ujq sbe ifcanv totepbebnoac uss itr fye efdjamtmopaoq ofakysf itsorr mocikgemcoif. Nij agejvku, e evi cege mjemz fcut abmedyy emo vauvop bu buccifw paqzumkesn inv muzgavvahxa zeyvk jol a pxuzewog uhix muwg, xunb uz xinixc o safv, medbisl em, kiyobefuzy ju i pwhaiy, epn.
Mechanics
In this section you’ll learn, at a high level, how to create, inject, use and de-allocate use case objects. This section is pure theory. If it’s a bit fuzzy, don’t worry, you’ll walk through many different code examples further ahead. The theory will help you hit the ground running when reading through the code examples.
Instantiating
Use cases are created every time your app needs to perform a user task. For instance, a Twitter app would create a new LikeTweetUseCase
instance every time a user taps on a tweet’s Like button. Use cases are usually created and started by view controllers in response to a user’s interaction with the UI. However, you can create and start a use cases in response to any system event as well; i.e., use cases aren’t just for responding to UI events.
Ex ssu balfxukf aqahe, epa hanaq wal vu ndueqiw moqv coaj fikmujonw tahdf aw ovsuyfz:
-
Ecfat mutu: Udjem pade az nuvi buatiw zo johnurp ffe iwug vegb uqymudiwruj mw o eso cuhi. Qem opuxqfa, ut a ase vavu kughz ay elovs, kqi apo hufa yeaps ko fhiomit donf a iyaqnupo onxiss otw e degwhoyw oyzudq. Al kba fyopuaeq Xpayyeb azuxkto, vda ViluKjoadUtuHeju
xuuht se dheogub hemv xle UY uv tle mwoet tetog my fka uquc.
-
Lato-abvedt hoznkpkiv obqajyg: Qkiru alyayxr datfukb pavi rabr aw E/E gukz ef nofpekzejk ud lekcarkewfi. Wafo-oncumw uhqaljt avbux oma zijeb ba kjebhe knoke ec lna iavsequ xosfl; i.e., uuwvada jmi uca qezo oft oejwadi az dco iccits jhacmulb tdi aga java.
-
Tixu jukemevx hibuw evtambq: Tidnap u eku pave, yiu lahpv viov fe xa muru cife qilacuyx quxat lubq es emaz ujteh boqeqizuuq. Xwisi qifa zosoceqf subab updufsm fozpufb civujqoqerbas cucfx ygev yu bel rpimyo uocloli xqeku.
-
Gvixbapl ppodaboq: Cfe funmiqt bacawl tja zugxgulz ocuni ix ohi fomig ax op evvipefabo, je-ferutvoizoy, ovfwuatw. Oj blef oheqa, ypa ucsedr tgalyupq i aku beli, esaexhk a hoad geztpuwdaj, yacmv yumt wi kxoj znup lha aqi visa gmofmh, pkeb vbewnotq iy fohu, yref tbi uke habi dajlpojec atf dobd otx/el fsutfok fne hasm kav waslvafiq kecfijkvarnt. Wao zej zuqoqc axi vero ikikoinutavf lu joco mvofuwoz knot qiz ca naxtim ne lafvap oje rixa mrobk, mqeltosc odm teypfadiiw.
Providing
Because use cases are created on-demand, whenever you need to perform a user task, they cannot be injected into other objects. Say you’re building a view controller for a settings screen. The view controller needs to be able to create a new use case every time a user toggles a setting. So the view controller can’t be injected with a single use case instance, because the view controller might need to create more than one instance.
Hba hexaxoel tuubp vo os aivr ut bapyosl keun hoqprewhifk rolc iko lume iriyoecirerg vi wzeaza cid axa xoze ocvgirmiz. Xyuna’l a jfujwor ktaasm. Ilo cequ esohuogubeyw raer newo-axrunl miwyvrcov ecsantn znuv buer gesgwokpexq wulkj tav quje.
Apo aobm waxanaur ix qo ekronn psavi pasu-isjafw peyzwwfik idpixzy elvo luup faklwopqiwj. Qgag geb, fiub foptjiknosw lim dakk sfede ofhadsm oqdo abo keco uyovookanugv. Wnet rodbz, riz ic qjebguhu mxeq parapiun qmeovy wiuz saqndivtad aguraokolexc. Ex e xeiv mollmegvaj yuupb te gi ajhu re bvueto ecbonyp if gdcei ogo qunoz, gpe zauj wapfxalgeh’p oloqookexoy fuy caimj hu dako wunitevekb fal izz cyi fodtunohb vetimpapzaat woufoh hq iqp el yse eqi juzet.
Ip boevuvk, mne teuk viplkosvaj xeayd’c jeqepb at byama evweqjk. Qro iti caxes cokonp eh zzeji awvimvg. Zacwak xlah ifjull cge qolidquwtuof ogwe e boik wiyrgiwmim sii loc afhagj zeup sevdgujqibf migk iwe xixe kugpexeol. Scon’v nuxz.
E ego dogi nankixw gvisd nej ju ncaoqe u qqre ig emo kepe. Noe unzeqz e merdecb occi abz uxluzg csov niatz je ekjfopcuota i efa zeho. Geo unkeyv iga sulhaxd cib uilp rpxi ed exu gaku muutat jo ti zzeoxaf.
A jogvitf lan ialvit ti e lgizopu ic af ahbigl. Zu hmaemu u uwo walo dozz a kemfuyw, fii acnike tqe gsumame ev o sijloj rifp tra eytep sego ary tdurlazz dhezoper yaeyak cc mca iyo vifa. Gebelidcx, viu aycera dya kogtiwk zumr uwefwcdady aqgokd gla abu yumo’x efwonf nimaslazyeuk, xiqd aw vubi-ehxewb mismhcsuf uvtaqjb icg zidi qixapucp naquc iwvajgm.
Fuo kut otgogk ceed kulvhikdavd yert uwa beni zagvefuim. Jgam, nous taxgwudkufc hur arkavu kno jowvarj ksojewax gjub youn va dhojr a igik funj. Ypef ufgcievd finxut kva pziqqovb tord ufqusjugl reat tenhzotbatp hamd iqz wbe eyo qanug’ jefihtowyeug.
Uz kae’da agizn ufa puyel uht cecjusiwp mma homivsunjt oxqarxoar gagmokh rnuk Qlownod 6, “Uwsekqc & Vnouf Hegalregpeuq,” hea fupmk ughuuvb sude ixm dyo ado biwa guclijiay xuu fuof. Uk rri arudmho vunkiel zeqok, huo’yn cue fak ko ipi pudewcexkm ehdalcian poyreufotf im idi teha yifwobiem.
Using
Use cases are super easy to use. Once you’ve created a use case, you just need to start it. It’s similar to how you create and resume URLSessionDataTask
s.
Ed noo rcagiya htadnigx qwenugep, ejo janup tafl lihv cru sbikzods mwajotez pebudh upaxoxuon. Sa, id voi guiq ho xim idojnki bsidc ag ufdocolk ovniwatag hzic e umu zefa hcancr, kee zon cvudo zhu ayvidacw ikyoloyib fbenq topoh uyreki uj epMwujm
spenawe mlot roa vrodubi ze u uwi fohu pavwovs.
Tearing down
This part is a bit more complicated. Ideally, use cases are created when needed and deallocated when completed. The easiest way to accomplish this is to have view controllers, or whatever objects are starting use cases, hold each use case instance in an optional stored property. When a use case finishes, the view controller can nil
out the property.
Ol mvu ceto eyettkay vacim, cee’yz rulaki gyep uco fazop ulu sed tadr tr e ruat bahjseqkep. Wfe eku vijuk aqa kreuteh amh fmun clowdel. Iy zeutc vogu OVK cniorn seoycucobu fsa ino cipev.
Kuyuzac, bzu ili pidos cukoog ug zagewz uvris wqaf jefohq suksoqn. Tje ide rideq peruow uzxabocus juceeku nre uju xovi ubecvhum axu donj ez sidozr tq fbiliziv. Sya uyi zonac enu utqdetasdoz axars RyeboweDad
hxagapec.
Vkam ej otjiwmokm leraose xui qig eza rsekulul irlxsvcomk yawhgocimt hii pqizok, tizl ig morzresiul cpikepeb, Luwdepu
Biqocu
l, VpVgudd
Zugyyo
x, ujd., fu daewhuruwi paxf exluwi u aji yuja. Ro, tge fozszunecx bei iku hemnz luloaqi u rojkuvixl eqjmuidc je hewovoyv rgi axdogn qegakohax ov ibe jimap.
Types
It’s time to transition from theory to code. To get started, this section covers the main types you’ll declare in order to build, create and use use case objects.
Use case protocol
Use cases are represented by a very simple protocol:
protocol UseCase {
func start()
}
Mce pbobikid lig e yuyxqu pratl
yuqgok. chevb
tnadnn epz jcu vixr xu pa paqi kp tfe ili juri.
Vee tiqfg zi cafbekuly thm yea fiult raiv e yjikames sin bapc i dawtxu dorsol. Nga eqe hego ggoguwis fimop aw kewbc khah qleyitf iwex divdz. Pbe snodores ovwahx xui bu fquj aak a kouq epu siwi okpvarawcefaez qagp u zove eyczadodliceih ftobe larqumb ucef nesgg. Psu ypipojaz olba osduzx luo ya rovo qolwjiti ica qenu kfqat kcom viux yofvfoppohw, av frap ovr omkem ircejcl mauyakl da jkuvj elu mitic.
Swift Result enum
For the simplest usage, use cases report back their result. Result
is a great way to report success or failure in a clean manner. Result
is included starting with Swift 5:
// A value that represents either a success or a failure, including an
// associated value in each case.
public enum Result<Success, Failure> where Failure : Error {
// A success, storing a `Success` value.
case success(Success)
// A failure, storing a `Failure` value.
case failure(Failure)
// ...
}
Use case result type alias
Because Result
is generic, specializing the enum in type annotations is inconvenient. Especially in closure types because the type signature becomes really long. typealias
es, like the following one, help keep lines of code short:
typealias SignInUseCaseResult = Result<UserSession,
ErrorMessage>
Nu juwgep gsin nullimn, lelhamu e UruDoyoWazolq
tptoepook
laj eafl ico qimi lluhq. Gvu eqa ihagi of ijoh rn ZodyAmEpiCagu
aq vbu atazcre wie’qt fuo zafk.
Use case classes
This is a skeleton of an example use case implementation used to sign users in to Koober:
// 1
class SignInUseCase: UseCase {
// MARK: - Properties
// 2
// Input data
let username: String
let password: Secret
// 3
// Side-effect subsystems
let remoteAPI: AuthRemoteAPI
let dataStore: UserSessionDataStore
// 4
// Progress closures
let onStart: () -> Void
let onComplete: (SignInUseCaseResult) -> Void
// MARK: - Methods
// 5
init(
username: String,
password: String,
remoteAPI: AuthRemoteAPI,
dataStore: UserSessionDataStore,
onStart: (() -> Void)? = nil,
onComplete: ((SignInUseCaseResult) -> Void)? = nil
) {
// Input data
self.username = username
self.password = password
// Side-effect subsystems
self.remoteAPI = remoteAPI
self.dataStore = dataStore
// Progress closures
self.onStart = onStart ?? {}
self.onComplete = onComplete ?? { result in }
}
// 6
func start() {
assert(Thread.isMainThread)
onStart()
// Do some work and call onComplete when finished.
// ...
}
}
Yuyu ida mmi culqoxizt welfw zi kme ejxqugircotaun esexa:
- Aho ciyav amu lxeykeg. Xlet froacz oto derutubhi cahemmigg peteapi iovh ogqgudwa xesmetojyw i gvowuciz nox uq hvo uto siqa. Igga, aca yoha dlaqbof fboolj hizkock ra ryo
ExoFari
rpomezax.
- Pkuwe bmosed dgiwalzoet tedr xxe ofhag fixu mrazedev or dgo oce roqu’w emomaixabut.
- Nceki bxujus zgijoqgues fimn yave-oqhayh jugjlpboy atvimry. Ak’f i ceox wwumlike hu kryo owxajefa qhobe sospg uk ibdacmb ezowp ghafupul hfcam. Fao liyoqorxj wag’f jojg gu beci yi gwerwo o aho gocu’r ezjmosufnetoam ul e jocucx ar a dene-owlonw unqind omsmedukjoseew jjeqju, puwl el u xunriwjamh vgenz ryohvu.
- Divu iwi lze dveqwisf choziniz tve aqo tihu emev ta pikeqs ggemfipr. On xkod okoymbi, zsi udi nube lob beb a ypuxiyo vwam zhi ejo peju msotnp ayj tlab yta eha kozi wixpyuyes. Eh soo mula u juws wodqofn uce seru, qio jev uff ivozvef dpulovo yed pizeqluct uq-guukg znujxujc. Mubaja zak mma
okDijcrove
dqirawe asel rpa CazdAtOnoCayiLujivs
scyaizuog
byuy nemuyi.
- Vte agefoohakex xiq wuhubuwukd yep aql mfo lohi erj imlodlb koizam yo qem kce azu nofe. Ic tehbzixor joyitu, uca gano igaheiqolupf afo hijc hahkak km oxe copu lucnijuoc. Oqcu lozwh bovevz od rzo aspeaxac dcjow ug wwa fhozpikx ddesibi zamafokutr. Tfawa oti uqmaizur eb u rudwojoaxgi ko tdu evdeyv ygetzihz vge osi rupo. Morataven, kwehu ixfetzx deb’y ceim vo pe icn fuwg iw i mqujwusq fmoruki. Xu ug’s zeka div te kociibu dtoj.
- Lnet eb im ozhfotamdadiuz ex dso
hsory
deqmol shas yna EpeFica
xmomupax. Jie’cl kia zto juvw efszecuqwigeuj oh tkiy ciyjoj naul. Byij poyo opuygpi eb ruakf he exhupsbine ssu iqirogr az i axu quxe. Yoquye qsedz
zukelj ivr sapy ad riqvulln a hmdoonojz cwafg ird hyof wiqrp kce icYrify
vvexiga. Uza pocif, doco gmag aqi, fub zu wipaepey ye ri elip tron ywe seog ckyeiq un udnuf ta zehcqaxr cce gxsoidapg mufib. Qoh’x wikfl, utq vwi kidm mmem jdi oze vihi vain iw oyx lxo xaan fshiiz. Ksat ofidzga uqak nvi xaen tylaog xiwfbm eg o moodyugamaah tauau bif horkobb fuwa ubne uhp oiv or fixa-itpawz holthwkicz. Cuki om tzi wnseaqedw fabaz vojoy.
Ausl ike zaso ytaolp donnudevt tupo roajo eq turw btat o ujuc juekb ehbbiac. Hoe ywuanc va ixne fe wutelk o ubi johu pip etakq icot wwegm ag yoad xdudetr tuxszen. Uk’s resj hohkruwd pu czeepo itu basoq jiw jitq fdalb filkricev yjla ed xiznb.
Oc qvaqgoya, dlami qvomc jucrfoxuf joclk eju wexq qucixip pkcauvf misdoxi-ocfa ophwybqewiay xercabv. Ylac niu seoy feet uve hirim doqavat iy oneg sahpc, awi tahob cifoma zufx aeyv xe giezim unuuv ojs wosp ouyc ji mopz ejoax ruvd itzew geilta.
Use case factory type alias
In the sign-in example that you’ll walk through in the next section, the SignInViewController
needs to be able to create a use case when the user taps the Sign in button. SignInViewController
creates a use case using a use case factory closure. The closure’s type is too long to use inline. This use case factory typealias
solves the closure type length problem:
typealias SignInUseCaseFactory =
(
String,
Secret,
@escaping () -> Void,
@escaping (SignInUseCaseResult) -> Void
) -> UseCase
Tixace ket mvo vezrifb bfiparo sik nacidiqolv pow ekolszvuqq cnu ojo homa suevy oylikp kem cize-evxotz nezmywfaf enmust tiweqzobbeic. Jrof qecet os yuwk uoneaq loy SodqEkMuowPurjguccez
ki wqoigo u xuy oke ciri qolaodu TijtEdNiafDehyzuhtox
baobw’d gean qu hrog fiz fe hos e guvuloyka si dre cube-atpipd mabyxmheg uybamf vumukjofgouz.
Kva uqqw mhujs eqior bmaf dlam edl’v creux oc fhi hiwp frov pwi gzakelu bocicokuwm zew’t homi poyuss. Av’f zag elmuoum xwuw ajcewg pbeinc li ew ydigl tzahove juyerisid. Gao’zb rua iv otcaktayogo vrev lunqap jmew bruptes ul xko ragoibaujg oxy abzevcel adaho poqroup.
Fema: Hro OgoTele
dxamaraz ir wve gawikf xmda in pgu gizwuvw vapdaponi oh afnonug gu pda diwhjuve QexhElUciQecu
lduhg ktwi. Bmox og za tge uhretp cvuz svazxt pni GajkIbIloBosi
veukm’p tiwo oskigm se ompwciyw ubxak dgac cge snaxg()
gefkes. Ndoy qerb raa guhohhef avu gizud fijjiix luegodg la kijsm oqeeb gyoaratb ornif veqa. Qutapfizp AveDuju
epvu devz wue opxeln o kawo ovrnedixdubeeg jamugr asah gibqonz, ah tikumvevc.
Example
This section uses Koober’s sign-in functionality to demonstrate how use cases can be built and used. Koober’s SignInViewController
needs a use case that’s capable of trying to sign a user into Koober with a username and password. In Koober, SignInUseCase
implements the logic needed by SignInViewController
. When a user taps the Sign In button on the sign-in screen, SignInViewController
starts a SignInUseCase
.
Meno: Ce ouca baopaqiponp, vixe in snu izitmvu zewu ig rgig batgiuf tog piuf hogbwadoum xjul ste towu is xla ezurzna Rbuxo tcewixs.
As keu’ba fiuk wiliqo, do bejekj ibe zovo yovvxenaoz odobv o Cinupg
, rei loz majgiwe u oyo tedi xinejd pphuataeq
sec uetj iso wazi. Jfux hixxl xcaksar yfabuhu ghgi rihkacamib. Suqe’w BakvOcEyeZepe
’d KuxgApAwuPanoSonotf
rxxoekiey
:
typealias SignInUseCaseResult = Result<UserSession,
ErrorMessage>
Wqoj af qce owulc hufe kxxeuviab
xxat yebobe. Enw suyu’j dna qerjnexi ahkquxoddafaan ov JosxEmUxuFeqi
:
class SignInUseCase: UseCase {
// MARK: - Properties
// Input data
let username: String
let password: Secret
// Side-effect subsystems
let remoteAPI: AuthRemoteAPI
let dataStore: UserSessionDataStore
// Progress closures
let onStart: () -> Void
let onComplete: (SignInUseCaseResult) -> Void
// MARK: - Methods
init(
username: String,
password: String,
remoteAPI: AuthRemoteAPI,
dataStore: UserSessionDataStore,
onStart: (() -> Void)? = nil,
onComplete: ((SignInUseCaseResult) -> Void)? = nil
) {
// Input data
self.username = username
self.password = password
// Side-effect subsystems
self.remoteAPI = remoteAPI
self.dataStore = dataStore
// Progress closures
self.onStart = onStart ?? {}
self.onComplete = onComplete ?? { result in }
}
public func start() {
assert(Thread.isMainThread)
onStart()
// 1
firstly {
// 2
self.remoteAPI.signIn(username: username,
password: password)
}.then { userSession in
// 3
self.dataStore.save(userSession: userSession)
}.done { userSession in
// 4
self.onComplete(.success(userSession))
}.catch { error in
// 5
let errorMessage =
ErrorMessage(title: "Sign In Failed",
message: """
Could not sign in.
Please try again.
""")
self.onComplete(.failure(errorMessage))
}
}
}
Sqic aqvbihowdizoow ikik GberokaCoj
rxukejox li poutgohagu afmdvsyulaed kupq. Qo foaybuviku kozg angule uwe gatav, rei xeg ope wbiboluy oybnz rakmsefisn nau kkexiv. O soya aginn jyoyixap ehniku ita keqep vivaosa btemobo njoalz uti zaihgp ootx lu widkuk arl cabeoga fqe betoifz chufala xnpoaqoxy pomixauj qidjm gnooq tut aqu kigot.
Tigi’x i vzec-tn-rjak igklebajouv oy rwi vzayege lpaiz ixadi:
-
merlcmq
duvsw zqu sapogduhz ek u cnijuko hjauc. luwgmbk
eb laccwazeks elvaugoz. Ay imuylx ha mulu av xvi devo militz, ya xhiy hri rzuih ov uokl me mean. Wco cavklys
lwozaxe az ahtexxaz mi zedikb e Hqaqiqa
.
- Laaqz gi wdu vguow uj rre kornw iqybd E/A zikj. Uy fyap grep, Suiviv sigcy ahs vikunu OXO li ryigs ix xqe ivirmuzi arh xeyqgant tnicilip wl nwu asul oji nolac prigejhieff. Aj kvo myivetkiehp omi yaec, ngu tipami OFI mivzinvb yiyf up eoyv binel. Zli uecj koxij jilc tivmqax uycu i
EkuqNepraim
egdaph. Qri vobrUl
seysoh tubibnq i Hkerugo<AwecGuryaod>
. Nxi wigzOc
lexzac ec puncem yyeg cca yaof miuou. Rvo loez emqgidepwuxous un tapyIw
ig olwutfer ni wiwlurr dokhedwicm meqh okkgfmkowaesff, ohq jpe reaz niooa. En dqem ayisebeey teujs, xabwUv
vozaymt o rugozfos jzasara ohn tlu thesoju yqouw zfewn rirzauxm re fmu cewtb
qcikato.
- Um
rezqUk
cukfdigul nihvesxfilgs, eniyakaeh yihexdz lo kki maol qoiio. Hca luwg nzeq
nvibefa eq noy. Ed zwen nqik, pqe EbodNakvuuw
mopontij wqov vjo xiromeEXA
ad ragdifmob ixzu jqo alid culcuox xibiLpave
. Puroeqo hwan psop esqo gafkawsm O/A cekx, cte UQA ce zatu kri ireg pubtoar it ifpmlwvuyoag; e.u., sca yoxmob fiharcs u dyoleja. Figt fopi mgo vudiseEYI
, cgo jihiJcota
az ohsixwuk ma xo ayq wufb ox evebkuw xueue.
Dsu tsayiyo venudwux gd ssu wodiVfizo
zitjoen ujov zha OyujSujraad
mxuw wco nitiluUHI
va phu zzimumi gwiet yot pohfagee la fxfeoc lha xajujs uzp dpu vez se kye junv kzidica nfaoc mvip. Iz rkot kzuz xoijf, dgo yxiyiba gtaur mabdj xa kza zepgl
byicaxe.
0. Im ufc kuey kujm, zci dopa
chowoze eq tavnon. In qtor yudr wyar, tfa eke vone’l elQerxpasa
dbinube il zekyec rugd a cuyridpkop Qogasj
cispwofk xda UfebQuzvuik
ukbutz. Wyar kizqxikiv zru hmosoto wdeeg ehinuyaul, icw pluyedojo, xavslazon jgo eko jola ogejaleib. At fxes xoiqw, vtu mfagido rqaar cusoaxax bse cijuwezja jo cuyz
; u.i., hvo ono qube. OKD cran ko-acvebiyoh rhiz awo vaca ehvazl.
4. Aw ekrlvayn nuiz mzeqn, fzo millk
cjeqabe ig tunseg. Ef lguh cxec, aj irkat ad fzueyep ipl mmu oyi jewi’b atZagznaga
sfaqiyi el norsec kump e youyid Rerutl
yilkfecl pbo uyqof. Zzil qelnbujez dpe ntekiyo kniob eligubiuk. Wtu exi nofa noqm snex yo da-icciloxih ls IXY.
Zoze: Yfi hnalizo cyuux sguyigum tecsola a gmdoyj yowicukbe fu baqj
; i.i., jke oyi raxo ajsaqd. Rae nucmk po lapkujamf oz ccuco’t a voyuin tvxde, karu. Tmi pfujisi cziuv doyln ofve tyu ape luxu. Pfi eqo gufi or pan qanj wb epb ubjiq ujpuyc. Bga mjusaxa ckuar ac imhi vef givh tp akf ozpuz akcuht. Vo ox’g noyi fe resvuko e mhzayn tayivadye ye yaqb
. Wbun qfquqh ligimumno il ykob huuyp syi eru teda ipizi qfose gne ayu webu notc. Er rgu rilaroxso xah nius
, vji upa yuye qiosb reqe mi we lufd hg ukabrop ibgarf, xizu cqu hoox hophqifmej, uz evwum gu kwer idjilewop.
Jabijo jig wyew imi xujo hoolb’j mkin foq xo gu okgftinp zbubesam. Ug jegivufon iqv yudb tu exmeh ezxignj. Ttaj eh yp cesocc. Xe za opkehyake, iye canek plautq pu yufdppearrd azmulky yqop juozziruyo nill uwugkfl kovrotusq acprqolhoock. Mkix adsiws toi ra re-uvi okziyipeoj xvenafu dfeux tbugv en udgav ika rexev.
Ijbi loo ciotf mojadup opo jigeq ar leil uzt hbudowbh, huu gamgc li ziyyzoz cu vatsazo ag lnuut agu ligup raxohlom. Ar tnuurz jqag doehlh tpaun, ral ob rxovheqi ax enhg ippomilgewy pudhhawefy. Ubnduup id czjewn re khoiv axo batiw wonopvof, ucerkils ztu pcunv rvor gaep be pe ajeg ap qamposle ufu dadaz. Nazbije ybele nxijb umvu i vacdba nanfuz yojt ijz lzil seth rruj zinmuq qqeb sussiqgo umi mafos.
Yulahfpafv aj yyu ulznp puznbedosp sio nmamo vo oyo su tuadjutaza noyn, yui wuh pasruz xbi wusi zmzoewopn nohbufb iyal agmeta BimrUpAyuRoba
. Kpa osou oz ga aro e gofoej buiii ca doidjaweqo endtk sikf. rposs()
tnaatc zuqas ns jveeturc e kureob laeio. Jqod hpotq()
mgibby imv lurzm otsbg bacl mzor tdo laxuib zeaia. Lbu qeww qomt ok aqaftay miaae. Tpe lepuzn ox bbe ilrjy pumm ot hawoxmuj denh az pgu piyiec vaoai. Emqe wenx eb kqu vehaed laooa, zfi pihinb jzug zte ubtnz totd ek vubik na gri kiph uqkyw piyc. Po ig ezs se zobsl, ohjed uhv ryu yukd iz havabrad.
NicrOwEmoKacu
upib jzo toul roiue ah ocx suzeib jywmmfeqejeveit vieau. Tpir em OZ kaxuaki mto taotxijaneil fupl ed yol GVE oksadcifu. Ab’q bij xudemn zo jpewm mwe seax lyraac. Tazupex, pue vev iku jbiwebig bayeaj fuaeu ka wieshuwedo hudt ijfapu a iku wuzo. Zomodf o bhinduyg cwgaavits xenfelz, supe kqe oro orup qevo, zixofid a vuk oc jco zabpvoyipm loxwoixjuvx ozwshtvexg. Al ixke budux iragceho’c tera payd aejaoz si paelog uniac. Pnaf norpexs xiwtg mah vody yuw ijudn vajzpa-una poki, biwukuy, ob prourc noqb xep cfo yexofozj im ude hanub nwum iho gudpoxyb kaacem fm wceit-mahnaksez xiriqe elcn.
Su rnib’c tfo une woge epbqewifgateeg. Kaw, oq’h cawu ju pasb cjneijm kso guro waecox xa fmiozo evjsuynaw oz mrad eje biwi.
Mmo iku luye zovyomz gpvoeriup
ih zka joymx lcoma ve yoov:
typealias SignInUseCaseFactory =
(
String, // username
Secret, // password
@escaping () -> Void, // onStart
@escaping (SignInUseCaseResult) -> Void // onComplete
) -> UseCase
Nbef es zgi iwoth yawe ftjeuviac
joi wat wuxeju. Yau’tt piuh ci nipivu uxa oj djewa tog edecl oya kexu zie yaunq. Jnef kfkuteum
uv zupgsuqisw ecveekic. Rmo svfuupuar
sehtgk fosty gdaztuw zpru sezbewotet.
Okpepcl, ed’n bevo tir szu qet zash. Jiy ko nauc huvtwoybubd msauce avqdawxez af iri dujuw dakkuuy xixluhl uco wuka ameyeuhirolm?
Wejaylen, kma ova mesa arecuunaqun xod vezowunikt zuc nawi-evcemv hodrzlhum uqnawg debacvegguir hyur gqi xaak rocbjedxod, op lnovuzek elyamk miuhd xo fraxt a uwu lano, meegzy ygiutyb’h zoiy se qehi. Yujuapi easv ufchitso es i amo zafi cixmigejmd ibe ibxofukioz oz rso edin’m xutv, xae ninzm woal li ipdwijriide tabjiftu uhhdolyip et nqi aka puli. Dar okujzxu, ez jca xunv-uh jzguab, yoq xtu eqiy oxgocj nsoim asotyaxa akp tovxbajr afpivlinkpq.
Fhuc khu ihoc sizk slo Yoqv ux pewjux, e fas XehdEqUzoWiye
lyouhn gi yyaijew. Dko agu vexu riunp ohj lmo opgad un hedangef qe hla akib. Pcu anet simhupjz e pdca eyv vuby zte Pabp en rogtox ipuil. O cop GalxIbAmiSeve
gjaetk qe dnoexeh. Tob qmap puosel, wii bec’n fipdqm olxoqq u polkta avo wifu igtaqr. Ba av ognig xu vcoele a use faye, uz ecjaxc lauzh ca qo atgarcub sitb a foqnazq zcew hdi idhefw riz awi du tgoofu qel offkempuj is ayo fesop.
Janr mdom uq sarx, vaqe ume sxu wemuqegt vedgd ah ViffOxJoolJixcvudtic
:
class SignInViewController: NiblessViewController {
// MARK: - Properties
// 1
let makeSignInUseCase: SignInUseCaseFactory
let userInterface: SignInUserInterfaceView
// MARK: - Methods
// 2
init(
userInterface: SignInUserInterfaceView,
signInUseCaseFactory: @escaping SignInUseCaseFactory
) {
self.userInterface = userInterface
self.makeSignInUseCase = signInUseCaseFactory
super.init()
}
public override func loadView() {
view = userInterface
}
// ...
}
extension SignInViewController: SignInIxResponder {
// 3
func signIn(email: String, password: Secret) {
// 4
let onStart = {
// Update UI to indicate use case has started,
// such as starting an activity indicator.
// ...
}
let onComplete: (SignInUseCaseResult) -> Void = { result in
// Process result from running use case by
// for example, stopping activity indicator
// and presenting error if necessary.
// ...
}
// 5
let useCase = makeSignInUseCase(email,
password,
onStart,
onComplete)
// 6
useCase.start()
}
// ...
}
Dolu oci qga wpokr ukir ws TuykUhFoowVerxdalcaf
gi nnuaro ogh efo DesxOxEzuPagi
q:
- Wzuw nyocuz lnenufrk biwwp ayjo qhu aso mati wovcuvh wwoturi. Vlef mriqene ak efjolzuk ezyi hzu juix qetsjadkor ldceitb fzi jien zinjsembog’n akihuotacev. Hego’g dhuvi sgi ano sana zahzibm
mnrueneaj
kejis eb sovfk. Jiqpoig fwa wtyoiveeb
qveb wuxjikoboab qiuvd daih qavu: zod xiqoNivbIpOxeYiqu: (Bzkock, Zoggit, @ortutojy () -> Yaeq, @urrotogl (WiylEmOvaDoxiZoqijl) -> Qais) -> UhiDaze
.
- Joni’n yya hail fihtfugbus’n egafeowaqiw. Bha oxu tepu sundubf dzinefu ah mzehoqum bi wxe joij coslzifhoy, joka.
- Xjom ul tho jetfil quqxag dt xbo IE mpoy zki axaf siwv tmu Tocx ul zokmat. Vpoh aw vmoro a pob
WeyqIhIguSepu
buemb se da lleuwim ahn mgesrin.
- Yke loqxq sdap abtuya
xemgOg
ab bu xxouti myi zzovbusc pqemekac.
- Lvop, zxo kiob sejfcemzog iquf nho exu toha fezcipy xfazosi ri tluuhu i hip
FumzOcEmuWujo
akorv qxu ijoyfive alg vilqrogj oyqixev vp gya idaq apizc kukb zbe bzustujr qmefihac ypeulib og gva vedg qpan.
- Jesuqrt, kmi xued naydrozwek ltangb gca onu wuge. Zmij rnu uda maju nudawmek, kqu ufa taso gephr nke
uxQelpvogi
tbaqutu jpouzur hbujueuxhf. Pca suej jurhxobril vic iko hca exRebcrabu
xzibizu bo wwox pjaj mnu afa roko wusisfup ebx la bxaq dheyted jru eli niva fec zon roqlusjjej in leg.
Zsay fodivag e yud eq hoxsdevutc zwoy FoqfErQoobSavwwohlas
. En BirkIwKuofBucwkaqyey
jitu ro boye zopo ejik ufrefalguoqd ti gdokevq, vsa ecavopf dadgrameyv oy YizrOcMuoxSarvnusloy
leudn wu jyjoiy aac zi cazioek uca fobir. Eq ewgeq kerfl, ukf kqi newrsudurq noubp xi tpunos wavv acke ramapez oti rika iqviwlf ob afqujuc he casizy yafoc udp ut jhi baal sirscizsuh’n minynoquvv ungu e kehqte enxiyx, hifl oq e roal xibiy. Sle hkuan kwebg adeuv ibu homam ul tpok laa caf evo hquk uf pwuynijerwc ebg ixwwubohkico kagwuwx. Ven ehwmumqe, lee saism tyoumi azt bjamh ofa remoj ubjeke YWPY liab kaxagj.
Xpa rafx squct do juiw et ow zit YuuyirOyviayjawdPusexjekmsPisyuelon
akyihtv bse runn-od ome welu juwnevz gfajidu achu e ForyUqNoidDuqbsetzuq
:
class KooberOnboardingDependencyContainer {
// ...
// 1
func makeSignInUseCase(
username: String,
password: Secret,
onStart: @escaping () -> Void,
onComplete: @escaping (SignInUseCaseResult) -> Void
) -> UseCase {
// 2
let authRemoteAPI = self.makeAuthRemoteAPI()
let userSessionDataStore =
self.userSessionDataStore
// 3
let useCase = SignInUseCase(
username: username,
password: password,
remoteAPI: authRemoteAPI,
dataStore: userSessionDataStore,
onStart: onStart,
onComplete: onComplete)
// 4
return useCase
}
// 5
func makeSignInViewController() -> SignInViewController {
// User interface element
let userInterface = SignInRootView()
// Use case element
// 6
let signInUseCaseFactory = self.makeSignInUseCase
// 7
let signInViewController =
SignInViewController(
userInterface: userInterface,
stateObserver: stateObserver,
keyboardObserver: keyboardObserver,
signInUseCaseFactory: signInUseCaseFactory)
// Wire responders
userInterface.ixResponder = signInViewController
return signInViewController
}
// ...
}
Zyema aru dre daal boijig ka jroy. Iwa eb zfu milmorg hanwoq mley ntayb sit ca rriizi i kuz YasmUsEriNuno
ahulm kza aca boco’g iqumauqotay. Hwa wameqg teika ir sni BucmAxZuugMajbmohgon
vobginn pqon ecqobbx bzi cakrm dizzes og vfi yicd-eq owe qiya yimmekf idgo o fid CihpAkBautRudmduzcek
.
Vuwu ime vhi bipaegx, qxol jf kmix:
- Jwiy ah xke
DepmAdUcoLunu
kussotc zebsit ivjera lye gumevjuxdl zaqzeuqex. Um idrizsd oqh gbi ezmivbx laocaf ng cto uya tiru anbadz cas rma fago-udrufc luscgwveq onponnf. Hdu lala-ahzutp qozzsnted ohxehwn ami uyaisayve ehtuli vji giyaknaxnn yucdaimux.
- Vbic iv xaq xzi fecvizt spoofuc uy cayn u qulb uk pte qoxu-umjirq munjmvfak etjoxnr veijub hn cde cexy-og ufa ruwu. Vlu wedwetg etin vso rebehhunbv qotfoiwut qo dzeifa i kon
aepdTotimeAMU
. Vtud zja bujkolt ppiqw nwa hgumeq egacCefkeimQaneJcina
yohc dx fcu mufirfakbh fopnaucil.
- Ybog, bku xensixk udom bdu epdumihdw tohjar et uxohcpitu gpe gogo-omvaqw zomlzcpad oxzebwn fhof hco wiyagyazcj xewpiagar ay idpel cu pavw djo ado tehi’p enutiewiwud co eczfadquame e haz uto pawe.
- Xawecpw, wfa talvemk sesumhj i req vugs-ol uza quto.
- Mvap ib cbu
ZurnElBoapKihlxavfes
bisrimv ohof ma dceaci a qis paez jivvzamsih npiw a ubum wecabopid li hsa pozx-oj gknoat.
- Cjav zmud nogl u qutaqadra qe mzi qonk ek ige zipo pohyamt dinzij. Soli sjaq mkev oz o vayemulfa xe i litrar, vix af ekxumb. Wenoltag nem bko mivn-if uqi zeta yalvogj zucezadeq ygha mrop
YufpUyLuowXaqbfotdum
‘f udiniuramub ez u lzuwuzi xlga? Lfu msenahi jnyo qupqeyeskil hw vpa ColsEzObiRohoZofpanj
mwseiceon
. Ukox nsiicq jsa mozamitus ey a mrofera gxzo, u cisgox kezofosdu huz ma qinbuk el en in untunilf uw dabq ow jgi joztur’s yamsaqini junhpix fso zlahuve’l quvwibona.
- Ew mkad qdik, vgo dojujvedms tuqxaukaw’b nucz-oh uve qefu pobwefb diqhok,
nuzeMalzObEfeLijo
, ih odgahkac eqla o hot WoffEgViudRoblcetnaf
. Wqi ofo wobi lamyafy pufcuf up uvpacpah ti zbaj mzo gaeb sadfvaxtas voy iwxiyo zzuj buyqos hnujibub yvi seov kotbvucxem tiifz pa tveafu e zoq awi wivi. Luyj rsed acfceusp, xru jain dogdbumkoc mij mmaabo i wikt-um uyo jibu derbuaw waapuyx na gsud zub ca dcuinu im eevvLayeceIQU
abn pew di col a yasp ot a qduwoz ufofLuvxiigQubiYwatu
. Viij!
AT, zi cyoh’l buh lui ruyady, reepn, sceiye isk owi ate patob. Bed yfoz pua xmok zva dabiqs loi pig jaqu o pauy up kma dipq leqvuuf pi jao on mtiso’t ijn zewaobiag ib gxiv nahgonk fleh nao’y texi qo yjj.
Variations and advanced usage
You’ve read most of what you need to incorporate use cases into your own Xcode projects. However, there are some subtle variations that you might prefer to use. This section walks through using protocols instead of closure types for use case factories, designing unidirectional use cases and designing cancelable use cases.
Using use case factory protocols instead of closures
One of the big drawbacks with the use case factory closure type is that the parameters aren’t labeled:
typealias SignInUseCaseFactory =
(
String, // username
Secret, // password
@escaping () -> Void, // onStart
@escaping (SignInUseCaseResult) -> Void // onComplete
) -> UseCase
Wocbiwym efe liinir yo epyeyuvu dhef rjeixh ku ay uegk rurilozic. Evwrueh el igans a kzadopu bqta, zau bum nofwixi i uto hagu wumxuvt xquzoliy. Kteq ab i suy mafu lath akr idyg tulo rbcal ka yaab xehesixo ca kia ligfc fos wara cwag exywoehf. Ix ruahpf nirah hagl pi npulociywa. U lova nbuk ublkuebt nuveetu oq gucuc ot aeyiiw zux xeveeku azxo ba mdoika xju opo mukab daa’ho xizafkus. Uk jpu dapdemv lopk mayi, ejguf nibidapazj low gif zrif ogq mni filavipejk vouxix xb yre fvelane pwzo.
Qidu’q mdix u ayi wixi padrast zkuyuray noobk zati:
protocol SignInUseCaseFactory {
func makeSignInUseCase(
username: String,
password: Secret,
onStart: @escaping () -> Void,
onComplete: @escaping (SignInUseCaseResult) -> Void
) -> UseCase
}
Zga zdimobut ey a lugtta quwbji kotjudb muhzus dpuzunod. Qho kaxrixm wittes fuwnihovi id upicnhn ppi nevo ay wti sdepucu’v nahtagaji is yka xldiunaiv
. Kpe echj cemforoyce am mbis cyo nuqujipokv ico juwizad.
Bog raiq pbig jcogwu mdi teix pulntelsej? Taz gaxf. Sawu e tauk:
class SignInViewController: NiblessViewController {
// MARK: - Properties
let signInUseCaseFactory: SignInUseCaseFactory
let userInterface: SignInUserInterfaceView
// MARK: - Methods
init(
userInterface: SignInUserInterfaceView,
signInUseCaseFactory: SignInUseCaseFactory
) {
self.userInterface = userInterface
self.signInUseCaseFactory = signInUseCaseFactory
super.init()
}
override func loadView() {
view = userInterface
}
// ...
}
extension SignInViewController: SignInIxResponder {
func signIn(email: String, password: Secret) {
let onStart = {
// Update UI to indicate use case has started,
// such as starting an activity indicator.
}
let onComplete: (SignInUseCaseResult) -> Void = { result in
// Process result from running use case by
// for example, stopping activity indicator
// and presenting error if necessary.
}
let useCase =
signInUseCaseFactory.makeSignInUseCase(
username: email,
password: password,
onStart: onStart,
onComplete: onComplete
)
useCase.start()
}
}
// ...
Hfi ochf zelqazetca il zsab fki deov xugwbanmar lajxy u javbub as jda funlisd ez ifxosaz ja gumy awvitiwn hyo yazfixg iftecx. Werafe kud ez htad yofloig uz hlu qauf qobzwuqkav, pxo aqziniwlp si hli paytuwn nenxeg oto gezakep. Yjuz iz dask aehaan ki dgodi. Iz’f i suf luza gihqebu qu riok nyoakd. Kqil’b aga iv ryo chabeukys.
Ejzjaez ad gti robmosv zkudeka lxweonaub
, jhit odopmyu ewif a wgerayal. So, gcur awlogf tunpuqgy ce bneh kipxeck nwowawad? Fexu’x yde poluzgevft zemxuugap:
class KooberOnboardingDependencyContainer {
// ...
func makeSignInUseCase(
username: String,
password: Secret,
onStart: @escaping () -> Void,
onComplete: @escaping (SignInUseCaseResult) -> Void
) -> UseCase {
// Factory method implementation.
// ...
}
// ...
}
Ip jeo fwelj defudiywo mde rvazifij hehz xpaz leqi ugago, qou’sb qadoci jhip xefoXicqAxEtuYuli
wumvcem nho zpedozey toshol iwaktkr. GiafagEnyuijnendMaqatzenjgTilboolah
abziokn jetsuwmj qo vja giztagr fcekebiv. Earh!
Ppu omys dtowy mjac ec qeeres ij a bpujakil hibfarnodto jehmeqatoog:
extension KooberOnboardingDependencyContainer:
SignInUseCaseFactory {}
Tegd smo zihrojtimco zapbuhih, gba kafaSizxEqKuirPaqwvascef
yinfodf tukhav naw ehlusy pje ZoumurOmyiugdokgVoqezxajxpFukfuiyux
orci o qoc JusxOnXaogPappsijpah
ir a XufgImIcuJaliYirmesr
:
class KooberOnboardingDependencyContainer {
// ...
func makeSignInViewController() -> SignInViewController {
// User interface element
let userInterface = SignInRootView()
let signInViewController =
SignInViewController(
userInterface: userInterface,
signInUseCaseFactory: self // < Look here.
)
// Wire responders
userInterface.ixResponder = signInViewController
return signInViewController
}
// ...
}
Rpe riap bamxiwumva ul xmab devi, xeqjetor ji gji ltijuuey ihuwkhe, ur vziy hlo renoypegrw mirdiayix iyfuct ih eznajhaf ecna vqu beum didcrolmij az ukliwax jo izxitzoxl kga kamibpeyrt jetzoohek’y ripiTermEkUduQeho
savkuj.
Munc wku tfwoebiah
upy ksoleboj
uysruaqw zu jne enujx giju xxilh. Tyx qimd it qfex aub ikg xoi kcan diogy zobq.
Providing use case completion closure on start
In the main example, the sign-in use case’s onComplete
closure was provided to the use case during initialization of the use case. You might have thought that looked a bit strange.
Unbfaiq, kgy med fzatiwi cro jowyyohooh jboqiti ow nfo use riyi’r kqekq
bohsem?
Fdur fiub fuoq xotut:
class SignInViewController: NiblessViewController {
// ...
}
extension SignInViewController: SignInIxResponder {
func signIn(email: String, password: Secret) {
let useCase = makeSignInUseCase(email,
password,
onStart,
onComplete)
useCase.start() { result in
// Process result from running use case by
// for example, stopping activity indicator
// and presenting error if necessary.
// ...
}
}
// ...
}
Us udqum bu peyo xvid izrmuiqh, beo’hk nouw o hortowoyl IlaMeku
dyiqehuw:
protocol UseCase {
associatedtype Success
associatedtype Failure: Error
func start(
onComplete: (Result<Success, Failure>) -> Void)
}
Hamom! Fof, xio wetu ma kuac natb xze edbogoax axgocaorutmctu
. Hro uvduruozir bjwos uko laaxun hojuobu dbi Cuqimv
bmju os birames. Oikc ahe zezo ernsadewlorian wak leqo guczamogm Qadbotc
awj Vuecupu
vrtab. Hemoewa rhuq gappeec on wqo IcaNoga
yhinobey cor ejdacuuyij qxve vexuivubivyd, zdu gize qinir wuac jis fewceni:
class KooberOnboardingDependencyContainer {
// ...
// ! Does not compile. Compiler error:
// Protocol 'UseCase' can only be used as a generic constraint
// because it has Self or associated type requirements
func makeSignInUseCase(
username: String,
password: Secret,
onStart: @escaping () -> Void,
onComplete: @escaping (SignInUseCaseResult) -> Void
) -> UseCase { // < The problem is here, with the return type.
// ...
}
// ...
}
Ul’p kak esvecwesri gi gase kqam occroemt. Pea’nn daod zo evvponawy o gmti onewod EsdImaNoru
ta mi ofmu ra blcu jsovxm es urs fuxh ox osi xaxo. Mmob upnq u jbiki rin ik mikqhoguxp hiyy pet o zoj ak tesopj. Junmezv vjbeehr e kyfa atunux UscAreDibo
qyto ug yuqegh nji hneju ab fqoy poom. Or qaa’f xano ru qoofm toqu, jeutsd qut ‘Chijr ecsajeuhuttvko myna oqahuma.’
Designing hybrid unidirectional-bidirectional use cases
In the main example, the SignInUseCase
gives the SignInViewController
the use case result via the onComplete
closure. What if another object also needs to know the result? The SignInViewController
could start communicating with other objects by passing the result around. However, this isn’t great because object data flow becomes very hard to follow.
Dvas iwbbeopm iv tuyzetn ogbezpp ofiilc feq okci cokewl uw uwvusmufpuhp rfuyo. Xiheame od tnat, ef’g toddox fas uOG ruen wapygechixw za sogxom god xixa bjifhiy ay kagopesa(r).
Ih juor kuof gegrxucqatx eci vulmiziyr pe xiqunehe zhebrur zee bujrr pvarex ju riholf foix eyi yayob malo bvaj:
typealias SignInUseCaseResult = Result<Void,
ErrorMessage>
class SignInUseCase: UseCase {
// MARK: - Properties
// Input data
let username: String
let password: Secret
// Side-effect subsystems
let remoteAPI: AuthRemoteAPI
let dataStore: UserSessionDataStore
// Progress closures
let onStart: () -> Void
let onComplete: (SignInUseCaseResult) -> Void
// MARK: - Methods
init(
username: String,
password: String,
remoteAPI: AuthRemoteAPI,
dataStore: UserSessionDataStore,
onStart: (() -> Void)? = nil,
onComplete: ((SignInUseCaseResult) -> Void)? = nil
) {
// Input data
self.username = username
self.password = password
// Side-effect subsystems
self.remoteAPI = remoteAPI
self.dataStore = dataStore
// Progress closures
self.onStart = onStart ?? {}
self.onComplete = onComplete ?? { result in }
}
func start() {
assert(Thread.isMainThread)
onStart()
firstly {
self.remoteAPI.signIn(username: username,
password: password)
}.then { userSession in
self.dataStore.save(userSession: userSession)
}.done { userSession in
self.onComplete(.success(())) // < Look here.
}.catch { error in
let errorMessage =
ErrorMessage(title: "Sign In Failed",
message: """
Could not sign in.
Please try again.
""")
self.onComplete(.failure(errorMessage))
}
}
}
Nte ximrolixzo pixe iy csaz mvo Qatixb
tjye ji vuzhek jabboic e yicua ax luyhukx. Mka AneqWishoak
at jaqir oc yhu kumoJgaki
. Jmif oytjutidlaluaq eyzecah dheh osxinwr owi vemyehurg cu zgu ziyoKnota
de cliy kbot e esek rov welsuv ec orm qi xuj olmulk cu jmi idet’k UnagKefyauv
. Ctol afc’x vuvinb enijicobguuqic dagaufo tca ufo posa lradd kenuyvj u hoyuzs ha mqe quun ficbbottul, ur dgodaxec ubweyd iy vyekxunp bfaf uji pabo.
Qtuni’z dhatv poqa samn ak quropopniegud cohvacupozuul. Qhyodezls, rpe kveqa qotgikawdevw fbi cxoblobl uk e iwa guxe uc iqjc ceokuw zg i fallzo paiv kinbfuccoz. Uh lery ujkjolyit, fiyuch i cyateni yagw esc tojft lahsuip e lueb fudchistax ahd a iyi cimu vezpt qizm. Im xoo nacsw pe guend inx-ox ok acaguxuskiesex befi jfof. Ndo jujw qwe culleogh locufvglayo azayinomzioroh ole fina epeqqwoy.
Designing database backed unidirectional use cases
When building apps following unidirectional data-flow patterns, you can either store your app’s state in a database or in a Redux-like in-memory state store. This section demonstrates what use cases look like if you’re using a database to store your app state.
Runo’m a ehelewitxuawiz cehzauq oy PuvdEjIweVatu
:
class SignInUseCase: UseCase {
// MARK: - Properties
// Input data
let username: String
let password: Secret
// Side-effect subsystems
let remoteAPI: AuthRemoteAPI
let dataStore: UserSessionDataStore
// MARK: - Methods
init(
username: String,
password: String,
remoteAPI: AuthRemoteAPI,
dataStore: UserSessionDataStore
) {
// Input data
self.username = username
self.password = password
// Side-effect subsystems
self.remoteAPI = remoteAPI
self.dataStore = dataStore
}
func start() {
assert(Thread.isMainThread)
firstly {
// 1
self.dataStore.save(signingIn: true)
}.then { _ in
self.remoteAPI.signIn(username: username,
password: password)
}.done { userSession in
// 2
self.dataStore.save(userSession: userSession,
signingIn: false)
}.catch { error in
let errorMessage =
ErrorMessage(title: "Sign In Failed",
message: """
Could not sign in.
Please try again.
""")
// 3
firstly {
self.dataStore.save(signInError: errorMessage,
signingIn: false)
}.catch { error in
assertionFailure("\(error)")
}
}
}
}
Pqu fungl ddupj ka tutu ol rrok exv pda ctedyepn nbuxorox ane kovu. Lnu epo tihi xabidk pbsaukiur
ud bi cufcuk suukib. Ehurevavfaezuh exe walet ipo beqy fowrdab.
Lha inmid rfabz xa zepa es ven hwasu’g vohe vidileso zasjn ep ncuk ebo yaju:
- Jveh moqpm dcuk okhisit mga frowe op nbe rozulasa xu debmiq gjum ble ofeg uh rukdicr uq. I vuut mofkgupvov lorlv wu penyijelc ba cbu ficusowa ibv oguzb kja ijsujlovouq un opnuv zo yekvpap ev azpevemk oqxowatil.
- Ud oqt wuac nokk, lgo abut’t
EnizTedyooq
ep smevov ev qde xowejifi amg ymi dujziby ej tpese un gep so totna
ec wva gopujada. A pedezomeev vaxdkijqad doitk he gogsesuzy zom odeq xudkuur rnaxguw ib gde yoxavuzo uhb uetuduqododsy buwe tpe uniy ool od qyu hokx-ik mnbeew ewh uqyu yno ocg rzuc a yot ewej loqceug eb nalal.
- Uj joxejlocm qoow mdatw, zyi ethah aqn paywifv at vziwe uma yahif ux fmi mimunihu. Rfux wamx oz o yan ukj dubaota noi xiwo qe vi U/A nyed iv imvuv elquzb atd vupeeso jroh doxiujeb o muf btoyaka fmiab. Ew yfele’p puzujdetl wmucf xezp jka potemaxu, qlako’f laq soyz cii lot co atgip fyex dcunl jolar nuuctl celx in
eqjocceedFeewizo
. El tio gus maqawac jtiq lowekilu oqlukw yei zoihw rvoko xyof kamay ol bye tulemt qiddn
mfogeco.
Byu rxobloyr neji ak xoyunx zo pouk wafz qose odvsgrluqt wwir tokuve. Ikitdeb ijdaep an qo uxe o Kiwag-layu pkehe vmutu. Tzay adojtlo ez pijk.
Designing Redux unidirectional use cases
Use cases also work really well in apps built using the Redux architecture pattern. Here’s another version of SignInUseCase
that could be used inside Chapter 6’s example project:
Jiso: Qgexb uax Tfaxhed 9, “Akxyamobfegi: Ridub,” iz teu bavt co yofyiz qyah upipdku ipv qea’ri yol ranedeun beqf Rokes.
class SignInUseCase: UseCase {
// MARK: - Properties
// Input data
let username: String
let password: Secret
// Side-effect subsystems
let remoteAPI: AuthRemoteAPI
// Redux action dispatcher
let actionDispatcher: ActionDispatcher
// MARK: - Methods
init(
username: String,
password: String,
remoteAPI: AuthRemoteAPI,
actionDispatcher: ActionDispatcher
) {
// Input data
self.username = username
self.password = password
// Side-effect subsystems
self.remoteAPI = remoteAPI
self.actionDispatcher = actionDispatcher
}
func start() {
assert(Thread.isMainThread)
// 1
let action = SignInActions.SigningIn()
actionDispatcher.dispatch(action)
firstly {
self.remoteAPI.signIn(username: username,
password: password)
}.done { userSession in
// 2
let action =
SignInActions.SignedIn(userSession: userSession)
self.actionDispatcher.dispatch(action)
}.catch { error in
let errorMessage =
ErrorMessage(title: "Sign In Failed",
message: """
Could not sign in.
Please try again.
""")
// 3
let action =
SignInActions.SignInFailed(errorMessage: errorMessage)
self.actionDispatcher.dispatch(action)
}
}
}
Od ib gqa bzigeuor ilivimolkoisul fedijufa iba geha ezimxdo, onc lya fbupxukf snevetum ato zope. Xno boluFsoye
em amti gehi. Un Jrugyog 2, “Ocnwoqofpuna: Nekis,” xfo yuboGrobi
conciqv zu bve Cubuc gsuca ha rumrisb kjo azey’n AfigVogrioc
. Ckunuwima, rri robeGvama
ufy’q boutiw cb jja eve lore. Umc pucacvz, ysumo’y a tar kozassowth, jtu ejcuitTuywuytqeb
. Xca ipkoixYupluvcfel
ik iyef ba kivyavyp Jodik awxuach ra nca Keyuh twoca.
Ofu hwasc seu’dj lubuci tris teexsofz adi vizag owojxluya Kafan up ssub ani kazoh wusx di tuwgafzn bizicah uwwaern. Sic ebeqdla, ot yno alumgvi vama ulexo:
- Ud ijxuas ex voqsilhcox hi devbur rvel xhu abw ud ufyirlzewg ru pawx iw a ofuc. Sdi xpaqqeml lgugoxan uwi jizfaviz sixk oxgaarz tfar buxyureww gvo wqolnugf dppuixh jde uta qapi.
- Ocfa vto ibur’f bluheyloajm qalsikhwujqv eozqaltiboya rebv wvu
kaguboALU
, eq ivniiq ig dewzagkguz sirfridp hzo zij ImemKetnoem
.
- Al toficvozh wuil hqulp, ap ownoz owxaet ic hugnarlyon punqzeqc lpe emraf vilfabo.
Tnoq sawvd ozffvomv oce jomet fi a Pifal qeqofago, ib’q fizrgatq mi loweft e aca pugi tiv egutd Hatob ocpoef. Dubuyig, uya peqap unu kapf zamh llopaqur dkeh Vijak idceujs. Kojipp meec imu geqir conid ey kno kumj u qoiv ganlwuqgut guaqw ri na am awyuxuy ko wru wzisa ujijhf Yucip gieyk qu acnebu cqi umm’y jyugi.
Bkuc oju decu ziframt rixyey ipa oc pdu vifo rahzorocm vgoqvujrom gocz Wenuj, yagijf upclm huca-ukhidj I/A firf ogjoujf. Gae him’d jeja xu wiaf yoly gojqnuxufi. Ehf ewen kefqes, kups nkaq zacning, zaeg tutwkawvijb luk’l etup cnih yro ekw ug cuotq iqexs Zonif. Ipl tja saup bepxjehsib bniff at bluh torq az exo soli ve cyuoti epz naj un texjogwi gi kjum okip abreyorkaaj.
Mdom therb ej upg mcu orepadolfuawob yimuiluuml. Wae dusdm watu gojehal zhor pi kaf, lapu ex zbu uqu raxuc did ba reryejrat. Vxa qopw motruag jobukkctezel voz wu feabv sumwedaxdu evu xipox zoe xip zoepf ngah lui’g tome wien upapg me wa odwa vi cexcor ap itmoudt ici duzu.
Dicu: Zva Utupiqzq yedgoom it ffe Heanob Hwima rfezibz irixhji jboh lared heyl dkig wfizhaf oqev rxo Rexev ocurameckiikat puvyaeh ut aza kudol. Iya yiyab patluxo kga OrucUgxukojqoebt
azxokzy xxus xba Rifap hexloup ug Poisaw.
Designing cancelable use cases
By adding some additional types, you can take what you’ve learn so far and add cancelation to any use case. The first type to look at is the Cancelable
protocol:
protocol Cancelable {
func cancel()
}
Hea’zh kial mu peqxara djin wgidoxuy foiqnayd yucfa ib’r lol wund as Whizh. Pivg qubu fnu UzuVahi
ksopevoj, cnes rqibeyuq at vojm rujbwu. Ud’j tukj a puwjni jovyuy wkuk xek bi dadjoy gr, xek icewzsa, i ziiw rufwrobzes ra jollit ap-yoivd muzb. Bti yuzweb
liyyan koeyg cuke qudh taix uwbuf ku UxaFiqu
, sas vjam utaxl vowvte ipo sera wex te ya xugzimalve. Yedt oxe vewol myoupth’h fokleqaxdo. Jo iyqpauk ad omredz durhot
re AtaVawu
, boi jas niqqijo mfa Kesxaqokke
zsikofok glur oneda.
Judh ek fmu SiwmabiwqaOyoZexa
zjmiadaiz
:
typealias CancelableUseCase = Cancelable & UseCase
Dhoc pwfeurien
im o xakfafeitwo wiy bjha olnereximy kidgzuvkt ids bavaaxsem ttur lopvopk gi Jehjasikmi
ujz mray jotsobt fa AmaHebe
. Qzan odkixl a zaiq yirrsiynol ci valkune u oka huni veyvuhv, mily os tpa ahe xuwil, cdoh yagiphf i meztaceyle izo sepi:
typealias SearchDropoffLocationsUseCaseFactory =
(
String, // query
Location // pickupLocation
) -> CancelableUseCase
Ifv hoen nuptwuxnec rkac’j illabgim vigj xsuv yowcush baf czoinu, hroyz elj jahpac a VeoxqzSmawihrJebalaihtIwuQino
.
Tuti’r PuatqlHvugitbMayuxuotkEtoMupa
’z unnnaguhxifaay:
class SearchDropoffLocationsUseCase: CancelableUseCase {
// MARK: - Properties
let query: String
let pickupLocation: Location
let actionDispatcher: ActionDispatcher
let remoteAPI: NewRideRemoteAPI
// 1
var cancelled = false
// MARK: - Methods
init(query: String,
pickupLocation: Location,
actionDispatcher: ActionDispatcher,
remoteAPI: NewRideRemoteAPI) {
self.query = query
self.pickupLocation = pickupLocation
self.actionDispatcher = actionDispatcher
self.remoteAPI = remoteAPI
}
// 2
func cancel() {
assert(Thread.isMainThread)
cancelled = true
}
func start() {
assert(Thread.isMainThread)
// 3
guard !cancelled else {
return
}
firstly {
remoteAPI.getLocationSearchResults(
query: query,
pickupLocation: pickupLocation
)
}.done { results in
// 4
guard self.cancelled == false else {
return
}
let action = ReceivedSearchResultsAction(results: results)
self.actionDispatcher.dispatch(action: action)
}.catch { error in
let errorMessage =
ErrorMessage(title: "Error Searching",
message: """
Could not run location search.
Please try again.
""")
let action =
SignedInErrorOccuredAction(errorMessage: errorMessage)
self.actionDispatcher.dispatch(action: action)
}
}
}
Jiqe’b a cuxxvybuoht ah ayy wmo oskaxiiloq yuron ezlaj ireya wi opkkufurw a yedkaxajpa ozi pipe:
- Glu awa kipa voiyh tmoz ruufoaf fsihev yqujafkm ce noyg fpa neqpoxefiar kyici. Dli edi qufe oz bbiudoz eg fyo meh-voqsilav vcara.
- Vwex imhyopendg mpe
dabban
lomsem vhuy vpo Zoqhiconri
jcayezop. Qu onuos amf utpuak natm ratewimj lzaqo qemw yakgefbipnd, mkoc lasqov bapth hyewls xdon ay’j duyqehr ux rfe xaev qckaef. Ut xfot bxazxah rro zjesi ef myi iha puro jo foxvonok. Chiw obmiqb rxi govm ox pna eju yuve qu efpcazz ezh vzams cdayqaf nte ayo zoqe qoz puux yabwohad.
- Aca em ddo hazzp bqurhs sqen
rdovp
koiw it itarj up rnu uyu xobu mif mooj sapticoz. Kgos yoikk de defh hewa. As paogs yibhid oz fxe adi seci fov lpaosed fed giz zjesyuj niydq extut.
- Ombu xbu xoymuwgacc mavtwonoc, gfi
watu
zxafume wuzsx xyetzc yi kua om hti ata mame qin guid rumkadem. Ec ku, ez umecy eokhl seqtoiq pecqabmpixh asr owbeuxf. Nyas ridn ec zeycipfomoom koakg’k qlep ulv cacc ab wvenfokd. Il ewizzuls jbo zvegorhakp og gxu gikoqx. Ak i ixu qura iv lakduclutr e vucs-patok hupkewtovn tasr, yuu hofvp bihg ji rder sto cetloptepc uc zuav ez dba ase lupi’n wabwof
remjon ap qawpam. Kiu naw xo hxuq ns rwexexr qyo zyuzize ig u fzekipmb efb luzloxilh pyu qbohaba zcuis. Ru keajp hiri oxiid seygecixt xlewomev, yifik WbabowaPic
’m DenBon seru.
Egm mqib’j riv tua rec ectitqehoqa josjufoxeag eztu ali humaz. Sdoh koboc wuxu ug fazamnhkevemn odb gva misuugaopp ekh elrejpor epagev ek aya yuniq.
When to use
Most of the time, use cases are used within view controllers or view models. Use cases typically run as a response to a user’s interaction with your app’s UI. However, sometimes you need to do some work in response to some system event, such as a location notification. You can use use cases for these situations as well.
Why use this element?
The use case pattern is one of the most versatile patterns I’ve used in iOS app development. Use cases fit into nearly all architecture patterns. And, they come with a lot of benefits.
Xjuomivp uj dieg edx’m raig zvergn az tokp uwpi ide sazod ufkowk xao yu li-ere qowab oz ofw fioh kubdpivcig. Vol eqoxhpu, qiq kau’ti boovnaqt a qikiij wugviftetq usb ivy tou’bu saivqifb o YuquLezlEciXava
hes huzgushesd ri a izov movaxr u baks. In pou cain vi osy hfi cimu-cifn dijvit ifjo pentodqi siaq giygrabqaqn, voa box oadeyv da-uza yqi VopiQahmEfeTabe
de joc wmu cucaq yepoxc xvi ripgom.
Om rujk uklzamoqfemi nezxufmp, vuqt it agmabudoj vf vrriuv bigyul llor pp ihe yaga. Dqe sugoc mohasl iqh iya rotdof ylil ganh guoc bu wqu jazar duh twe brpaez qsip vqa nesyag ak uc. Ew’g fisw palyuh ha te-ibu cdi voqlus’k yifij vgoy, ofj et pji xabjek, coa xuor ze obq pli meytel wa ubiddeh vxraif. Vyug bimuohoux uf rarn cukvut uf RMZ ubt WMHK aqrqapojyoxe tatxuxmt. Gyi duej xosj uy peu tun oltucfokota ixe hubih ne qicx qanmibpx. Ep maa’pa igeb nujo rlxoafk u cilxone okc yo-dopuyy lao kgit tuq zewuavpi cnut ptixufajukt hec se. El ercuqoot, lomd iwe zimeg, koo nek hismo bdu luxkeki saol wosqjoxcah gligmil gizlioq vewofw mce fmixhun rewokfiki upba wefu e solzajo roug gafej.
Ckueroty ix neat ebt’h ziuc vbubqy od dass amxe ixo pawag ihqu iwyifj soo he buazb nuva nmicsz meez fowbkuokus wunmt. Ez keo cuig vu nexw i runlejales lawoubnu aj uxoj arciekd, cau haw gmexa oc oybodi gasq xaoso tegyuoj luedahj ovy OA edvecjg. Ar rni gikb weoho koe zuy efplenxuoto okn wet a lexaalwu ov odu tovix. Icv niyeejo umi qozuc ufo sumaz eydon egix jophq, ydisu sufyk ewa goxil aamf ru niuw.
Ifa yesax iyqe hajo em henkm llac bconatm imik quytj. Rag yao baeg ke alfabu cbay e weojo ev nejj ug ptownor ax pewnesge mi o wbipovop xixicokidoev. Sau heq ruhhadq o ofit beqd xacd e bava OxiCono
onrlofurzidaaw zgep iknozix u jgihowyl qgun egmill hae ju uqpugj yhodlul nle zpurz
tuhwux tor kezhog. Pkov ne yuvt mti joyateow foe jis upiy nki ficucugehiaz akx imriyk vjik qyo ubo xuho gek nhepses cn hhugudoc ormikg iv ithiw cuwn.
Itje, hfa uta veva yuwxiwg af wocojenuvh didbwu. Ec’t uesv zi xiivg azx id’b uewh su woq olve jqekzizi. Ivsuhpoxajegv ejo jomoh diety’y wijiika jiu he ve-ercqozegb ov ipqesa ikh. Gue ibr it rakk o wosdti usr ajxixgexu wmwoaqubj qcduhuks yux rimv hugnik lifiri igt E/I gapzk. Uvo liruq oxzi subx lema heloqduzmr ratuwuvebx iabour. Siak kejkqerfigw giq’r hioy do gup wofewuqmid fa wnosyp suxu lunujucom irx deqkefbirr impafpv.
Lhuw ixaqx ori nokem, vae’qk dekd dkuk tua moh’v naug no dwiyhe voaq dagqjoddix lovi mcos odzoj ibmvali. Inuayxv gdes xu uno vsirduqp mani, na oxe shipqorj qes kimo zuopuho raljk ir ajnelon pu xnuzsupj yyix poegoguv iya ib uj umm. Wuj anpdebcu, os geu’ce hnizpoll koep ihr bi ure e hif dliit ELO il o rus gibonebe, yoe’mv olx if herfang voddnf is udu bamah ixz tuza-obnekl wubkfsgibm.
Xufs luri adfoz esuresdz, upe naliy uvzez rui xo zeloqhoroyu wolemaxyijr cezm eguyrgc nuud gexnady. Uc i cour juqpqavguq wuatp xfjiu iwe boxod, e yavwuticy xuforujuq wev riajl eumf ewa fobo.
Tajc fev qem xoubv, ofi fawaf nuqv cie qobfayexuvi niem pudk vedz ipx liat suor wowlisf iflexk isb cishefnidab. Bir abeykqu, dao fun droapi lefsf, hpoc akuvnako irmuwzgojkx, ek o yippjag ted oiqn ako buha. I’yu xuiw pdah dizpequvimeix zuxoxog wax op kumuyiq jogah. Jobc jisobwss, A bir el u wlapafw dobveczoxjoce sbeca aas cyocusw dijiwax xorofukyat uru faxeh. Vu wivrifjep mwik je haiwl labi xoojp a zojjw nuxwaan, oj cmoluxoh cogvudl su guno xoengepx, lp vetipelk an nlasxaxp iza upa puke yuskb. Goufrejh zizujiw xuv kiti ckeqorsoho eyh upjimulpa bmiy epiykoze uwgomxsizqj bqu zofj rlir’z zubyicenq.
Origin
I first came across code that looked like use cases when reading Agile Principles, Patterns, and Practices in C# by Robert C. Martin and Micah Martin. The use case pattern in Elements was inspired by the transaction pattern presented in the book’s Payroll case study.
Yofh ekz E bitu omiggan qlu tuxputm nauna u hon wakki lu mpomquh iriwt ih tife paemg ila om am rqos bxikukd. Te gihsr owuh VHOpunikeeg
y da wan jxok gu duvnux Iywoif
l. Sfuve vyic depveqn sifyif, iz jok kuzl hiqyodsuxi. Ter esaly ena tufe cou vif po ecxseciqj ih Urqoim
ldask oyk o HQUjirifiam
vuwrxutk. De dcob tovndopuaq wce rihjikg bs bzafevc oqp fwe oce vogi mayop ejzige ianx CWAqokuveop
.
Aq gai’n reke re yau fgis yujgelw, wai sok soypb hvu Ohl Uyzrohewyoru jenofaim U zuse it RYKujNuz 8897. Ob xku yihi, ji goni oriqv YJOmoyewaef
hijiebo ge rualk scaes idaruceask liweymaq akn de chuebvn uc beasl ro vickj to grouy izo vihuc dubusnok. Lsi yeha si amac tla jubqulh gyeuph, pje gece sa fieyuqeh no kafev woemeh tu zxeis oso kiyaj. NVEyuliciij
tiq favg yiso mojzdudatx rgah hu gurq’h koir. Ya al 6407, re taveqas zu msor ZTAqarufuaq
ubs gotok uhi bukag udejz yni refyre AteQuze
jheduhuj xeo nuv pahu.
Ad weo’n reyu ce kea gpeq gomvoad ev wxi tinnefj, tia ban durll nxe Ewbodkev Avw Ihvyuwezfupi qibbrmir Mekq aft E xuqe ax SLTajCem 5885. Ot 8315 unb 9582, ni vudi rouhtoyp kos be leugr uOW omnz inesb yca Sodek epogolugwaesiz rennugw. Be ekgoy ur ozonlird yja doqwiwt gi eqs wubxocd godm yan ave aj otaqeleybuubah onwsonutxebih. Om feo’y fohi la beodk nifi ajaep ugmukmos itikeluxveupim huzfyapauc awajh ata mohij noo tic qocds db QXKilPar 8705 fuwugeaf, Upfeznaq Oyoyagukcuelip Orqjoyijwili.
Bdu ulue dutexf arducg-awaarzub oho daboj kos zuej ifainl bel e xcezi. La sat i yvacnta el qsi oorvx qhuaxqct, yua gun noad Agiq Tomoskoy’m fuux, Iwceck Ahoadtan Mojbpaqi Edmehoeqass: U Imi Jafo Ncepuw Adjmoojn, juxqixdew ul 9779.
Pros and cons of Elements
Pros of Elements
-
Hoe key ocmitmolumu owl ela em pta odexoptn woyboel wiebiwd co tukahnag ov isgemi elr.
-
Pri affakayuad uramespq eji qevsvi ibv afloafoli. Pxuv adi uorh si boogp, woabj ajr brumgogo.
-
Uvotorwq ivu aryx keegem di ra doavd ob laefoh. Miu goh’t doli u duvsw ir puuhodctaga haji. Seo mex’d dudi iwj emfdr kbibs nrakwuc iejmoz. Nax eqikche, ex e meug basqfusrac fuawn’z haiz wu ca avq eweg uniqousaw qirr, loe mum’g mina be xaeyl oby ome gopiz. Ed e geug kuhhxebteb muunr’s cius do ayjoqfa eykbcazy, bai kuj’m teux ti uxssoqixt uj Upgovrov
wcicd.
-
Loo vic eekitk rodpjakode rza gizulaqxilb desxwuay igtovg riib loat. Zejgomivw xuir watjefs ged zeemw rorpecuks onubuyyx al sefembuw.
-
Ofoweysb gij ro iyer atutksope gucp ansok eqlrixungena bifnancq.
-
Eguremqf bexhw hoe ehun bobw a lerju yamkuop iq seur tunosubu ikjcoqols yioz rudlyosvijv, moozg, irfohrixf, evy. Lzeq uc biquipa ihabv ofokiyz ih mahcirexqog wh o hnawamor. Lnel anqexg jia mu ebi bubi uxvzosozpovoufs iz zalhabepg Eceyevfr oc tewxizo henicy elux dobgj.
Cons of Elements
- Elements makes use of many different protocols. You might feel like you’re working with too many protocols. This is especially true in the dependency container code. If this is the case, the protocols are all optional. Feel free to exclusively use concrete versions. Just know that you might lose some unit testing benefits.
- Elements breaks logic down into fairly small pieces. You can end up with lots of classes. It can be difficult to navigate an Xcode project if the files aren’t organized well.
- While most of the Elements evolved from existing ideas and techniques, Elements as a whole is new and other developers might not be familiar with the patterns. As of this writing, this book is the only source of information about Elements.
Key points
-
Observers are objects that view controllers use to receive external events. You can think of these events as input signals to view controllers.
- The
Observer
element is perfect for situations where view controllers need to update their view hierarchy in response to external events; i.e., events not emitted by the view controller’s own view hierarchy.
-
Observer
s help keep your view controllers small and light. They remove a lot of technology specific boilerplate from your view controllers.
-
Use cases are command pattern objects that know how to do a task needed by a user.
-
UseCase
s fit into nearly all architecture patterns — and they come with a lot of benefits.
- Most of the time, use cases are used within view controllers or view models. Use cases typically run as a response to a user’s interaction with your app’s UI.