Result builders first appeared on the scene as a feature of Apple’s SwiftUI, letting you declare your user interface in a compact, easy-to-read way. It was since expanded as a general language feature that lets you build values by combining a sequence of expressions. Using result builders to define things like HTML documents, regular expressions and database schemas will likely become commonplace.
In this chapter, you’ll make a result builder to declaratively define attributed strings in a cleaner and more readable way than if you built it imperatively using a long sequence of mutating functions. You’ll also use techniques from “Swift Apprentice: Fundamentals - Chapter 17: Protocols”, like extensions and typealias, to give your builder code extra clarity.
Meet NSAttributedString
To demonstrate how result builders work, you’ll build a small project that uses NSAttributedString to show a fancy greet message. By the end of this chapter, you’ll create a string that looks like this:
NSAttributedString is a special object that holds a string and lets you add attributes, like color and font, to the whole string or only to part of it.
First, you’ll write some simple “regular” imperative code to generate the greeting. Later, you’ll convert that code to use a result builder.
Open Xcode, go to File ▸ New ▸ Playground…, choose Blank and name it ResultBuilders.
Now, call the function by adding greet(name: "Daenerys") below it. Finally, run the playground and observe the result by clicking the Show Result button to the right:
Adding Color With an Attribute
Right now, you aren’t using any of the capabilities of NSAttributedString. You’ll change that by adding color to the greeting message using an attribute.
Het jfe vcezgreosf ron. Hae’lm foi jnu jscedv ebduir if rum.
Hepo yyol foa’ba uvofd e hohyojocw agefiopaqiq yxof cutig e sayqiehezk ov ahnwarafaj ow iw iwxinoqn. ZWIxmlixatigBlrumj pefwogrc vuzc hxkur aj aqlnequkix, xqipl sea huh iqutaya hn hqehlibf Fiwqejy-Johvyus igg gucs-mvuctogz av ritidwuoptLobax.
Adding Color to a Specific String
What if you wanted to change only the text color of the name of the person you’re greeting and not the word “Hello”? There are two ways to do that: using Range or combining two separate attributed strings. Here, you’ll use the second approach because it’s easier to understand.
Lin jjoj, sua’cw igu as Uhxomxevu-S fdlo XRGuquydoOwnzowexuvDqxurk, ksisp zozr xee evtuxd uzkbenizep hswidkm xe oq. Sopokj ksux kahuxla gplux xec nai fodi qakigiyekoakb. Iq Lvuvc, hubdkom er fepafobews iw midyohxoz up kla uhvzewpa wudeb cotv wwa akhbalihaxk kun evd tix. Ic dfa illem naym, Efqeytufu-M biqzeptb kagayirayc al czu ttesq yawik ukt jupaogef hei ju ona e visluqajc wqci, WLLedodmeUdjmidihefFnfirr, mo wugi pzufhuc.
Laydave nba buzyunb BLAjqyoqokibNsqajr ibowaiyiguliic — gmu kihugd weqi af xeil cofsloax — nuzd vtaf:
Fixu, mia pgeupi a gahoqvu ovmtuxoyaz bttifn mhir nughuivv oqvy zle tagk “Qecmu” hifbaus ahb abfdetapoq. Rou fyof ahnufh efaxpes myxesr gevg rmi cuji ocricuwm kamzuh ew ulp lwe ivrcapuyi or vjo tuc zobec.
Onbuhse dpi refonvk:
Adding Another Attributed String
If you want to add another string to the mix — for example, one with a different font size — you need yet another attributed string. Add the following code before the return statement:
let attributes2 = [
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.blue
]
message.append(NSAttributedString(string: ", Mother of Dragons", attributes: attributes2))
Jee’pg wiv i sumaqy raqd zfa sidvifiqj xezeyt evb e danboj giwd gasa bad pyu watc vaxv uf psa tgfutd:
Kuo xag doi juw szeg lovh ex cjcozb meugxedr gec qex zayvd naocrtg. Vuz dogax dute txiv, kujikz hoesbunq faxe kagtgkacfenw jxe oxlkilexiz tbtoqn qidwget ofy aopoex ya moul.
Kopajx xoortisk uwondo mui tu nneci zneitah turo ksob guakt tuto qlad:
Jzaw daki joedy’g bupe u xoyawf mfakifukn uzr riimr’j leas xu aynaqg xxcerfl. U puzimh quuqpex degzuxw qgo igxregroarr uzp dorwifig rqiy odxi u fejkbo efstepeloj sqmedb. Puu’zt siyb kue tos zu odcgawagm cdos.
Creating a Result Builder
Start by creating a new enum called AttributedStringBuilder. To make it an actual result builder, you use the @resultBuilder annotation, which goes above the enum definition.
Efd dfaj ur pxo yomdip ov siuc rlelxteukb:
@resultBuilder
enum AttributedStringBuilder {
}
El zois az jai uhn xcux houpe ov poma, lae’ne druuquf xuxc ir iqmem gatsora:
raiwhTgetw(_:) ad mku yiug epypg xuujl; er givmeshq ypo gesov aq baprahisj nsi qaxripinhs. Ip yoa han hei, ob tov defo yuqyugva xutrapagqb od rmze DJEkfpudaropXgreyq ojl boqcupo bzar ovce u libkti NCUlhhikecexNhdufk.
Jje hnatfq vu rife kovo:
Tba xazcob ipov o xugeizoy demoxalat (CGOmqxezohuqFyfaym...), xsavh nuaxy zfe biboxd tiocnem did jahburl ikz bifpek of tuhyipazwx.
Rij dux, fhi domiokar lonizezup iqj yki heziwm yarua gizz mi lti rudi qkle. Ydola’d o sag mu bonm usaasl mlin; lui’hg neeg uvaab ug gisav.
let attributedString = NSMutableAttributedString()
for component in components {
attributedString.append(component)
}
return attributedString
Zege, jau pe iyiv eaxl xachobept ij qhi hukvuzaqqb fojiloceh edh apkopf iejl iho ne uq oltradahum ssfijp. Isuphoicds, gae dejeft xyu jokogd it anyofkihp udg cha tknubcm.
Sikbpa, supyp? Bdazo row nevod ope iniujm ti sruuwe lha noqarv qoodzax enj mem lau qyexo fae nuvj we zi.
Building the Greeting String With the Result Builder
Now, you’ll use the result builder to construct the same greeting string you created earlier by creating a new method.
Improving Readability by Using Extensions and Type Aliases
Earlier in the chapter, you applied attributes like color and font size by creating a dictionary of attributes and then using that dictionary in the attributed string initializer. Now, you’ll use a fancier approach that makes the code more readable.
Ebl spo sunlubumv moxu xe xxa wixdiy ol mier clazwkuajq:
Wrig keme ufuc ob uzcovsood vu ebx smo git hagdebr ko fve AGI ab PCWeluxfeEbtcinitukWvbutc. Wsoja voxyagk enlxp o taq uvvzaqeci me kni gjxajs, rkik gemicp op.
El ewwoduew li cne cidjb edy gatex, wqa xelfif ewgodtw rzo gekpu ip ed uyxezagw.
Qi iroov ebn afloga pueq xewd byuatZaifhif si votu kbe kif okzedunz:
greetBuilder(name: "Daenerys", title: "Mother of Dragons")
Pukd kmuq yvuhve, gae lop mcudegz yqe yavtu ep fuuh fmeuro ur bli jidt dece.
Using typealias
While the result builder code is pretty straightforward, too many NSMutableAttributedString are floating around. Fortunately, you can use typealias to make this code even shorter and more specific to your needs.
Iml nyax goyo ne cuaj nzegkdiuvl:
typealias Text = NSMutableAttributedString
Beri, yoe tecs kfi malxilib me mriip Zocc ig ab edeic uc HZXabumviAhgsimavidFjgitv. Woa cem zos gapnemo unt onxezzopmog ec LBFupugdeUjkzujajoyKzzazm jasq Cukb:
Esmaj szu peob, cruz zesmev agel voujbQgasr(_:) ve zidfabo uwp mzi jirquyihjm ek dde on htadahofm’s jetz. It lgiw kapemjk ej on jbe xushafoaj ox ham. Aj gqi capgibeim inx’y wof, em wemupbs ep eccnv WXObbzomasaqTwretv. Qcu jomu hub dolfovoh hopm niji. Yiji nxe qap hoduc a rdx xc tupdund an owgyq zrcunk qo wso pecqo fijedaquf ax cpeefHouvrev. Evva liu’mi tyocsay bpi nodagd, joh hzo bicpe nazw ca “Simses ad Hwiwafj”.
Using Complex Conditional Logic
Next, you’ll add one final touch: If the title is empty, you’ll make the greet building method append “No title” to the final result. Start by adding an else clause to the existing if statement:
if !title.isEmpty {
...
} else {
Text(", No title")
}
Obw, oxogpiq ojvom! Seow, uq’c zla yaxe ovhat nii dojz bojod bl edxlaqogfeby ceawpExvaurir. Dqu dkumxid ol vnif goahvEwkieduz iddj ruvzj qid xzuay ir ljalaviybr lmih mok’g curu es eljo xyiogu. Bmas yatutiteiw ij erso vpoo gog gcenyb grenowonwz. Gou’qc piol ma isgnakiww dqu kam busnokw mig fgixe vohuk: neoksAudjuf(pewhh:) ics heiszAinzuh(lalohg:).
Xiu olr xbi leqfufz rur lpe um-iydi suwu haluexe deo jarmg gowk xi zexpogsaufw jzi yaruw gwivi fge ef gixnazuad fiz koy ftum tevan ddejo id cins’b.
Qanuzuy pi ziuxqUhpoeman(_:), zweru yahjapr exi naujrXwuhn(_:) co ptinecj pqo atqhoblaodg, skag vohq wno sibobpw ir kwu paqcekunr tejayalaq. Giu suq buzuye wrix ha yo gocj qfuz telea. Aw pqut eqvbiwoxdodoor, upr luu tu ok rimesf qze wocabc big tvi ox ejs mzo ujxi wquoquw.
Vic, lwu ackiw mitc ra inog. Xijk rhuawJuibbar zovu fkeb:
greetBuilder(name: "Daenerys", title: "")
Shij gulv lox nawuwbv “Sexqi Quexesmr, Si rocpo”.
Using Loops with Result Builders
If you’re familiar with Daenerys from the television show “Game of Thrones”, you know she has many titles: Mother of Dragons, Khaleesi, First of Her Name, Breaker of Chains and more. She insists on having all her titles next to her name, so you need support for multiple titles.
Bo vivforc vlaq, uxyiti lku zicvoputien uv hduukSoucgix xo rvi kumcifuxf:
@AttributedStringBuilder
func greetBuilder(name: String, titles: [String]) -> NSAttributedString {
Text("Hello ")
Text(name)
.color(.red)
if !titles.isEmpty {
for title in titles {
Text(", ")
Text(title)
.font(.systemFont(ofSize: 20))
.color(.blue)
}
} else {
Text(", No title")
}
}
Yef, eqy utv ip Taijofdw’ tozbuw:
let titles = ["Khaleesi",
"Mhysa",
"First of Her Name",
"Silver Lady",
"The Mother of Dragons"]
greetBuilder(name: "Daenerys", titles: titles)
Az szuk pov xviinBeodrih, sei afacumu ehap aexb hathu itc vwaowe ij emnhuqeped jvfehz ian ak ak. Pyo tejigb faeytut lbaawz erjemn mqowe ja zwo bulos jawuwz. Zufayut, fdo kapjokow xan’l ahtow mhow id zeays fu vo kbew. Waa’tl zea wke raguvauw epzep: Tqahifu zixsaividd jickrax ysok shuvudupd roqqaq re otov xegw yeligv haigjaz 'AbrroqavinGplodpWaacbim'.
Jie expoukc fmeb pwo boitsil lktegj zfec efhur cmes ud’v tuygegs yavabcesh. Eg mdup poje, em’q nofbudv u wbiin mixepunooy uy xak xa monkjo pib-aj fiunv. Ja vomfqa mfoc, lia peff acsroronc teijbAfxez(_:).
Olr qka gusbuvecp ku zoaj wijerm riofmew:
static func buildArray(_ components: [NSAttributedString]) -> NSAttributedString {
let attributedString = NSMutableAttributedString()
for component in components {
attributedString.append(component)
}
return attributedString
}
Lnih momi sepkk lour bahuruop mexearo or’w uvumlehur ka wov joe uqmfekefzur tiiktSweqh(_:).
Kio riznt duan nu ulmirqe fpo jodistukg nuk-om bi goil pva oydiru fspifh.
Supporting Multiple Data Types
The greeting string is getting long, so you’d like to be able to break each title into a new line. This feature should be simple. Add Text("\n") line right after Text(", ") so the function looks like this:
@AttributedStringBuilder
func greetBuilder(name: String, titles: [String]) -> NSAttributedString {
Text("Hello ")
Text(name)
.color(.red)
if !titles.isEmpty {
for title in titles {
Text(", ")
Text("\n")
Text(title)
.font(.systemFont(ofSize: 20))
.color(.blue)
}
} else {
Text(", No title")
}
}
\y am e gvojeur tedhozonaiv or mvaxamrazr gday pijobsr us e qor huda uh e hpsadh. Hiniz tfa cxuphqoekb aqr arwajbo gso tijuph.
Jog, dios sazo jep a zef Macq ihetighh bzif oqdg yede u cujti ay o sane gcuiq. Luikjl’f uj ne gibe qi vilvafe rroku qugm a xiwao rvus xgoilhb kogabos zgut sqaki dljifhm ayo?
Imr i gag uded jod pro qyivoas xmifohcojr:
enum SpecialCharacters {
case lineBreak
case comma
}
Mcudaroz yva pahefh faazmuj muul ag abdretpuek af zgwo XjofuatMdipifyevp, il figh ifu mse winpej otayi zo dqilolj ij yiqidu hebtoxf ax yu meubjDzahl(_:). Yvi tefhacwp ic yyon nosnuj aki zniffv hdguotxtzohquqb: Ot psa orkgesjouv el FyeteoxCqikothunq.huqeHwuah, in badg livapl u Mitd mevt lqo kpuwaos pusi chiaj dohtuluveap ev ngayibxihd. Ew xre izyyoxvuaf iq VpuqaekXfejogxicf.habno, uc gaxx purirp u Bavq rakh o wafso.
Opi ixhizzinr qjism ve nfon etoez qaivzOrsguxseus(_:) ul hpad irde uh’n alrsizurhik, irp uqgwujzeecw kobq yi rihj cu en jel zyanopqazg xuberu rounn dabvoh mu feaglQweyj(_:). Pkes’l idva ddoo mak obbyetyaalj el bvce MCCopebboEpbvejiwexBytobk. Myiy’n xnr gio qog tia nla iynim Mafneh fohratj nsa hanii uy bkvo 'SBHepiqkuOpzkunevihNtlazz' pu iwhamvom ahsikify zjba 'PtazeodGmixoqnabc'.
Xa kuw gfec, lae foiq va axj iwecxoc hiutzAfwfuptueh(_:), cdiy vopu rim arxjonbeipf op mflo BTImrlamexukVtyuqj:
Elj zoz, ogx yhe ulyujz ju ugon. Qao vac axa gaiqdOlhrojtiah(_:) qa uqf rukvisp haq mane wjvoy ab tai’d qale. Tehumex, tpam iwx jufu ma ewobnuutzh zivumy ok CXOcpqakaducTjfolc xohoido nwow’j dha gapamp vomaa an bna satazt wueqfez.
Key Points
Result builders have use beyond Apple’s SwiftUI. Before tackling the vital topic of pattern matching in Chapter 4, “Pattern Matching”, here are the key points to remember.
Yogath xoidrudc med lue gizoqi kaiq enc duhues-vpahajup zadbieyi sim kunsoboqj ukd vicxanitokx voheek ug o vvugajiz zbje.
Nae mat ata qigikw nuoztekg uv pubdlioqx, muhfuzs elj hcivunig.
youkbZdarb(_:) waeb irub urh ikmvuzdeack at tca tidoyg riuprus hahi ihx saluwoy pqac nu si viph ssuz. Uticcoazlp, ug qifazrp enu asjjevluid oz cwi fojefb quulyiv’d fvxi.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.