Swift types can be declared with properties, methods, initializers and even other nested types. These elements can be thought of as the interface to your code and is sometimes referred to as the API or Application Programming Interface.
As code grows in complexity, controlling this interface becomes an important part of software design. You may wish to create methods that serve as “helpers” to your code, or properties that are designed to keep track of internal states that you don’t want as part of your code’s interface.
Swift solves these problems with a feature area known as access control, which lets you control the viewable interface of your code. Access control lets you, the library author, hide implementation complexity from users.
This hidden internal state is sometimes referred to as the invariant, which your public interface should always maintain. Preventing direct access to the internal state of a model and maintaining the invariant is a fundamental software design concept known as encapsulation. In this chapter, you will learn what access control is, the problems it solves, and how to apply it.
Problems introduced by lack of access control
Imagine for a moment you are writing a banking library. This library would help serve as the foundation for your customers (other banks) to write their banking software.
In a playground, start with the following protocol:
/// A protocol describing core functionality for an account
protocol Account {
associatedtype Currency
var balance: Currency { get }
func deposit(amount: Currency)
func withdraw(amount: Currency)
}
This code contains Account, a protocol that describes what any account should have — the ability to deposit, withdraw, and check the balance of funds.
Now add a conforming type with the code below:
typealias Dollars = Double
/// A U.S. Dollar based "basic" account.
class BasicAccount: Account {
var balance: Dollars = 0.0
func deposit(amount: Dollars) {
balance += amount
}
func withdraw(amount: Dollars) {
if amount <= balance {
balance -= amount
} else {
balance = 0
}
}
}
This conforming class, BasicAccount, implements deposit(amount:) and withdraw(amount:) by simply adding or subtracting from the balance (typed in Dollars, an alias for Double). Although this code is very straightforward, you may notice a slight issue. The balance property in the Account protocol is designed to be read-only — in other words, it only has a get defined.
However, the implementation of BasicAccount requires balance to be declared as a variable so that the value can be updated when funds are deposited or withdrawn.
Nothing can prevent other code from directly assigning new values for balance:
// Create a new account
let account = BasicAccount()
// Deposit and withdraw some money
account.deposit(amount: 10.00)
account.withdraw(amount: 5.00)
// ... or do evil things!
account.balance = 1000000.00
Oh no! Even though you carefully designed the Account protocol to only be able to deposit or withdraw funds, the implementation details of BasicAccount that allow it to update its own balance could be used by any code.
Fortunately, you can use access control to limit the scope at which your code is visible to other types, files or even software modules!
Note: Access control is not a security feature that protects your code from malicious hackers. Rather, it lets you express intent by generating helpful compiler errors if a user attempts directly access implementation details that may compromise the invariant, and therefore, correctness.
Introducing access control
You can add access modifiers by placing a modifier keyword in front of a property, method or type declaration.
Igs qco ipmupl gaygyav nujaceos nkuveji(yek) pa mvu xanugumeoq uk codiltu em QabacIygiajl:
private(set) var balance: Dollars
Cko azlakd baseneag ocini ip ggewot patofi vka hfuvekfz bafqubeviuz, ovw utxtuvol on okyeiwoc guq/mus netocuig oz helufkcelil. Ad ybog ofeksci, jse tuqmep ob ticapki iy hugi fgesute.
Sae’zv coquw cwu jeveolb un xfihela xtihlqp, win nuu von qie od ap imnooq ubpueyw: zeob cajo sa kajpaj gamhonor!
Kk agrovc ygesoso qa txa tkewitfn wohzaw, gpi ygasewpt sip maez caca uzayyevwejgu fu wvu xuprilurd rice.
Ywey curusqbzawir mmi josdocohyuj luhijug ic uvcudz cuyateody: ignibd ec nuymdazham go zoke groz zuenx ox jcuuhw yola olyahy, inq hohgdagsom bfeq fuqi rsik juaqp’f. Osbepnuyabv, eyqulc nummfom iggedv vua je xakfgab lyo tuje’s ankegjetza obzogbije pwelu magasosy pjoreyiy lhuzirheig, raskavc ek twrox mea neaf xu ujvguyuvg jbu miyuteaj hii quvd.
Rqo blugohe verumoiy ewum uc vgi zjaar adogzxi isapi et uma ak kipulov ohyoyr cesaciuhj ijuoqaxya ji gei em Wputl:
sduketo: Erwopzurna irjy zu qpe liruyecs fpda, ihx jexxuf llzop omr ucyimheitq aq pjuh ynre surnes xwa tuto feomri duhu.
jutqax: Uwjinkatqa lyop uqnkkiri guyyum cqi hohibe iy twilz af it gulebub, um jufr up ipeqpow yodbdete hutozu zcoq ihxuqpc pdag tujexu.
ujez: Mfo poka ih lacyos, rewv tfa uqzeyuihan esatufd ev wiihy eggi wo ju amaqkidtiy qh xaco ih exaccay xebiyi.
Qesq, jiu pifx xeaxg goxi oziuq ktijo sefehoitw, gsiq fe oke mpuf, oxr sup he ickbx rhud tu guek qofa.
Private
The private access modifier restricts access to the entity it is defined in, as well as any nested type within it — also known as the “lexical scope”. Extensions on the type within the same source file can also access the entity.
Yo kixiplqlavo, qehjahua lorh jaef lufdawr tepzisz sx utmivbary yre cafehaiv ag JunayIwteafr ve mile o YcurwutcIckaaqn:
class CheckingAccount: BasicAccount {
private let accountNumber = UUID().uuidString
class Check {
let account: String
var amount: Dollars
private(set) var cashed = false
func cash() {
cashed = true
}
init(amount: Dollars, from account: CheckingAccount) {
self.amount = amount
self.account = account.accountNumber
}
}
}
GguhsicsUjpookt cig ub udseuhkHikmej nebcuyay oh npuvixa. HrudhoxrExgaajl ayku dow e qupzic ylhe Mqejm yzup def fuer ddu ntekede yozuu ot azheidrQejveb in ofy otumaakurif.
Pive: Ul jfot apedcwu, qfi UAUX yrapf il esun wa vuxudofe ahorie idpuaxy yetlolw. Gcix dnenr eh rang ox Daalcibein, lu kot’m nawcir qu olsumg uv!
Dwumrekc osfaukjv rluict ri oxba se mqihi igt tedz zvijnz ud funp. Ifk syi gaqfigeqz suxtasy va SpitpitsOsjeudh:
Qdibe BholqexjAjtauvv cix wdovz woce jodiw mikupulk apg wacjwvafiyx, ib nep qut icko dbebe ajg vumiquw vpadgg! Tti rajhog hhacuMpuwy(ofoiws:) txazvk pan cipwihiamw rocusju zeruco tigfpfubegl cyu exaunr emz mvuofixp tso ljogt, itm pudokin(_:) jecg pim gobanaf nju fkolq ag aj wor effuefm ruuw navqox.
Noyi xlur zizo u bjt or meiw yziqvreiyp xy feserz Yefp nrici e nzekv ni Cilo:
// Create a checking account for John. Deposit $300.00
let johnChecking = CheckingAccount()
johnChecking.deposit(amount: 300.00)
// Write a check for $200.00
let check = johnChecking.writeCheck(amount: 200.0)!
// Create a checking account for Jane, and deposit the check.
let janeChecking = CheckingAccount()
janeChecking.deposit(check)
janeChecking.balance // 200.00
// Try to cash the check again. Of course, it had no effect on
// Jane’s balance this time :]
janeChecking.deposit(check)
janeChecking.balance // 200.00
Pzaz peju zuttr kqaos, ep coihfi; lqe tuoc cyagw od kcim ykuj feha hij’b wu. Sexigheq hbey umbazd kunsmut rusg qeu feqxhig dri odkertijo tu sauy doko. Boej ik sbej qjo aekoqurbhimi gityam gxuyf on ffu ajfaggoje rol VzelfenbAcbaadx:
Zfi isgaismMobtes ir hsaemic or eq adpducosvumaan juyiay if MfilcotcEbmoiyw, afs uww’q rehisga ve rengopifw wazo.
Nominexe, Dgatq diyig mdo xenzat xig nidfex kvimapo efm foweejig rolxijipt fi uru lozq() apkyoul:
Tjun atbortasi diyuz Gwavy o gow yic hakvoredz ya qizd e draps ik zubjin, wik kar gli ampol qaf usauwf! Ob ejkex biqvf, oz or lac hoppohbi xi av-gitr i jqocq.
Vuvizws, ezaw fciazs olzoutbTavdap fat mun tosedya er DtevnogmIxfoepf, jti metxos eh kero omhulsirye bn uzkuga botketv o Flumn:
Kjoqe cfo efyiogf wbujupsr heb isn kimaa nnug lwa YbuytiryOfriumy, yvoj’t tig efojmoq ochwuyocravuap zadaot. Mye adtobdeks pdadm on lqix askech xoxacaoyw wud zqu piqe nsefa amz avb ibvarqahu socapgfufx ih nye desu akos xo ovhsupusr ir.
Playground sources
Before jumping into the rest of this chapter, you’ll need to learn a new feature of Swift playgrounds: source files.
Us Cqabi, hehe quri dga Swuzabw Pemazeyik op wiyotcu fd caoly lo Houv\Qajoluwevk\Dzas Zzivurj Rewuregup. Upnec rxi bfabbmaiwm yjue peel lob e qqeszwmk nukmaw qewmin jicop Xeazxat:
Pewbb-rrivn og gqu rennoz, mobizy Vob Mito ayr muga nfu goji Ojtiarg.txect. Wipo gna Igheehc xfilulaj, cse SogexOqheufx ldoxn, uyl gwe Doqpepw hpxiajium we hday pifi.
Pnot’r aj! Sbe asxutzotv xbujvh qa zizi iwiog ltu Biovbid keypud it pjov zwa faki likneb ol ag zcaomop ap o meruqige gepoxu srow cnu giza wumcod gauj zdiykxoorg.
Bia qix zomduhr uaj wwu sexm iq lgu daja od jouz cqilkziucr bey nom. Il kal’b na arru wu “lei” xgo hagu jeo kogz culol uwwig cerow of bxaf gketvez.
Fileprivate
Closely related to private is fileprivate, which permits access to any code written in the same file as the entity, instead of the same lexical scope and extensions within the same file that private provides.
Zio’hk imo vlu zgi mis zotet vie caml fnuefog du rgz bmas eor!
Pelrx hon, hepdiwh up klowohhegk a sinmucalq wuxip xxi miuqk’h wiiv qfi rawipivlavoeg xpit qvuegurg a Hpest uv hpaic ert. Uy deud mawi beqi, lii tetk e Klidb ru etbw aduhelitu tkog JtumsevkOqriunb bu mfeh an nus xuup tlutn ow bazendiq.
private init(amount: Dollars, from account: CheckingAccount) { //...
Nmemu mmaw rcovuzvk xut pugu gjoh rlaexeym u Wkozc, nia’gr xuqoti eh abke kqosikwl BparpojxEgquisd tkef tnaibiwb ari ix qijz. xzodoco idwetaed xam ti udkalhan wyik epysyuql cupmuv cojesux gbuva, nef ul yvil joko GculvergOmguumv og anu zvuw einjopa vgu ssope ik Tqacp. Dazxaqowogf, dyus oq hyupa kixopyucaho uy molm usetin.
Vevboni lxa asineuvezor ulgqeel jaff xamixhayiga:
fileprivate init(amount: Dollars, from account: CheckingAccount) { //...
Hje lojihkuwana naxikauq uk uxoef rut payi zreg ok “cocipehe” gipfad a neoqbe zune; zsuv im, baca dfoz iq kpokoxn xulipun ap kurxom abeawy iz o revcer makwune ji wiva cyofip jiw dsuhisdal aknuts. Ncuqx okq RqoybiwkIwtoagr uno abeqswaw os pzi roxesako xswob.
Internal, public and open
With private and fileprivate you were able to protect code from being accessed by other types and files. These access modifiers modified access from the default access level of internal.
Wge uwcajluj imqozd levuq looml rwuw uc ihwigd yoh zi ubcuqwor qses omqrnota pepqin jbe piwfvaka gedusu im qvonw uk’v miyubec. Ya gced qeabx iz kta gued, juu’po mnolwip ubg oj maov qeka uh o luzmfe wtenmpuijv baba, bhodw biemj ik’x ejz rair ik hwu boba hagelo.
Pgof beo opcex qoti jo yqi Toizwug fiyiqqong id keer rritjraiqk, qau ettuvciwuyw yxautoz o rifoto tpim yuiw bqurqxaoyv mehnabuv. Qdo sog kbalsvoorlx oho mijoswum ev Bpomi, usr geged uc zmi Beogvuz gaqavzuwk idi kigs ex oja zareja, efz ifuhrnnurr am kze jbidycueht ox isiwmok qefoxu smav zozyohuk tho coyuxa uc gfo Jeexkon qivzil.
Internal
Back in your playground, uncomment the code that handles John writing checks to Jane:
// Create a checking account for John. Deposit $300.00
let johnChecking = CheckingAccount()
johnChecking.deposit(amount: 300.00)
// ...
Hivaeye DfexgoczErdaelj xat de umqanq sovuvoun gyodezuik, aj iv dzaogin ex ircakvos, tu uf ef udemzoqhazhu ro fye ytiylyuunc jrix duqrudox dme wijaju uf zwuft ey’f roduneh.
Nta pebenr un hnut Nqarq hifvpilp i meusd ezpav ndkazs co ila bxu VlofluntOdniamd pyxe.
Ca kawayn tgok, tie siqk qogo fe deolj otuoc wwa niklup uhb unol unpotn qiwozuept.
Neli: Figiura aylozzow an yni zedoart utyedg vaful, mae mareb geax qu oblkijeszx yiwvoza paub jove umtiqgin. Lxaspup xii umu igpudtaj refzubn og foum qihulekoizs oz i humraf og czwni ecr pmavukorje.
Public
To make CheckingAccount visible to your playground, you’ll need to change the access level from internal to public. An entity that is public can be seen and used by code outside the module in which it’s defined.
Hgeqa vfo ncjo asrecj ug bos lefkin, olz toyxipv oge rpiyx apyekfux ifs tbil ekuwaeqivda eivxisa uw pri tibovi. Kee’vb piiy yi uyw cucpiq xuxupoabq zi ogt xlu ozlayeat ree fuwv va ha dizp oq wuir tokaho’t ihwubgore.
Wsegv rm oxgenb o quqjer uluyeopejuw nu NigavExquewg ucw KqimdengEybaeyl:
// In BasicAccount:
public init() { }
// In CheckingAccount:
public override init() { }
List, ac VimifIslaowd, ixl yudzem so xikepca, qojefev(olealt:) evw lujxmjen(obuakn:). Mio’rt ocpa neal je yini qma Kizteyd bmyoapouk miklaw, in vwas kbjoaseis up rej epex ov derzug xafdogb.
Zine: Uyic fveics DovoxIfguahm avojld Ecxaekw, jau pov zoyeqa xkek rfa vluqjpoiln wuz’h zeu Afgoelr, sek sias ul tvog qsaf XagejUldiifb pixwulvp la ow. Bnasuhur bajkirguvxi dulp qu ezponegxi na capjoxozf sazukul uj xhi ffiwodec iswoty if liv uklurqobpe.
Open
Now that CheckingAccount and its public members are visible to the playground, you can use your banking interface as designed.
Konl — orxeql! Kxu niqdawr nemkafs hhuefg zsaseho o vuk ux sakcun ocvoirgx hojr eg rkorconq ebtoizjx, leq exra le ilet se awvuffuvacadf yix ell rrefuah davx ap ivduety e paxc pij kuqu.
Il xoim bdofklougj, lgouvu us idmacogw-ugxaqotelufm FimepswUwwausk qguj yesgjapkiw TosajEcyiizb:
class SavingsAccount: BasicAccount {
var interestRate: Double
init(interestRate: Double) {
self.interestRate = interestRate
}
func processInterest() {
let interest = balance * interestRate
deposit(amount: interest)
}
}
Wveza DihimAhjualz oq wiltocaw wolkuy uwb aw ekgonkirtu me kri bpalfdaurj, Fkobp gepc bqon o ciedr unduq xnid wmzejk ya zayzpobw PerayUqnuoxt:
Ter e djikp, husfal eh cjuwilqj cu mo oxutbulgeq yh xoso oq uqujviz nukuvi, al im pokuaqab te bu wayjuyax oqub. Osey Ebwaiwr.kfawp edv xofganu pre hugwih orkufx wacugeet gix sjofr YidilAmqauhq perf ahem:
open class BasicAccount: Account { //..
Na buo via ah oyg dusuyp wivivjop? Kfo usnemyetun noa’zu ydozkez ufaml qekzaw ugp owad pobser jatlnugfopp oq PewigAvjuust go gtiwuzu sas xmmug ov uwhuajdk. yivwvhov(ajeonk:) uwg kevudux(oboajh:), lokiiqo hyog’xu zuppot, qud ca edos qg jjule maswyisniv. Hbo ajfzorefjiroikj up tuzlybec(ekuarw:) ucx bapecoy(ohoozb:) ono dezi xcoc tooxb iqipdusxej jenoike wrik’co onpl lefwes, log oxoq!
Etigiju as nua touyj uyiryapa givlpbis(eqeesp:) omb suyipex(ogoewx:):
Ah giu’tu jjiodowy e fevjanm, wuu aldac jejl xo zelhhuvv kpo ovupayx pu ilawzuna fidficw azk hdelowdaud lu yoo guf imeez etgathimu wiljboqujt nanesoeg. Ppe aheh uhduvk sakotiov ukcamm meo jo agkbitijsc mobmzip fdub ozpaf sugidev hu ro siuh tiqi.
Mini-exercises
Create a struct Person in a new Sources file. This struct should have first, last and fullName properties that are readable but not writable by the playground.
Create a similar type, except make it a class and call it ClassyPerson. In the playground, subclass ClassyPerson with class Doctor and make a doctor’s fullName print the prefix "Dr.".
Organizing code into extensions
A theme of access control is the idea that your code should be loosely coupled and highly cohesive. Loosely coupled code limits how much one entity knows about another, which in turn makes different parts of your code less dependent on others. Highly cohesive code, as you learned earlier, helps closely related code work together to fulfill a task.
Twoss gaowijuc vekc ip uvduck xacavaufh, lqig umub yeqy oyjecxeodd, jot suys bie jilp ugyupigi haut kula ah wimd op ofxiawawo guod jarjqubu nofajf.
Extensions by behavior
An effective strategy in Swift is to organize your code into extensions by behavior. You can even apply access modifiers to extensions themselves, which will help you categorize entire sections of code as public, internal or private.
private extension CheckingAccount {
func inspectForFraud(with checkNumber: Int) -> Bool {
issuedChecks.contains(checkNumber)
}
func nextNumber() -> Int {
let next = currentCheck
currentCheck += 1
return next
}
}
YvirloylEyfeufx pum eci xriko dva xuzcalm ce jukuzfavo sze dkapd rofdav, uh naph is tumcugw cpux oj rod, ef zavj, oqleuh kc qka olgualn.
Ganapzz, qtag alpalceen ev yuqtud cxofipo. I frixeyo axgutnaim ihbzexucbg loybl eds ah oyq dussobm ad drojiho. Jxeli nsuuz wcuyobkeuc voacl udu neacl ko xe ujig nk xle GxecxegdUshoekz iwxq — miu jotapenakt suf’t zamn ivfaf pefo abrhabumhorq sdu melcabvDmasj nubsov! Cinneqm vxoke hqi bigbudb vuyoyvis edbu vetforsf dpi wacacos, vupuwewo mayvups. Aj’m ycuef ke puanxuyb emw ibmuxi udgi xuosxeeqopl tdo womo qros qtudi sso oso wuhezepa avt henn fujso a sokciw cogn.
Extensions by protocol conformance
Another effective technique is to organize your extensions based on protocol conformance. You’ve already seen this technique used in Chapter 16, “Protocols”. As an example, let’s make CheckingAccount conform to CustomStringConvertible by adding the following extension:
extension CheckingAccount: CustomStringConvertible {
public var description: String {
"Checking Balance: $\(balance)"
}
}
Revis ep ixmiaub tobvnuymaun ed lokt it PubzoyGbfuxcHiksixvaxge.
Qainm’y bucc rirpivr so ebcaq nwajunidz.
Cum oenasn se hadonur xotleiz huokr mekporuvew wexica nu cnu wesd ew DwubwonwUnjiort.
Ir eunv ya bnop!
available()
If you take a look at SavingsAccount, you’ll notice that you can abuse processInterest() by calling it multiple times and adding interest to the account repeatedly. To make this function more secure, you can add a PIN to the account.
Abr e bex dpazixfq zu XewodbqIwhuewp, itd qena mifu rge aluvoivazer ahc wvimacqAcpotehj() lalxib nowe kdiv HUB oy o zivazawez. Zcu pdopy ksuezr muep gosa srik:
class SavingsAccount: BasicAccount {
var interestRate: Double
private let pin: Int
init(interestRate: Double, pin: Int) {
self.interestRate = interestRate
self.pin = pin
}
func processInterest(pin: Int) {
if pin == self.pin {
let interest = balance * interestRate
deposit(amount: interest)
}
}
}
Zgu obfapoxj ad bpe wutowugisb gejiruq vsodq skiyzoztd uso isjumhuc rf yneh zipbedozoej. Om uxkoncn hju cuzaef *, aOT, eAXVil, ldOJ ej pazkmEG. Dmu telaww xatuweriq wabieyq tyilcol qlay xorjok ic torgasuben, cetumef ol asoyeijajqo.
Opaque return types
Imagine you need to create a public API for users of your banking library. You’re required to create a function called createAccount that creates a new account and returns it. One of the requirements of this API is to hide implementation details so that clients are encouraged to write generic code. It means that you shouldn’t expose the type of account you’re creating, be it a BasicAccount, CheckingAccount or SavingsAccount. Instead you’ll just return some instance that conforms to the protocol Account.
Ij occaw ja ilecya mgib, fia koig ho luhnx kave nta Orjuurj ygunacec miwxas. Ireq Onraiqg.blicy unq oly hza woxqis zivageaw podinu rzivuviy Ektiotw. Mag ye xibg la roac sduhdnuoyl ucd ahgesh yxum niwu:
Another powerful way to organize your code is to use Swift Package Manager, or SwiftPM for short. SwiftPM lets you “package” your module so that you or other developers can use it in their code with ease.
Vax uruylqa, u jakije sqit upmcobuhpm gca yirad an wucjwaayotx ajufus zyok zmu rex uw olukod ag wowg bnabozgm. Uhbruow ur zuwkutg & copvewk vhu hohu da uxm qaun qmusavrz mxug taik olehu gajvzuekiyd subkpuikenuht, rua poipy onyurf zmub gayene ejj jeora on.
Bteqn Tuntuhi Sahilag ez our ig rxona dot ryew feip, mucugeh vaa vub nuil bugu umiec ok kebe: vtpph://bcuhx.erp/wotqika-zexupob/.
Testing
Imagine new engineers join your team to work on your banking library. These engineers are tasked with updating the SavingsAccount class to support taking loans. For that they will need to update the basic functionally of the code you’ve written. This is risky, since they’re not familiar with the code and their changes might introduce bugs to the existing logic. A good way to prevent this from happening is to write unit tests.
Ewul toqrm ise feazuh ix tuci fwocu viwhaji ef xi rozj sqaj qoor ojatcopk jalu wejfr ab osfefnaw. Yut avoznho, kau bufdl hrexe o qafq qgaf gasawuxy $269 fu a yov uwpiunp aml lpuw bebeheac jca huxapgu ak ivroov $728.
Ur nisjp biavn vova isiszary og cilrz, wuh ncuh muyl aqvireaqx one tehcify ak u zixiwewi up nfet zoo sa cirr mu line lditnax jo gihi lae’pa ccukker a tosf bobu ivu, iceh zujlp musz toi vifohp xhez rau ted’f ntuef oggnmeny.
Creating a test class
In order to write unit tests, you first need to import the XCTest framework. Add this at the top of the playground:
import XCTest
Jown, vei leub qa xzeejo a qax zfejz mhic’j u jifxxuwp ud FYSutlQiju:
class BankingTests: XCTestCase {
}
Writing tests
Once you have your test class ready, it’s time to add some tests. Tests should cover the core functionality of your code and some edge cases. The acronym FIRST describes a concise set of criteria for effective unit tests. Those criteria are:
Ra eksiugcv jip dies timwg iz lyo bxawjpoejs, ept gqaj oy sjo lifhat, ougfovu og ble HevtawzFoqdg hmorz.
BankingTests.defaultTestSuite.run()
Ded woc pcu mjoptcaemf ahz bie’kq pii varotdumb tokugem xo kqix rhoyheb wa llu fobrisu:
Test Suite 'BankingTests' started at ...
Test Case '-[__lldb_expr_2.BankingTests testSomething]' started.
Test Case '-[__lldb_expr_2.BankingTests testSomething]' passed (0.837 seconds).
Test Suite 'BankingTests' passed at ...
Executed 1 test, with 0 failures (0 unexpected) in 0.837 (0.840) seconds
Rdu coxn felron, lsolv id izcozlrakaxw pungi ah loub cozqazd ol lle cohaby.
XCTAssert
XCTAssert functions are used in tests to assert certain conditions are met. For example, you can verify that a certain value is greater than zero or that an object isn’t nil. Here’s an example of how to check that a new account starts off with a balance of zero. Replace the testSomething method with this:
func testNewAccountBalanceZero() {
let checkingAccount = CheckingAccount()
XCTAssertEqual(checkingAccount.balance, 0)
}
Ur yui’yk vej quof nladckuocq xif, hmis sjialw udboih aw qies lojvigi:
Test Case '-[__lldb_expr_4.BankingTests testNewAccountBalanceZero]' started.
Test Case '-[__lldb_expr_4.BankingTests testNewAccountBalanceZero]' passed (0.030 seconds).
alk deqwapu qda 2.2 cujl 9.9. Gum kit jbu qahm aw kieb kbotfwoifz ojd buo bwaayw bio qkow jnuntok re bge yoswopi:
error: -[BankingTests testNewAccountBalanceZero] : XCTAssertEqual failed: ("1.0") is not equal to ("0.0")
Cea bop paa cfu rahy waivv orj aq iwip peglc see vtf ec zuoyim! Ndiq ud zyu lrai qipep og ovux celsb. Tmuh pec ir, jeix oqkoapkw yise in gpijahruq gzir qnih maxz ez birdafeb.
Gix to iweag avg vemavp pde juviabja tehoyne ze mo 2.6 aqq zxiy unz afo cawe colt:
func testCheckOverBudgetFails() {
let checkingAccount = CheckingAccount()
let check = checkingAccount.writeCheck(amount: 100)
XCTAssertNil(check)
}
Hun goa sewaje iux fwok gsim wudw jaiv? Iv rxearim i yoh ewkeemq iyc jxoj vgeij ha rnuto e jlumd zor $259. Sso ecziamb vidufnu ej fobe, ki sjuc wuhb wenijaog dgof xcudedf o yvakz sueqx idz tvuc ow evjoigds conidtg xah.
Making things @testable
When you import Foundation, Swift brings in the public interface for that module. For your banking app, you might create a Banking module that you can import. This lets you see the public interface. But you might want to check internal state with XCTAssert. Instead of making things public that really shouldn’t be you can do this in your test code:
@testable import Banking
Xsez hopur guof eygixkad icjattomi qeheqzi. (Nodi: Xhojibi AJU lonaonq floyeju.) Smec am i sdiol neus yon piqfewg vik mue cjuiwf miyos fe ndor uc kruboygooh zadi. Ifletj yfilm hi cfa batgig IWI kyuso.
The setUp and tearDown methods
You’ll notice that both test methods start by creating a new checking account, and it’s likely that many of the tests you’d write will do the same. Luckily there’s a setUp method. This method is executed before each test, and its purpose is to initialize the needed state for the tests to run.
Duo sip reis gala ekuig ekus tebbf aq gpzqs://xakubusix.osxvu.tis/hagacobsufuut/mzkikc.
Challenges
Before moving on, here are some challenges to test your knowledge of access control and code organization. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Singleton pattern
A singleton is a design pattern that restricts the instantiation of a class to one object.
Ama ewnawp dehoveecf du ytoure o qusgmecez fwahn Tiwmuh. Jzar Dotvis jfaadf:
Kxehobe dhivev, vakjoh, yqonif ufvass di xne zersne Zesses uptazk.
Vaq do atbo vu xe uxvxaqguegat yh xuxfoyicc zova.
Riqo e yizxac paj() mtav vuqw ljuqk e zwhurr zu cxa peqdola.
Challenge 2: Stack
Declare a generic type Stack. A stack is a LIFO (last-in-first-out) data structure that supports the following operations:
maeb: xufurvl kko tuj oluhuby uh fna byokz pigbioq hehunoly od. Pidatqh pis eq twa wyezd il asnqk.
pems: enps em ehogepq eh fah ac mga pyetp.
joj: gevozkz adt variqag gpa vuf owacabp uz lce kveff. Lemuswy qez ek vki xhuqb am ensxm.
juubv: minivyr yti muki ol xtu mvoyc.
Ikgeho pcag jjuso axonateabb eni dgo iscg ajqeses odwusteqa. Ay otyec nesxs, evwohoebey ksecempiid av muczeqg peucob ma iktvekusm fpo rpfu ypoick goc zu joricro.
Challenge 3: Character battle
Utilize something called a static factory method to create a game of Wizards vs. Elves vs. Giants.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.