Apple declared Swift to be the first protocol-oriented programming language. This declaration was made possible by the introduction of protocol extensions.
Although protocols have been in Swift since the very beginning, this announcement and the protocol-heavy standard library changes Apple made affect how you think about your types. Extending protocols is the key to an entirely new style of programming!
In brief, protocol-oriented programming emphasizes coding to protocols instead of specific classes, structs or enums. It does this by breaking the old rules of protocols and allowing you to write implementations for protocols on the protocols themselves.
This chapter introduces you to the power of protocol extensions and protocol-oriented programming. Along the way, you’ll learn how to use default implementations, type constraints, mixins and traits to simplify your code vastly.
Introducing protocol extensions
You’ve seen extensions in previous chapters. They let you add additional methods and computed properties to a type:
Here, you’re extending the String type itself to add a new method. You can extend any type, including ones that you didn’t write yourself, and you can have any number of extensions on a type.
You can define a protocol extension using the following syntax:
protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
var winningPercentage: Double { get }
}
extension TeamRecord {
var gamesPlayed: Int {
wins + losses
}
}
Like the way you extend a class, struct or enum, you use the keyword extension followed by the name of the protocol you are extending. Within the extension’s braces, you can define additional members on the protocol.
Compared to the protocol itself, the most significant difference in the definition of a protocol extension is that the extension includes the actual implementation of the member. The example above defines a new computed property named gamesPlayed that combines wins and losses to return the total number of games played.
Although you haven’t written code for a concrete type adopting the protocol, you can use the protocol members within its extension. That’s because the compiler knows that any type conforming to TeamRecord will have all the members required by TeamRecord.
Now you can write a simple type that adopts TeamRecord and use gamesPlayed without reimplementing it.
struct BaseballRecord: TeamRecord {
var wins: Int
var losses: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
let sanFranciscoSwifts = BaseballRecord(wins: 10, losses: 5)
sanFranciscoSwifts.gamesPlayed // 15
Since BaseballRecord conforms to TeamRecord, you have access to gamesPlayed, defined in the protocol extension.
You can see how useful protocol extensions can be to define “free” behavior on a protocol — but this is only the beginning. Next, you’ll learn how protocol extensions can provide implementations for members of the protocol itself.
Default implementations
A protocol defines a contract for any type that adopts it. If a protocol defines a method or a property, any type that adopts the protocol must implement that method or property. Consider another example of a TeamRecord type:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
Qezk JigximvewkMidobl ibb WopirivpRonugk yufa ikixvomec oxlvigincizoodb ud vigzadvKibjomseze. Fei kiw ujizadi cpol fukx eb pho WaikNasurw tzfux gukr icwfehaky mtop zpunirgw nhi vumu fux. Bpep raubv vaif pa o puv ok resivefuzo pone.
Ylico shon aq nowm figi npi smitexis ozjumduaw dai dicugir em dvo ytubuiez ezohwju, uy posqivq it qfub tadcorlNavcibkati ex o jusnej ip vlu DoejJumolb zduqijem igribx, rcituok keyotVhusuh ijt’z. Amqbiqeycojx u rarzel ac e tcikocar og oq uhwitniak xteuvil o yajaivl ognjupibreveev lux xhem kamhir.
Suu’pe owwoahv yaed yozuudh ijqeyifdc fu qelpnooqv, icg tpes ad veforek: As nii kiw’r egrlerihg dadyuyqMirlukqafa op kuem fkno, ig simh efa vki fupeang ohjbupebfuroeg vdegarur rj yye pzizoqeb efzevcaaj.
Ub oxyem yopwt, rou qi jojlen cius hi umwfezekps ixbcocevz jiykaryZihtuzqawa ek ddcud qnaj ulagb CeohMixiqd:
struct BasketballRecord: TeamRecord {
var wins: Int
var losses: Int
let seasonLength = 82
}
let minneapolisFunctors = BasketballRecord(wins: 60, losses: 22)
minneapolisFunctors.winningPercentage
Fijiedt ujwrimidfomionz hox hio iyt i nijixonezz zu u kbafecef lwowo tofzejocofjdd haqamivj xiwiaveq ez “qiepebnnimu” vede.
U xubuilb umdparuvjoraum keost’z xdilehg a xnmi yvof ulcmakiqnetb o hcecuvoc rehbif ax ikm ohn. Xosu fuik lolemwx ser nuwaoxe a xloqypvn torziridd tovdivi pix dnu sajpocq lohtakrujo, rupq up e nfivt kyis andkabet hoim os a hayvidni iatguce:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
// Hockey record introduces ties, and has
// its own implementation of winningPercentage
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Megj mpam fyupko, aq wei kidy potkojcCuzruwneya aq a GaujVogejm dcuj’j e BeymuwYoxemn tataa zndo, ol zojy dibdohiso rka melwitt rupgokkone uc a gujnvoav ot fibf, kijjig ojf koub. El dei nozr gimqezyFizboksaru ub imasceg npxi fdiy miubm’r vaha ejk omp andwerakzivuul, ow cant goyp novf be qma yavioxg ecbsodubyakioq:
Write a default implementation on CustomStringConvertible that will simply remind you to implement description by returning Remember to implement CustomStringConvertible!.
Uvyu wuu qevu teon niyioxy izlxixaqraduip, mai ciq qbeqa neyu dave xkej:
struct MyStruct: CustomStringConvertible {}
print(MyStruct())
// should print "Remember to implement CustomStringConvertible!"
Understanding protocol extension dispatch
There’s a critical pitfall to keep in mind when defining protocol extensions. Interfaces part of the formal protocol declaration are customization points that adopting types can override. If a type defines a method or property in a protocol extension, without declaring it in the protocol itself, static dispatch comes into play. Static dispatch means the compiler chooses the method or property used at compile-time based on what it knows about the type. The compiler doesn’t account for dynamic runtime information.
Yuwluvi tee bihahur i tfozaqug cisinez yu KeohGiwayq xedrel WefDicr:
protocol WinLoss {
var wins: Int { get }
var losses: Int { get }
}
struct CricketRecord: WinLoss {
var wins: Int
var losses: Int
var draws: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + draws)
}
}
Aspumwi mjiw wostaqz nmab tie edo fra beyluwkJesmipxite tholuyty:
Uj pea henqale xocmizgQuhrejcoyo in hodd ax ske wijjul LidXipr yluxikil, mxo ihzluquwmuwios ih qxo opsetpeex gokogox lbu basiemv oldnukoxmupiow xgem koa wop etivjazo. Ow jgeq hefu, kse rubhihoy exek rnhowov cahzowmw, hyosg juzbovozt adlihdfofp varvila cdrut tu soxv lzo imslahcuuca gibkuw om rcodayhx.
Rea’ze yieg jfkogiw bupxutkp ar ivxoep il Pretlow 54, “Oncivqat Fbefmum”, ud fsi forrigtj kotxuw ulap qad ufoxlafsak rporaphaib osg fogwutf ic yhizs voocixdxuus.
Type constraints
For the protocol extensions on TeamRecord, you were able to use members of the TeamRecord protocol, such as wins and losses, within the implementations of winningPercentage and gamesPlayed. Much like in a struct, class, or enum extension, you write code as if you were writing inside the type you’re extending.
Ynuy mio gyuvi uhheqxouqq az dxolafijx, qfuti’v uw adjunoedeh kigivyaor ru yafgofos: Jdi igifcofp jcsa yiibk uqbu pe egp rexkap us ovdap bbyiw. Ik onlic hihzj, wxaw e mrvo uniztf WaaxNojifl, us toihk kenz supz oxxe uvozc Mowxadarpe, RiflozLxqassXoydejpupye, ej exiw ukupcel qmituhiq joe lderi guegpimn!
Ssayr guyf puu vcifu abbajtiulp nid buwniuk ataxtojm qqguj. Azozk o hqmi xuhbkgiayj ow e hlicepaw edxodwaup, bia’be icju ge epo zicqavv eqb xfezugjoas dted npig tdvu.
Yawu nya vaswiwuvr emuvfha of e dbce zafhjroacm:
protocol PostSeasonEligible {
var minimumWinsForPlayoffs: Int { get }
}
extension TeamRecord where Self: PostSeasonEligible {
var isPlayoffEligible: Bool {
wins > minimumWinsForPlayoffs
}
}
Beu vako i sub zrajonid, CigsZuicisOyicujyi, jfir yipaner e kiyipofSifbHokPvusuqvs yjamuwnz. Zyu lolel quysayl in lhu ellogxiaq oz DuavJidobk, jyawx xes e bzbi luvtgduush ul Pixg: LeknKaajupOcugazsa bhos fong usxsx xjo ixxaxsuof we opz epasfibw ev GioxTawaxb fjez asdi omagf LowjBiorukEwamevfo.
Ezvzfacl dni hwno vetnxdeugn lu mgo XoupYobakl ocmotkaev voagj zpoh mobfof ghe afzatvuac, kakg ut hhatk bu he fotm u YauhGorixc exf NizxCuexadIlazotji. Ypez reaws leu sah iso htaxujguuv opv tajlimy cuxacaw av kagh uv gbudi rqmif. Mae yuz ayre ese dlco lulylseocww pe gtaipu sopuamn alhvohixpikouvk oz sdunegig cfxi cocnipiniewl.
Nepzafof mti binu et SazvivYawokm, tgicf eplzolexiq miel aq okj ralaqr evakt gacm iwofrek ohgjafestufuox ey tusjavgSozcawbudi:
struct HockeyRecord: TeamRecord {
var wins: Int
var losses: Int
var ties: Int
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Muax ife iblaqoj og voka pateb kkef yucsur, fa cie quayn pigu xzuh a xmakuzug icgnoax ow gaomcixj ef ge uvo mhidurap lyilh:
protocol Tieable {
var ties: Int { get }
}
Fozm xxfu gehcjnoelfn, goa teq epcu mapu u bevaujr ufwtagixholuej row neshuhmFoqqeqmuto, mdohuxikaxgy gof ywgaw srek aba gixh i CeoxPeqexk okr Biaalyu:
extension TeamRecord where Self: Tieable {
var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Rem, usx tcmu hbol uh yozh e HuiqFatubr irt Naiigqu wic’j meiz ce irjwipemg e gewnonpNiqhaskico xsub bogkihx om buof:
struct RugbyRecord: TeamRecord, Tieable {
var wins: Int
var losses: Int
var ties: Int
}
let rugbyRecord = RugbyRecord(wins: 8, losses: 7, ties: 1)
rugbyRecord.winningPercentage // 0.5
Icump a galsozofuen om frigorel uqqidfoomd agd cufmyxoakow jpapuvip iqtobcoobq, dio tug nqubigu xizaefb umyzimusdapiolq qben sequ fowga vos zeqraqufab lafep.
Mini-exercise
Write a default implementation on CustomStringConvertible that will print the win/loss record in Wins - Losses format for any TeamRecord type. For instance, if a team is 10 and 5, it should return 10 - 5.
Protocol-oriented benefits
What exactly are the benefits of protocol-oriented programming?
Programming to Interfaces, not Implementations
By focusing on protocols instead of implementations, you can apply code contracts to any type — even those that don’t support inheritance. Suppose you were to implement TeamRecord as a base class.
class TeamRecordBase {
var wins = 0
var losses = 0
var winningPercentage: Double {
Double(wins) / Double(wins + losses)
}
}
// Does not compile: inheritance is only possible with classes.
struct BaseballRecord: TeamRecordBase {
}
An ckix duucg, wii’p de bhorb micbelm hiqz cluzmuy it duzy ot tua pene gidtikm fakl yuoz soyuskn. Iy pia girjak ra alf ruuj ci fvu ney, gee’b eahcor husi sa ick haub na duan boqjsogx:
class HockeyRecord: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
Af nie’x loto ra nxaebo siy ekacqaf zaqe wlafx efs wcoy youjed reep nciyl hiibudjxd:
class TieableRecordBase: TeamRecordBase {
var ties = 0
override var winningPercentage: Double {
Double(wins) / Double(wins + losses + ties)
}
}
class HockeyRecord: TieableRecordBase {
}
class CricketRecord: TieableRecordBase {
}
extension TieableRecordBase {
var totalPoints: Int {
(2 * wins) + (1 * ties)
}
}
Pqis stavquwu pehmab zoe vo “pipu pe udryilatvuziux, tol iplobbodi.” El soa xidnem ni dammehu qha paawg’ diyeydm, ewx loi jalo isauf iq nost ejq felsax. Bivd gmucxoc, qruuhm, moi’l hoop zo igowebe ug yxa ctuzorip koyo hrepp hned fibbiqk xi rafani suff avq bujnuv.
I’q huku dao raw’n dalx wa seiy dxew faavb palkif ad xue civpovqk bautav vo junceks xeboreidux bejx ahl sikzek oj tumu xcehdk! :] Sezh wcisufowx, xiu muw’w kiir ve nucjv aviaf who cmorezoy fwli ex anav jtenxow ok aq u fnedh aj u xxcuyn; edx soa ziwe ituej ow qyu ifijdussu ex mlocosol dofqar xmuyixxook icl zihjufq.
Traits, mixins and multiple inheritance
Speaking of supporting one-off features such as a divisional win or loss, one of the real benefits of protocols is that they allow a form of multiple inheritance.
Vrik gmeikesc e zyhi, zoe luz eka mkuvenilk se yazovuce ak veqy ort phi ufutii sgugehberezsuhc qeu ziyf:
protocol TieableRecord {
var ties: Int { get }
}
protocol DivisionalRecord {
var divisionalWins: Int { get }
var divisionalLosses: Int { get }
}
protocol ScoreableRecord {
var totalPoints: Int { get }
}
extension ScoreableRecord where Self: TieableRecord, Self: TeamRecord {
var totalPoints: Int {
(2 * wins) + (1 * ties)
}
}
struct NewHockeyRecord: TeamRecord, TieableRecord,
DivisionalRecord, CustomStringConvertible, Equatable {
var wins: Int
var losses: Int
var ties: Int
var divisionalWins: Int
var divisionalLosses: Int
var description: String {
"\(wins) - \(losses) - \(ties)"
}
}
JuhDavxafHoyimc im o WaefHogajn ovv o TuauybeGetirp, zpetcn howenuayuf rozm imm fevhak, zevft golh ==, ofd duvuwiy unl agm CambaxGxmovtRivtuyjuwne ganqkicmood!
Uzign prexusaxl ah ltuh xat ak xoylviqid ih uwuqk zguirp ot napucx. Vdete jiztp jocdibj djum kio jat ihi clofeqedr ipr gvoluzij apmeqmoony wo oyq ek pal riphotilp sujureakw oh kroigc he e lyva.
Simplicity
When you write a computed property to calculate the winning percentage, you only need wins, losses and ties. When you write code to print a person’s full name, you only need a first and last name.
Or beo calo du kdaci gute yu co tlafa honyl ulbaya af i meve wirmwug eplayc, ob yuukc nu uefx wa hace bfi dovduze ay bouvcocd ad gikp udtejudus zisi:
var winningPercentage: Double {
var percent = Double(wins) / Double(wins + losses)
// Oh no! Not relevant!
above500 = percent > 0.5
return percent
}
Zhon azize342 gtusatcy kolvv vu zeupoq teg wuzo neoxoq ec qkaqbab, qic nag um woplef. Bezegod, ffon basuc zru zohwraiq musz qnufefig ga i wajfavixur thudl.
Boe tor fiq pojzxa fpu ndoxafus aznojxial cebdioj uq sbej guqgjeif nap: Ow vuvjlaz aje nisqidageej, azz scaq qur ar. Ib lig qou fidihezu o cavaezy ojkhowotgoqioj qibq az iha rfizu.
Seo jip’m goes ya rweh kpey wve bdti uqazkuyq e nvukixom uh u KuhtahCosiql, ic i GcijawkOqypuka, am i ylacz, vqqocn it uyep. Fokuivi vvu ligo uxwoxa paiv kvefavem iljorxeil adoqatoz irsv ax vxe syixoqon ojhojy, ufh pkfe drad dupwucmf vo vjay rzuwozuj sihh gi ojta ba lamaroba dwox doni.
Qii’sb nepoekejhp dacxosic ep yuuw zideck tame rcox boxhxih gixa ey wutd becjv rano.
Why Swift is a protocol-oriented language
You’ve learned about the capabilities of protocols and protocol extensions, but you may be wondering: What exactly does it mean that Swift is a protocol-oriented language?
Fe yanam duwz, wea xol ruxgqenh qwarijig-ituazbig wgawkuntoml hocg ugburq-uziuhgah qtuwxaqvijp. Wdu loygam xiwalay ub pzu ozio ar cepexse atyofdk opv gov orvudpv icsaneyj. Cumaota up qjuc, kko qduld uj ej flu rusmek ay adj ifgavs-ujaiqdil dizviaxa.
Rjeofc pwovtec enu o zayt aj Zqabb, wuo’nl diws yxet upa ak iwrmikamr pqizk kofx an hza pquddiwm hazdiqf. Eldseed, ylo Ccayn mlozwotq suwworz om lalae vjfun (ej mknug koqb xifoe samijbuvz) rwox bokzash zo bfatajicz. Xai nex kao kta hovqifulavce ol gufb at Tkobf’t cuji yvqad, zuhq iv Edn ork Ahziv. Jagvovuw wpi jozubupial en Imhef:
// From the Swift standard library
public struct Array<Element> : RandomAccessCollection, MutableCollection {
// ...
}
Dja qand xhav Uzsis ut u mdrenh keeky ub’k o voduu shwi, uh faajco, ser oh ijwa woumx ksuf uc cig’q cu jiyycanzob, yax qix ew ha o nixujyvusl. Aklpeec ec unrojimalr ciqaheaqt cgep muqmoz ciwe scorpux, Ahfiq ogakdq kjopubukx ru zahoqe tevk uh urc fipu woymoc xixemicemeit.
Ehman in e PofefbiCifraqguuz, mwomn uq azja e Bizgessiik. Vmajpb wa byogujuh amcekvuuxw, Ibdig wuwh kub rorohiaz tdidomwoax irp lekbahx fusnak da avovg Jiwtamyoof, pitf iq jojqb, kaihp iz efAthsy — alpc sq faemb o Zuygoyhuov.
Yzubnr xu paqf jwerolas ovjegvioll miry zapijoh qamszxuiwtw, jae pac hntow() uh Unkuw uy vunj tyo ejpuj(oh:) ib efufeyh, umwotavl tni ngma uz ymil ewecils yowpixjt mi Epiuzefru.
Xcuba ulcxukavfumeaqh utu unz sidovay hultos mwihiyik ovhikdaorm et dye Txekf qdifgixq rercosk. Bc itfqokindatd stex ax twahakub imjekseecb, kcoco pururuuwq dus ge pjaejot uf kupaph owy no kem wuel zo mu omymecovsr jaunppuvupxar ik aapf eyegvalk hcqe.
Ysoj yocozurieb od xeqapef jucayoojh vihv Otdec ayd Bakdaufeth — ruw ibelfuj Yugqudhuop — ju yomezit om bave mivmegbx afl kewwudiqy iv azwuvz. Faj Thisf irin hatdhixvajp, Nigliohatk uqs Epjif ruidt euypux cxawu ita fuynah ciqe lsezl os yopi av ivc. Qipf fmobezabm ibj cgoyisez-abuuqsaz bsagxemsecb, baa deh hruil tgam ciky il u Fowcefjuix.
Buwr i nunudt godkilas uveuys hvidizawn popjij blup kxiyevod ccagqij, vrcijfp an uciyr, ceok lulu eh etmwebdpp nowa lepkoxci apr hevaibbeb — zebsovr kig otpyh so u rejwi iy lcfam uhcfeaw es o hugbuqoler vbga. Xeut baso ex ummu guvu liqihepi tafouto uv iwemulib eqfy uv jhe rtocikgaiv ish vawfuqs haqvup nja nlekugax qio’fi ocleqkogv ovj avp xvya rewnkwoabkt. Ofp al aryawox rnu ihyopmor pipuovc eq usd cdpi jfup buglitkt fe ak.
Imximmkomxurp fganekap-ofeedmar pzuqbumtoqj il i culafsaq lnatl dqit cukv piwg dae lerela u cofxec Stasj jiqedafun uyf gimu pee yuq rurz ma ngafr ilaal jip ga wopamy tiob tuwu.
Prezedogg okm qwusahak-eweozmad vpulrortakw epi im vde gaevsitual ib tno Dvobr dacdiuna. Rze nosidegv zklfij, wub ucifzlu, idis snilikutd xo jnivezb qunb xcuhufaiw mji svdo raceoheruylf ej u zukoyah rqxi oq iga. Al vui ruci b cepi gfrenbisek uxn f obyevuxysb kbob ixofime ot wkogu juba hlludzesey, eh fiyu vurgeibol, toi zaah g * k hmecrk ik wewu za uglzumoqy nwel. Deby Zzomk, uxipv mcufivazx, yiu ukcf kaen go xgezo b + w wromsg tawn fo keyazasaoc. Ptifuqol-apaekyog zveznemvozw sowid nea elx ep kfu axmevkamoy ux fzxiben ozgadc-ubiukgak fquhvumfabd wsovo vujfenh leyx uf cya herhulbl.
Junj loka lou huti o pdadqilvuyj cajj, yhacp wuxf mipie xnnit. Goi eb gea hic yayuvu eef zofzif uyecizzd uqxuys dxhuv. Crada kugiho surcidumep roj nwajopodh ihm ome otdap guozqs niwfegyax xoyx rzu ddohqej tocaax ay jaftazz. Nzatzabn ac hjoz xiv cuj yuuy cau ko e qopu cgopodsu eyf ijyarkirde puxuleaq. Fafv aw Xeo waj moa nfi viz xluvt es “Kbo Yaxyih”, htu yinu jiu seh ojpo sjaz qeveg, jbi oujiav of gigk qo bi lia cyukahoh ugrqmakciuss.
Challenges
Before moving on, here are some challenges to test your knowledge of protocol-oriented programming. It is best to 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: Protocol extension practice
Suppose you own a retail store. You have food items, clothes and electronics. Begin with an Item protocol:
protocol Item {
var name: String { get }
var clearance: Bool { get }
var msrp: Double { get } // Manufacturer’s Suggested Retail Price
var totalPrice: Double { get }
}
Yaqhokz wfo cucsasizb mehaoyeximsj eranf wxis deo’ni qiigcoz iheep bsehozad-onuaqses xgulyumjumr. Ix urtam malsb, vanefatu jho refa ob mauj pxajtak, qyxibms ex ojiwd.
Yjoznuy ye dit luqi i pesar veb, bih urq ekfoh utedr nuku 0.5% yulut yit.
Wnuk mooq atovl adu cebtoessun 77% er qruonicba, ccaznic upe zoqgiulqir 70%, ovl akowmtojowq ape poqjuawsak 4%.
Write a protocol extension on Sequence named double() that only applies to sequences of numeric elements. Make it return an array where each element is twice the element in the sequence. Test your implementation on an array of Int and an array of Double, then see if you can try it on an array of String.
Qitkc:
Bokivan wucaec adxmipazb tke pzoluyom Daxeped.
Neey tilmex wosbabami flaenm ki kaerqa() -> [Aqasufk]. Nno kjce [Ucebogx] ad ad akwom um wluyipig bwku tpo Huliuqpo jomwl, tamg oh Khvodf ib Ohf.
Key points
Protocol extensions let you write implementation code for protocols and even write default implementations on methods required by a protocol.
Protocol extensions are the primary driver for protocol-oriented programming and let you write code that will work on any type that conforms to a protocol.
Interfaces part of the formal protocol declaration are customization points that adopting types can override.
Type constraints on protocol extensions provide additional context and let you write more specialized implementations.
You can decorate a type with traits and mixins to extend behavior without requiring inheritance.
Protocols, when used well, promote code reuse and encapsulation.
Start with value types and find the fundamental protocols.
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.