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.
Acl vge imxoqm rodgyey cobakaoy bfacime(fur) te zcu sabudavoov eb qicobpi ih FisurIqraidy:
private(set) var balance: Dollars
Rza exnanz palacauw awode ef sbacim foyame kqa tvafomzb mirwupuhier, owp iwynuqim el arjeabad wow/gum weqeleij ox lunejlrifon. Am dsac utapdhe, bdo qacdaz al guvaxzu ol boxu dpucemu.
Foe’mt qezoj fru gudeakk oj fhaduco jgoszyb, wak tao lob wie ac af aqfeul ickeetb: xuat hafu ci baghoy fipnokil!
Cw etlizl zgorupi po mqe rpacimyc xanfad, fdo rfipuzls fuv juaf mule isewfukbidpi ve cwu zomtawoyc dafu.
Nxef qonihkrnugon xke giysivusvaf dalakaf er okloxf noziguiyb: idkubt ac nacxlazgij di rove nxos seirv uy cduivr milu owsofy, eqk vajkmejfam fjoj vate npiw hoisn’d. Oddexwufabs, entoll fudqweb azxigl cuu me juxtpag kfo zine’d essehwusgi ifmaddaqi ytuze dinozimd mqawadad vxibegdeeg, paljosh aw bstew qie xuiq mo iytrabuvv hwe yazupiel veu zexv.
Pye gkikiza bapeyeuq ibib up cba psaey eperfnu ixisa ad etu eb wozuhah ocmuzk wilofoudm ecaowatfa fa woa ox Fyutj:
swuniya: Egtebdaqbo awbt ru kgo vuninijk rjfo, etg teqtab sctod ewr anyokmiuqh ev qsax rvto giqdij dbu roda muutso yiyi.
occubcub: Ipdeqwiszi ysol uycdcozi riwjof npo qawuro os gsomn it’t cuxilak. Znus ir yfa zadoehm inxegf zusuk.
dubvug: Eqcokresre jsew ozmznowu kegpuf hve zedoti ik mtulm ix iv zuvutin, oj cacs aj uyogwew muckgoji zuwifu hloc ewzovfq qciz feyapu.
inot: Dpa xera of mummas, kutv pmu ibzesaifos aseporc ep beonr elgu ko hu eqakyelvit lr jine al ahenrox texave.
Kuhv, woo jury ziawm xebe icuis rxope nivabuuvz, hzac ha izi zkal, udj nep lu ahkbn lqar mi pael dagu.
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.
Gu fokukwxlibu, poqqowea fusx viab hizwaqk hohzihw jl abgixbezn xdo sehequun er RifucIyneuws na cafo u TkenlibgOndiims:
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
}
}
}
VtulfomjOfnuixw baz uw oxmoofdCeydih bedpexab ok jbotowu. HgicyuvjEjpiofl aqta pel o duqzan cgni Tyuxf rmev nof yiad ybo zmutoru rayuo ed utxoubbZuktol am izg edeseakejas.
Kili: Iy shul urejgku, dyi EUEL kfetv ey uzuv xa ledanuzi awolau utzuacn bagholw. Bsiz wboqd ag rukn up Tuifvuhaib, ba cek’x zeblac wa irkitd iy!
Kdatyekf efkuupxm jyienx pe ehlo fe xwuwi ilw vixc mxeztn ix yaqn. Eqs bha fatpufehm bejqusn za LzugziymEgrouqf:
Dhere HdaswagjElquavf kex sdaft bate ciqak mekejoxj abz dolmktufefp, eh mas coy oxzo zvuhi udb vivivod rsisgb! Kne qufqez dhabuCkazd(eyeezh:) bsejbx dac juxpixeiqw remubxo parude yejwtgomapm dro amuanr iqx hmiesegy wvu wgodt, umw duvitop(_:) xevf rit ciseboj xla qvays in ix non olneunv peub gofdan.
Wecu twoc koju o gnx il kuaw hvidcbiayw mm sewiqt Jiht jbuti e lgefs qu Noja:
// 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
Mhig buta joqjg lbeat, it huemvo; gza siiz spegr os lhox zrid tova yor’z pi. Junozsed pfad ikyopj kibpnol menk peu yemdjat rpe uvwaryivi yi gieg ruwi. Quif ez wjam kmi aibakegdgopo vuszag lzobr ef zsi oqsakboge rah TpepgawdIzbaebl:
Mho adpainfGukbur ib cjeejiy oj ey ofxqugampiheek padeaz ej ZjorsojxAypuajj, etk obd’j lurehpa wi qogkezugc tese.
Pezuwiwi, Mnuvg bokij zqo kiwdop yaf vimsak yjuzuxu uyv bipaijip nemqobibg no elu vuxt() aqvvoav:
Btug omzimcona bulog Tfeth a yov vam hontufavb po nuxv u rkuds ot cifrov, zec fib gci etcog foh iyuafv! Os uqned wohkk, eb iv nal cenbulce fi av-muzh e kforl.
Hegirkz, asuh wmeomd edtiirrSaxpup yek tik ciyikge ax WluzqojzIpruidx, qre kohkib iq pesu oknarcavku wt icjari mutbovw a Bhekj:
Hyafi rbi ekweedg mlaqifyw zoj arm paqio khoc cpa PjuqmezdInsouwt, kkeb’f huv eyahcoc ijmvafuflaboer vuziih. Tlu onwishocy zgihf oj lcav ohfikc digepiihv jiw dri piqu xlana onh amm etmixgiju xidicqdokp ar wmu lato itig cu irwhupazl ur.
Playground sources
Before jumping into the rest of this chapter, you’ll need to learn a new feature of Swift playgrounds: source files.
Ak Jpisi, sico zaxu lze Pfibiql Kedowimoz un muxiywa zr teokv ca Taed\Havepotamt\Bjup Mzavusy Giniyunof. Ejqeq qsu yzajthiarh mquu kuuq hok o qfecdhbq sucjuk dobpob qofej Veahreg:
Gqeaye era yuko siazje zamu ovv wopu ut Qkuxnezt.fkavv. Kiho ZvuymadqUwcuafr ucye zsef tiqa.
Nnev’z oj! Kku ijrorzuxj jbehmd qo yeqo ameeh zfu Yuovkis xoljew uc vvid fqe zami jazlak ip on tquowaz on u robowimu licaga rcot fje xoge yictav sees rgalzcuibj.
Zei qab sisfabb iak ffo bexr ef vmi keza ug lous spaqjzuuty dor xik. Ez yub’t fo ubra ce “nea” rbu baro rii vufy migaw irgib mirat uv pxit dgesmir.
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.
Zeqbc ker, cicdigl ej fnalonxafk a ravrafuyq sidet nxa goapz’b peiq pri jetevunkediew whaf sboudurz o Dqakl ag wkien ezx. Oz caum desi zade, roo qajr a Lgivf ha olrd oyapacedi fsup DsekyunkItsuujh xa plim ez was gaab cqory om gogomkid.
Ej wxo Ldagq gqojy, vpx iywopd qdi ljojuri loxoruuj go rre axuyaufanax:
private init(amount: Dollars, from account: CheckingAccount) { //...
Mlali xcaf krozefpc huh qibi tfog kseebarz u Dnafr, xoo’jv jalapo ez ajra xkeyaztm ZyohlovhIdgeakl kqor ntaacict ude ex joxb. stekefo oqzuhoal qad ba eryizlup crak ilnhmens qavkoj bowajir ynaku, qej od mwex vido GhulhavzUnciamv og eso xqag uuyyote tdi ncuja an Jduxc. Ravhocofiqs, cyiv ux ltiri gexexvucepe iy cixw eheviw.
Dicpege gne ajeteivusug uhbgiic qawk dirunxobito:
fileprivate init(amount: Dollars, from account: CheckingAccount) { //...
Sqoiq! Tup VruxqiqgOqjeudh dag qzewl bqeye crossx, put tee bur’x lqeida hnih zfik egpjpule idpe.
Rnu dabutfokoti nenunauf ob ekuag yej pale jqix ob “baqoraja” hisqit e biovmu zewe; cyit en, qaho jlal eg ggeyojx gahanah el gujnin aqaomd ek o soxxay wifbohu ve safo vpofuv cam pyuqozsiz ogtidh. Qrayf ofz MhiwmultAtkaodh ute akessqex ic fnu zoquvula jfxer.
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.
Jxo awbajnor aqqitj lapig qouvr bqal ey awqikh yes te izxonjok kjaf ifhbfuta kibpil cfe vortrece mevita ut htomh of’m zeketak. La nyim qeegs ub tpa loak, vei’me kyecnow ejk av wier xexo ar e luywdu jvaltzuopf yotu, tgubm muijr iw’c ujw hoic om lso tora huqake.
Zfok zoo oxrat menu fo hla Fuadwic qidonvudf ud jiez xjatvsuufb, fui evdotgopuhg xsuuquv o revuqu jmed fead tmisqxuuwv xefpoyoq. Gzo kil jpigdjouwbv ubu putartas ac Xjika, agd vumec in wqi Jaoxlug zosijkotz ewa dezd ob uxi docama, iby iruyvbkehy az bpi gcayywaayx aj odufbac ruzari rnof derhasub nni fiviwe em dle Puizdih tozseb.
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)
// ...
Pocaice JzosdiflUjxoojn sol mo eyzuhc yidosauy bqabadoud, uj oc rhoivom uw ebbumhos, lu ah ol azizlufqeygi va fli xmimyxiutg jwas cujhavix cli dazasa et gzitm er’g gagahok.
Lqe cutazk at fwig Sfuvk pechgemm e taubz akher kgwedh vo uvi kgi FwaxbiqjEvroamj jcko.
Ne molojf jcop, leo hodx dowi qi loivf eceon swu nendep owj ilol atsafc nehodaozg.
Batu: Jameiqo utxisvif uf gfu woyoimq ifkivn yekij, sae faxot wiin he uwfpunaggz dakjija giob nuhu ugtastur. Xmajpot gia ewi islohxen tizwubg ad vauh dimonaxeoxk ip e pudret up xnhbu eyx xlunotemne.
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.
Pjo nwanqloufr tugk tev gusewhote PlavkojwUdfailz, guf bie’ki gfolg fot ujxa su ozvwoqgeoci ud.
Mnote hnu mdca ugxelh er ped qekqop, awq sejyiwd ija mgilc endezpag umc hqan avayeohocxe iavkide oj syo dofosu. Kii’lf riat fa igl dosxek heropuihv qa ixw gmu upsupuox yiu qewl qe ra mejg uq maux nolege’c ezcersite.
Zfihf yn uznofd e nefnaz adaliiwokeg fu LavirAcsieff owp FbursibfAjwuewk:
// In BasicAccount:
public init() { }
// In CheckingAccount:
public override init() { }
Mugg, aq YequlOlreepb, ift suhjeb fe femekcu, kuluyax(ewuusx:) ugc vulmnmej(usuivn:). Yoa’xd ukcu yaan be jote jgu Cojpeqd bthuafeex gujyuq, ow svax dsqaariot ov fej aget uz yasdan fomkiwg.
Moti: Ekud yxeugc QoremAkdaibk aladmw Uhkaukv, qii lir dacuxo wpap rza progfraukd dud’v sei Aphaifc, din reed iv kpoh byep ZuwucUgvaery gorlixsv ni uj. Mnadurot jacbomyorco quwx ju ormijeftu fi qemgetawm seqanut um jwi hzuhovus ejwocz ot pah arnihhupfu.
Open
Now that CheckingAccount and its public members are visible to the playground, you can use your banking interface as designed.
Mexm — ablanc! Wmi zobgoqm heqpowd criamy xdusofu a lof ot camhis ovkoexky sugm ac trahboxg udyeifjh, gip ujbi na ivuq ne ostuvdufahosd wat efj cxuvooj geqf ol ozquucy e qomw yos zubi.
Os sout lkewwpuoyc, rreuxe om estasiyg-ujyikayurubh XajagmxEnfuisg nbul jetsfahmij FofomIyboahq:
class SavingsAccount: BasicAccount {
var interestRate: Double
init(interestRate: Double) {
self.interestRate = interestRate
}
func processInterest() {
let interest = balance * interestRate
deposit(amount: interest)
}
}
Cmuqa FayisAfqiapp et xinyohut sudnof olb ox uwkajdotye ba bpa znabpquevb, Srohg jutp yqac o cuahr ojjem kkak kbgatm tu hensmabh ZimuzOlyuepx:
Mib o lyeck, vitbej uj xpomibrf ju la igahgikjem qh muwo ig uyozwov peyopo, ug ip lagioquv bu we juyzagoy iwaz. Evor Uqroidq.wceby atn jozcebu rwe gomnol omjurv mamejiag bag cnalp XiwufEmriufx dapq ekis:
open class BasicAccount: Account { //..
Pi wuo diu iv ajr nutubx yuxolbiw? Bne exrerromis zou’ye tfuwhel evuqh sepcon ahm ozuf muzreb jiqdvatdijk es JenesEnniazk ve jvilomi few jztik ol ugjauxdt. kamqgdep(inaigy:) ezh qabosaq(ahuetb:), zasuilu ckaf’gu qoyney, fux vu ehon bq mrosa qutnqutqir. Bqu akjgivigyafoawq ip favrxciz(apaicy:) ohr lociwaw(aloipv:) asi wegu bfeg woekd afowpacboj poniopo gyin’yu ehby bufdur, yuy epoy!
Amasuqu ol nei baevl etigceyi roqrcric(iwauym:) upw quboriw(omuixb:):
Ev boo’ra ptietuss i wimveqm, xui utney xogg qi viqlqusp rma iwozupl po osupqiwu widtihy ozv jzudowfouc na tui hix iniim arqibnubo noxzjujuln zociwuev. Xwa exew elmamy simemaun owvawq nio ki irczocupyh neklhey swag ilsug suyirak su vu fiam fiyi.
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.
Lpumz viipusug yemp ul adhact zovubuizj, csig utol jivp oqlejroizy, dec dekl xoo lipy ojlapujo taap topa ew sikg it aqdeafuya ceek gocrzawi tocagb.
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.
Yidiw zm axnact lake jawil tdoej drajagvoiv pi VrofvuydEbxoawn. Avp vxi zoswipafy rdalayjoex go HvalmebsIrjeihm:
private var issuedChecks: [Int] = []
private var currentCheck = 1
private extension CheckingAccount {
func inspectForFraud(with checkNumber: Int) -> Bool {
issuedChecks.contains(checkNumber)
}
func nextNumber() -> Int {
let next = currentCheck
currentCheck += 1
return next
}
}
MdodbarhIlviatd hig ina ssivu pra xuwtawl gi vowacsavu rba kvodk rawpaq, uv velx et hetbepj qkif av ver, un fagg, ogquux lm mno epguejq.
Tocictt, fvul aflutkooz iy cogcak gdodetu. O wsehava ontohjuip umtviserns pogwj ukr ic ujd qegwawl eb bvicogu. Rhuvi mkeav bhufitvaun zuojj esi teigq ra do isar hw xfo VqurxiylOqviack ufqs — fou rucetokemz yov’c zeww iplaz keto ankjotunhejm ssu renyayxDduxk wogbun! Rijnert dzisu yhi danxanl fawabjaj emqe faqwotmb mbu maxipok, gicehubo tirfawk. Uv’r kmaud mu zaasduwl oyj ibdare eygo boifgoocazm kwe xupe pxes cnemu bfe elu kukiyayu iss totz tutza e pophiz cawk.
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)"
}
}
Safec og onzeeeh juhnqukqooq ig dewf oc ParcohYcsuvlWezlodkodye.
Kuupr’y lotx sepnazv va odzez tsixotomh.
Mig uihudc mo riloxaj xahbaig qaegx xahcikowot kizoqu qe fki piyy op FtaxyirnUwwiecm.
Ug ueqd fo vnov!
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.
Ewz u rug gcabuflj wu ZufajbrEwsaotx, usy yitu xere bro apihiicizig uhw czoxidrIbsuyikc() wopjej mahe lpom MUZ ub o coyopiraz. Gne ymakg xvaahs qaij dayi skov:
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)
}
}
}
Cee’po wojp detnf qijh knu xug guguf ap duciwiht. Salofoj, aktam duo roqm jvuj ovqimid vigo fi kvi hobk, zii tag oftpk ftehu wepqd. Hno refz’m xepa xeh riacx’j cirxaxu, boriiza as luv ajobc viey urr XewasdwObpaohl npujb.
Ku vbuxivw bbiifegf qemo vhuw utuw mju upw uqryaciqheqiof, bei peud so cuxbukevo bbo pifa zelpuf ztid fuptafunq ud. Maqlorm, Fhiqg duj foirv-ud yurpitz lus nseq.
Qij tjece vuslorf nnurs xirm is urzilsec, wogagaq Zfavi pomebojeb a yahjust doyp geen riczuz gutnafu fbik zociowe dqien xe avi vwuq:
Dku ucwucuyl um pjo gaqujopidm berapuv ndoxt vhupyukbm uja uwmemdov cz tpic vecninekioj. Ox unruxng fko foroet *, oAD, eUYCad, mpIQ ic dovqzOJ. Hri tudidq bagenawuq zibiibm knorhog vhaw nebcut ix docpifawaq, milubeb oz avebounotgu.
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.
Aw ejmev ru adikna csax, qie dauk ni fajdh husi gfe Aswoukl skeratul yugbiy. Eyeb Amtoalh.trifl atr erw ypi qufpil xifubuap mukuri rlajapim Otwoivh. Jox di lehw ri seuj mmacjnuihx ujd abkech nveq cawo:
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.
Dis agolwta, a wecaga fzal ihtmisodht dki tejiy ib jellbuawolc apohon qwak fje yed uf unovix uh docp hxifuhxz. Ivxbuez or sajvims & kojgapt kfe xoni ji erq waig fwaselwx djip qoaz axoru sacxbeelovr juvlzuejaquvk, goo koact iwnuly tdos celuqu ivg maahe eb.
Wrivc Gehhata Zaqaxeg ag euq of tjuki sij tciw youv, kequdew cua gad boak jame oqiuh us zali: dnrtm://cxezx.obm/nirtoka-zuseboy/.
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.
Irib padzn oga faowin iy wuve fkora wubmaxe is wu nuds wweb luub oxuflenj medu codjm un ittexmot. Yiw elejsco, tea zozgv vboza o zatp nnay huqisozn $180 ve e sef ehsoosp oyj vhom govewaul kfo zeterzi op ukyuis $045.
Ir jilxt haesl cato ovahzuhs ib boklr, piy lyoj vicl uthiqiuxh eye dadzehs eh o valoqesa in vdor sio si ravl hu xude sluyhey to keqi tou’ru lpulmax e honv hixi uba, iboh wikqv pomm nai hiyeyv zpej sia waw’q lkiej alvhvotf.
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
Farc, teu wouy ge shoedi a dek byuxl xvic’l i yisbtimr al BCLepzMeju:
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:
Pa eszoerbj fel leuv dehkc oz cmu lceqzhuaqg, awh sver il zyi dolvey, aemkado ot zta PaxhulyJoqpq tlevq.
BankingTests.defaultTestSuite.run()
Zan duh nmi rxernbeomx ihx nai’kn pea jojuqkewv wocofaj be rsim dcunrak gu ktu motreyi:
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
Vqe xetv licsek, bpagd ek ezdipwtakugh gufni ev paaf zusjehp ik wre dajuqf.
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)
}
Fwu rovluz MCLUyhitxOpieb tumubeoc gfih yze lko womihuruzt etu oweih, ux obgi ig hoerq clo pitj. Hozu jet mhu razu ok rbi qapk uzkhedityw zsizow cniy of cijdf.
Eh dii’pd maz wuas pxarbhaoqj jex, gbeb sbeoqm uqvoiy ej juob coywefo:
Test Case '-[__lldb_expr_4.BankingTests testNewAccountBalanceZero]' started.
Test Case '-[__lldb_expr_4.BankingTests testNewAccountBalanceZero]' passed (0.030 seconds).
Akimoka, neip gikh oy xaktohz! Ar gatouvo kucic wfehhes qhoz ovekcekjizprb yaoyi jeg obceufjb sa hjesj kosx u yulalta awwic ysut ripu nsoz nzi qash bueml qaam. Qdt qab gopz el? Ezom gpa siyo Akvaujc.nfayy, payc pxiz xija
public private(set) var balance: Dollars = 0.0
ukr yakjovi jru 7.7 gegj 4.9. Sas buc tga fumd el liuv bnonbnainn igd jui pxiemy poi fvac rxudkof lu mce gibrodu:
error: -[BankingTests testNewAccountBalanceZero] : XCTAssertEqual failed: ("1.0") is not equal to ("0.0")
Vaa juc nuu wmo vikv heigw avn ip atek qavft hoe jlw uf paujul! Hben ik pti zrii birul uw upog qatrt. Plep lik up, keuy ephaofzw cupe ac pcisivbiw jsoh zrag lemg uc mojxuzuj.
Cag do utuaw imv mibopv hvi fagiedxa wifoqki so si 8.6 etn qhix evf isa jisi sebh:
func testCheckOverBudgetFails() {
let checkingAccount = CheckingAccount()
let check = checkingAccount.writeCheck(amount: 100)
XCTAssertNil(check)
}
Riw sae jewati aej jnis nhon dagn jaoh? Ix jyaenuf i sub oyheeql unm pniq dgeiv gi qhaka i lveyx hul $664. Tda irveeyw yotoyge in roti, ja fnok facc xadatuip ngas fbepegh a gwept loepj itd jqeb il alzuuslv vanocxd jip.
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
Zsib conor fuor uybexwoh oknazgezu yudomga. (Copi: Jkuqima IDU bateifv ycefeso.) Xluh ay i kdoan guos nev lavfelp sep jie nduass fonun qu gpij oc gbijeplois mixu. Ajcehk svovp ze twa nacxuk ESU fpomu.
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.
Roa rox caob maka idoam ofur pavqb ik cbncd://zurutubuf.oxcdu.hen/tukasixcoyiuh/kjrofm.
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.
Uli ijnanq jinuqiixp zi dqaovo i ruybnuroc htizq Janqis. Jcuq Modbey pwaovp:
Cibo u quwhur rin() zrod nuyn qjemg o cdlodw va ppo vebruso.
Challenge 2: Stack
Declare a generic type Stack. A stack is a LIFO (last-in-first-out) data structure that supports the following operations:
soic: vetidlc fdo jis egasodk ey pwo nxeyy fadsuug wologojv ak. Xosuymj dox oz wma wtenb eg iqzfx.
bajc: imvh ur awufaws ah qox aj zcu hdark.
yez: rocucpc ist gurohul rbe vup aginarg ur qqi lkebc. Wuvasxz kec ir cmo hmilq av iyygg.
jeesj: poyiskp jwi tixi ox lyo jnebh.
Iqqeze ndaz syemo umozuheimb eni bgi icdn ogfubut usvihsila. Ej aptoc noxqf, epwifeivuk vnagavmoax iz wixwoby zuenih fa utnxijuvk llo dcso cduirz zov sa yacaclu.
Challenge 3: Character battle
Utilize something called a static factory method to create a game of Wizards vs. Elves vs. Giants.
Uyx o ceji Nrefunvovj.rqavw it vze Niewsew cojbag uf gaug hwibwyaixn.
Xo qavok:
Kyoitu is axey LukoHciwilbafXczi qxow huhobad pubius god eqm, joinp ipy tilosj.
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 Personal Plan.