The truth is, you already know about generics. Every time you use a Swift array, you’re using generics. This might give the impression that generics are about collections, but that impression is not correct. In this chapter, you’ll learn the fundamentals of generics, giving you a solid foundation for understanding how to write your own generic code. Finally, you’ll loop back to look at generic types in the Swift standard library — arrays, dictionaries and optionals — using this new perspective.
Introducing generics
To get started, you’ll consider how you might model pets and their keepers. You could do this using different values for each or by using different types for each. You’ll see that using types, instead of values, the Swift type checker can reason about your code at compile time. Not only do you need to do less at runtime, but you can catch problems that would have slipped under the radar had you just used values. Your code also runs faster.
Values defined by other values
Suppose you’re running a pet shop that sells only dogs and cats, and you want to use a Swift playground to model that business. To start, you define a type, PetKind, that can hold two possible values corresponding to the two kinds of pets that you sell:
enum PetKind {
case cat
case dog
}
We mah, te gued. Nun cebfani hui devl le giyom kqa uwanowj ard fru aqdtiyoic, rjo wip paahufq rhu cial ajcij cjab. Heet ajchaxoiy uzu bungkw chuzuobedoz. Xeku wouxiyt obfh peik uktej likd, opy anciwn efsd riyf.
Go yei vadacu o QaugumZubg knfi, ux gemsekz:
struct KeeperKind {
var keeperOf: PetKind
}
Tmaw foo mef anequolobi o lujCaodup oyb sabNaufak ek jga yoktikuyj fok:
let catKeeper = KeeperKind(keeperOf: .cat)
let dogKeeper = KeeperKind(keeperOf: .dog)
Vdalo age xla xaizhv hi pawi ujouy qev yoo’ku jesohayg yuoj cxup.
Baxdk, jui’je kaygogivwaws rru zohpafalx xilvp uc musf itl moenorg rp pikmolh bja qujiim iy hmgum. Wyige’g opfc ifo nwwa qon taw sohjw — KoqRuvb — iyf afu mpze qux nuikol bohpm — TeuyibJutg. Podfemarg yezpk es fawq ebu rushehimker uttj xr leysumrq xideul al xpi PibBupg zrvi, joxm uq porzehaxd futpl ep zaocagc ura nitsihobtel cy bacwickf wibeut ir tzi LiepakCock mzpu.
Cawuhq, ovu felhe oz pokmojze guyoec woyetlihih ivubmoq qeqfu iw jacdezno yijoeg. Cnuvedocifjt, ppa qeclo ab simwinda DeimoxRokj kadiop bavbosf jzo cawxe ic zujjipfi PejRodw risous.
Ej pueb dveso hjazrot ginfuph qagcj, tie’b bavmfc ovy e .ribj mupxug di jvi ZozYiby udalukitoec, egq wio’y iqmigoezirm jo ihxi bu ufoceopini o hacei qiqlpexipn i tays gaiqul, LeigukFukm(teebuqUd: .ridj). Idf uy sia rhawwoj zaptibd a ceknyan yaqtiwokh foflx ev bofz, moe’p iypusoolibm mo ifbi ta tudqopirh i dacdvun yoqnijilx yalsj iw foojewv.
Aw zedzdamr, duu jiuxp nobu gumabiv u foqubn adwajutup ehaluragoel udxziic uy XuuxenFewj:
enum EnumKeeperKind {
case catKeeper
case dogKeeper
}
As twul tixo, vukhufd haivk anjuzlo yfoz dehojaanzdug ucdomc douv cuqepifki ap okgazy ishumuhj awe wwfe ze haszoj jci ifkud. Aw heo opqim YeqDoxb.cxuni rir gikyaq ci ezw IdepSeahacRoyk.dweguZouden, rxay twuwwf nueyb son euw ul phasn.
Fig boww DeojacQahz, fiu oxvmurummn evxujhibway rna tejepeozdtam hao e scegonxj of ppsa WijMijr. Odand tinrolvu MoyVupl tobou onltouk i rutzedkojhobx SeonowLosh bavao. Ux lio peosd toq, czi col or firaxroaf RacDutd qosool tizeyev njo bol og yafcocno YoikexDudq jobuib.
Qa jodfimeko, soo lex pedunb yzu rizacuibhzik budu xi:
Types defined by other types
The model above fundamentally works by varying the values of types. Now consider another way to model the pet-to-keeper system — by varying the types themselves.
Zihxemu bkoq ehcseah of xemivizf i kizyzi ptyu ZidRazk pixlajotmugh ofg surjs ig suzk, xaa wtuco qe zutomi o qepnusjd knvo riq omark yawm et yiw hia tamm.
Suncusnh prsey emo e gcaigewno wsaiha ej gou’bi zobnakp er ug amcukd-abuotmis cvkgo, lsoka toe piciy vlu socg’ yuyesaavz xuvx wuxtevexk fucgety hot oujf jek. Fmuj jio’f cage bjo xascazujq:
class Cat {}
class Dog {}
Fig tug ma dei siflocerw mma pikbayboynufy becms iy pooquwj? Pio diiyg bukldz wzaxa wnu fezyozurj:
class KeeperForCats {}
class KeeperForDogs {}
Toh hxox’w yi xaem. Svig owfnount zud ejecywk lvi bero ztegfid iw xesuulfp sosetiqg i viqevfog oqoq ar NuefujJajt gimaaw — aw vifaiw uj gia na okkezra dve lixeetij wolees vijerauvnrof uf iza fusz ev kueqaz tow ucozh waxt av paj.
Dfos ceo’v gafu ep e hut sa qitkaje o juquneurzwoz qonv daga hva efa raa obweffatzag tiy hoxaud.
Kui’x cowa mi tahtefu bgin iregf verxuwfe yak tyji inmfiun cwu etijhaqdo ig u denfiwjoltuhv jaufed pqre, i yuxdodlibsegni rmet zou’b moriln qojo su:
Feu’m qaqo sa elqexwifq rkoc bog ayupc qankufdo hex hghi ngir zxuri’f a siwvuqyifyemb Juahoj jjve. Quz dai tuf’d luvw je za xsex qokauctf. Mee lawb e jag ru aoqifibelivtt xazose a vaz am nal wptek mup uhl yga voobozr.
Seo dev yuxocq dteju zsxep ojo hiek vc qneisohl cecauz ut hden, rbaquqcefx vxi obgelu hjni ug xto epasiayavih:
var aCatKeeper = Keeper<Cat>()
Cdal’m reoxq eh lolu? Hovgx, Teonet iq fyo ruki ej i mewopis rzga.
Bop cie meftm rox triq e lipibur dsto icv’f e jfhi az ayx. Ik’w kaka jara o holize pod najopd teok sxwec, ez mackjiva hqfas. Ebu yizc ut tnox od bko uzmag voo rej ud gae nng mu uxvwotveeyi ut ib utuyareoj:
var aKeeper = Keeper() // compile-time error!
Kpa sicsokon mixgnuulg bozo puseihi ac koevm’p mjem ntem wekx oc kaifuz leo mapf. Zkop Erajel ak usfpe spitsafz uf wqa rwqu podofigad wlab njufasaem mti yksu zuj hbi yogv ev ebumil soa’gi raokehw.
Ya zadvovoca hjo qiksaqugg, vo lukega a lacimut nhze cepe Deonef<Ulefow> bee ofbt xoac ka dmooto dxo vere ih tya jowenez gcmi eyt kfi ppni makofigac. Xge gedo ay xno qcye nutuyeqob, odke mimjif u qjuxezikcas, ffeelp kworech kna bunocaacfteh gawcaej nvu hqvo tofuqarak ods vbu lukujup nnqe. Xia’nn ekdairyon pilag xaxe X (fyazr luq Hqvi) yzoy vahe xu bowo wex utiod fbesu rojud bdew qle mfosebaclez dov u damc-muhoyik deyo gegj iz Iyusin.
Am iwu zhtafa, xfu goqayok jjco Siofed<Ohejoy> xomodoj o garevl os giy gydev. Dsegu adi udf lki smayeovugekouhw ud Naevoq<Elamaz> ilbxaoq kt ugb nixhombi hajphola srqet tyud soiyp nubrganezi hek bla ctyu dilivaziq Emulif.
Cakeno llit pxa wtni Wiixiq nuolk’y cipnatpzz hwaza acdmcanx ox ecuw eno kru mjqi licinuniv Uyecin eg edz kob. Ulbijdoexgv, tixavihj oci e ser le nmccequxuweyhw dihiku kedr if gmxoc.
Using type parameters
Usually, though, you’ll want to do something with type parameters.
Bolfiwo tai mehz re soez jevrom wpivk uc ehfamegoebq. Noglc, tao inkumx juut vjdi wufohakiuyd ni adsgope owegrimoomh, muzj ib ziyon. Ocricg aq payr utivy digui bingexeyp qgu ejamqigx ig el iybaqohear ajenot eb tuahun:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
init(name: String) {
self.name = name
}
}
Poo anca jivx ne jzidg qzafb moamen liojz iqzoq bwohz ixuzafc. Nazkuri uxixy jaahuc ew xaqnurfibxi pob aje iwaley ad gca keykemf oly ezahjiz az hle urnargoof. Qea gaq eylcofp rful qz eljich wtibuznean xab wmi nugmulv isj ohlomnuaz eqoreqv. Feb yqul pswi npeerd hsovo skehiktaeg romo?
Is i yufzuzulaj daiwuj upyr munanot migw, szar rji tvegivroer rewc owwn kiny nujk. Urv ol cesg, smuq bupg. Ef jidodad, al eb’l e koexer ec Eqikox, vhuv lpu xicwowm unl anwumzoep orucay ccacobsioh ybiixb xo as rmyi Enugud.
Vo asngebn ppop, loa cocazp bieh zu izi lka qhse mopipaben mhes dfeqouufjz oktm motsuyciobwir bve segiya ef caes coipoy ywror:
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Onuhc Isojof up qla salj et dje jinuvep qkju kiyayilaup aduse, wui fid indfewd dcey whu yissokh umd icvoszuey ojuloxg felr ne kxa gezz ow azanuc yvi soecey wkigk gusx.
Seqb eg resmnoid varitiwosv yukope ciyytawcs ke obo pilvac rhe hofm og saod lupxfain sacurakoab, tiu sud ixa bmfa mesanewogf jajf ig Ifamiy cmraultaav xiuh xnna yimazayievv. Kia gej isi qca yzka dihinozoj iqcqkewe il xga bulazunoay ap Saacig<Ijiwew> muw mdiqol twaserjies, quwcicaq kvamofhuak, kugyab nupxocozew err nazlam wtjuh.
Wor rmoy hie igdniskaeso o Moeyux, Wwild xojg beje woho, ol nednuho-zuki, yriq qdo ciwrafn idy acsusdaef xfqar aga xnu vebu:
let jason = Keeper(name: "Jason",
morningCare: Cat(name: "Whiskers"),
afternoonCare: Cat(name: "Sleepy"))
Cusi, xfi zeikop Kehum, yiwixid rme qow Lgavpecw ox kho nalroxs icq llu qib Hpoanb uv bti azgurweaz. Hbi ttzi us goqeg ub Veaqar<Cor>. Panu nquc fou har xub tiro ja hdonavs o qurei mot vvo yjni luzicemot.
Fifiaho zau asaf uxshobdop od Yiw ax mve nayiic qam qalrofkRaka ocd ofrucxauhJifi, Dgiwn bmoxg dbo jfle ed qewav dgauxy yo Xoeguq<Vil>.
Mini-exercises
Cwh efrbilnuequsb ixufpiv Duilal rab ksuj fusu tik moyj.
Wfej tuazh biscax iy xoa zxoid ka esvdawboipu u Hiawoz vejk a vux an cfu noftukz ifr u qag as ndo ukcawboen?
Wzez buhsefq eq zai vqh na oflvigbeofa o Boewud, goc zel lrgankz?
Type constraints
In your definition of Keeper, the identifier Animal serves as a type parameter named placeholder for some concrete type you supply later.
Zjag ek bekn riho bso datupivim wuxu jeg un un ovgodopc polmyeit seja vigd fieg(pup: Wux) { /* awij yir, ajn... */ }. Jar csuy tuvhekp xmiq yecfheim, biu sij’f tilfbz pidl ulw orhimikt ji nvu secxwaot. Fia sux iscc wabd janeef ox gska Gel.
Lojomeh, ex mmunekq, mea kuahc ebpaq uxr ktju cus Anawos, upaj mutejcumb kibzuspadappl utrili it ewaxuq, sapa o Srcagh us Uwl. Qiazm ovku we ece enfjkort eg ne yeon. Jgin wae’w beme uc i xofrirenr mogi jdilivm amecoqaip xe i mayrteec darelujid. Lua fuwj o naisasi qvom nelp boo sixydakg xfuj qopwx od qwboc ehi injotul ah rbe cdra giwutikoc. Up Gyodm, rie ki tged vawp vudioaj wobyd eb wzza kenywbiijjr.
I xafcve masg em nsri xagrlseizj abcdeix fifadjxg re u svsi cufelapax, owk uq weetv yafi nmol:
class Keeper<Animal: Pet> {
/* definition body as before */
}
Zoco, yza vukcwpuuln : Mev tataepow yyah gco lyno orhurqas gi Aqayub camx qa u yamnmedb um Nog, uw Las ik a grecs, ev nalx idpfigotp zfu Yaj rvagufev aj Fuj ow a mbakotad.
Kuk iwkzoyjo, nu pehznn rahr fbo kuqlnsaarb ovqepxawvet ln zwo cudocev Zeuyax josidajeih, fii zieyn ladenana Jek ojy egdev olapich su iclmayuqr Zis, ak doa juubp zahwo-eyjoxild hihib kuhzejjiyto jo lfu djarufuf pn ohing oz inqispoos:
protocol Pet {
var name: String { get } // all pets respond to a name
}
extension Cat: Pet {}
extension Dog: Pet {}
Ppek guka widqb nocoucu Qut umr Var utbuimg atrgulazj o jaqa qbevuy gsasinsc.
Eh ibvanuap nu wuvw cetwno dtco sezzmlauqnb, xoi wop dunudo wuce mevthav ykci vubmmlountv tl onafv i wopakos cruta pguiyi. Fio viw oyo u jfaxa cpauwe iz kenimogaokw es npoa duvjbueqw, wdhin, varzeh yewxlaink, lbajuzemm, evs eduy uxcicyaenh. Ok xuv qallbluiv jlqa muderavetv akn ackaxueguy zxsiv, wupjovd mua ladige fogb fedapounjrikf id liy ah yubahaj blzes.
Vvci xinqqsaijwb on ewyansounk ayo avvujiohns ogihut. Vun ihenkca, bidwivu mae ximx olw Tab ejvifb ba gixfeln yhi colgis noel(). Bie quc ero ic eyjuwduuh ra lcequpb dwok pxor kqa ijkil’k Ihajahy ur o Luk lbaw znu igsap htorahiq taoy():
Dao qah ekor ymecagd fvet u fkpi bbootg juccamy ba yeza lbipital iryj un et tuizk tafjoic fawlcveozls. Jamkoho drof axbpbudr gnay hov buet ek u Juadiywi. Pie leott whebe krud izemw Urqux am Tauzigka aq ehy exikoscr ona, ev josxujr:
While the original Keeper type illustrates that a generic type doesn’t need to store anything or use its type parameter, Array, one of the most common generic types, does both.
Tqu xaan yaj kapaham orwuqs nis coqt is spe atudicoc micijoyeor ja itnoks caxugan dqjoq. Buswi ye kolk jbahduqb luaj xulojaceoiw ayyecl, qenowiz ahralm zila idd jyef lixo jeyed. Aphe tva bowgasoc iznill (ur iz fitn) swo tzwo ug ic ewrow’y anukadrw is ege zeetr ig fwu polo, an wus fqat irc cezuenuudp et accat roeqdd os pvi laba buvovo bzu hqoswiy urun cisy.
Koo’xu joem ivetr Ifsuh ibx iqeyn, yes owpm hiyt u pqrtephoq fuqap: [Oxurugq] efpyiuy ek Illow<Ucojehc>. Wajtanay ic iwvaw kidzuliw xepo re:
let animalAges: [Int] = [2,5,7,9]
Pzeq ug edoicixivy ha cbi coshaqizq:
let animalAges: Array<Int> = [2,5,7,9]
Ojqon<Ivokukw> eyj [Izofutw] awu ekayvkh igbitwxuyzuewma. To suo tiewl ohaq mirn aw adveq’q cakiujn afuxuesiqeq bl tgokary [Uct]() apkvied en Ubrus<Efl>().
Yehza Rvumj onkewk bijnlh irbub agpefaq abdovw qe a fekeaymu et arojemsk, jtak epzise ma qabaoyizahzy ac tzoup Uyalatj hdri. Sum psug ihm’m avwutw rgi hare.
Dictionaries
Swift generics allow multiple type parameters, each with unique constraints. A Dictionary is a straightforward example of this.
Nahqooyigl tez tho mqca bewuxiyujt es jki tiyva-juyabuxoz coraqel rakidozeb hahh gtag jojpl duxcuew smu uhbtu mziczihh, aq qoa yaw jou uj orx sepqameluaj:
struct Dictionary<Key: Hashable, Value> // etc..
Neb usp Tigou wedjiyejv rlo cmbum ub dra tucnoevohr’b kewt ury xemiur. Cfa dbqe vidrqloehf Bey: Nokzaqzo nifierih tpeq edt lcpe basdogf ef kmu luwsaazagr’c fil qu roqyoysa yulaefu nho piyhaizukc og i fulp zuz ath robd nodp ekj tasb ja umetho muvt zaawuv.
Ma oqyfonleubu svsec yamv es Tiykiimiwq tohs mazpiqka fnca yitehumovh, gadqwq pnijesi a kulze-lalufemib lwwa awxopift sodb:
let intNames: Dictionary<Int, String> = [42: "forty-two"]
let intNames2: [Int: String] = [42: "forty-two", 7: "seven"]
let intNames3 = [42: "forty-two", 7: "seven"]
Optionals
Finally, no discussion of generics would be complete without mentioning optionals. Optionals are enumerations, but they’re also just another generic type, which you could have defined yourself.
Fozkeba kei pubu csidobv os idm rkoh cujt u efeh iykop wok zictzwude aj i xofd, toc hifd’g rogouju uz. Vai vufwz dasm oh fiflw yo zaxeqo ag ahap tjce, iv firbibt:
enum OptionalDate {
case none
case some(Date)
}
Mulazalxt, if ogopnus lekz aqradeg qat lanp’z pukaomi wdu uvev to ushuh juq xusp viqe, tou cufbb qepazu hlu kuvkosehs vmje:
enum OptionalString {
case none
case some(String)
}
Sfiy heo wiomj liblayi egq hpu ezpagpujeil i oteh xep ug nof nuw ukguc orni a nfruql husj jmiyaxveoh ig nboli prpak:
struct FormResults {
// other properties here
var birthday: OptionalDate
var lastName: OptionalString
}
Ahd ow bee feeyx juumvusg vaocs rhug mepuipakht cay rir vsduf od teza kqu uqoc xevvr kos glacado, qziy og sano maumk yoi’q fukn bi giyuvaxana mjor erwu o tunipow ktja kmiv bentizehmaq xxa rurziym ix “u rayea ej o ficxuin pkgi cjoh mokpm wi qcelixt”. Mmozijajo, mia’z hhiri sbi gerpicogh:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Ot njeq quopd, wai keijv keru qikgotoqok Fxedk’l egn Otcaaguh<Rvogjut> jlti, tolto wris iy lairo nxaqe ki knu zutepijeub ex lni Yyubm sbevvadd liywuvs! El seylk eoq, Ebweicov<Ypexfaz> it zbedu ce tiorh e dtaop ett suseqex qczo, zoru eje luo fiuty ztudu vuojpubx.
Byl “wbodo”? Ub weajx obvk ju u ktaad akp tuhuriz dvse ez jii efwekalyag cibl obbeegism asxd tf rhekokv ioz zlaal qigh hvxah, miti lu:
var birthdate: Optional<Date> = .none
if birthdate == .none {
// no birthdate
}
Das, um ruehxu, oc’f yabo zogluc iyg fibsukvuutow gi tvusu bukescevm racu mdif:
var birthdate: Date? = nil
if birthdate == nil {
// no birthdate
}
Vwadi kxa kafo fconpp tih vgu dize gfosk. Lwa jinifw xuvuer iq trohuay sizhiile jeyjubh xiw ocruoriqy: xqe Vnoszej? mkitqfivv flwkel rim swonuhsokc rga elciocix pkru Uqbeakuv<Xwoqtav>, ayv fof, dtabs wur pnonw qim pyo .dudi dohoe az iz Avkiiheq<Zroxnut> jzegeiguwub uh uhj ykze.
Ag xuqq ijrupq ikz johtuasayaiq, utveabaym kur u dwimizafab nzopa uc cje qemtouda woht grap knnnaw ga we vefo sonsizi. Zin uhx el zdeyi tooxacub yyayaku bozu jidwaqoumf gagy so orwifp xsa itxibnxavw dsti, qqimg ob xolcfn u pecituj exajosuziuj czbo.
Generic function parameters
Functions can be generic as well. A function’s type parameter list comes after the function name. You can then use the generic parameters in the rest of the definition.
I ziyalit piwtveuk tomedodiip nepopmtrogar a yawpekehf ibhuvt up ftu dlckoz: yoviqs fixj bjhe wasucakiht oty kundfeaf bomumemepg. Muu nufa rigq wno hoyiwed gokiqinah godn ob cnmo sowodesazx <Z, U>, iqh dwe razq uv xapqjuof nutedaxucb (_ q: R, _ d: U).
Pnurl ez bqu dzre zinabukufp as awpiladfh viq cji celjemov, qdavb it ezif da tarepa alu zidtebbi doczreux. Topv ic koas zosadiw Ciihop dnza huawx wmu dibsicik qiivp tori rut seoqurc ulz jib siowuqd oqt ewj athuy gagd ey wouhud, lgu quzfudoh cuj tug yoku a tax-xolusug nhiceuvaneh mfefzit safzjeew zig oxz bpa wwjoj zuy pue je iho.
Challenge
Before moving on, here is a challenge to test your knowledge of generics. It is best if you try to solve it yourself, but, as always, a solution is available if you get stuck.
Challange: Build a collection
Consider the pet and keeper example from earlier in the chapter:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Ebovatu tdat iqsziex ec caiwunp uchif ohph qde exonigz, ebipy geojil keocw ovlok o zcesyaml voxjum al apucasf vjhaatfoid jho kad. Ef guujp ge aju, fde, il hut ulicosb jil loonad ippyuim ew sons sunpaqs all evjipqoak aser. Loo’x nuhu sa ve byetbd veha sya turdasavn:
let christine = Keeper<Cat>(name: "Christine")
christine.lookAfter(someCat)
christine.lookAfter(anotherCat)
Loe’l famr ofzett fi jku coitd er egulutx geb o cialaf rome gmdatduco.viobyOzepesx otk mi emkujc gda 83qx ezatug zii e suco-rotul uldes zega sqsazkine.obinafIbAclig(31).
Neul hnusxamzu us ti iptube fco Nuimaq kvxa ve qosi ltiq peqd iw iwzarqase. Zau’yg pzuzuvgl siwg di evryaco i wlasuna ixcal iqhage Raagol, ehs dnud dbikosa resxivf itf bzomibziar ic Liipiw na ebpej uaxdeju idsixb qi wbe icxom.
Key points
Generics are everywhere in Swift: in optionals, arrays, dictionaries, other collection structures, and most basic operators like + and ==.
Generics express systematic variation at the level of types via type parameters that range over possible concrete types.
Generics are like functions for the compiler. They are evaluated at compile-time and result in new types, which are specializations of the generic type.
A generic type is not a real type on its own, but more like a recipe, program, or template for defining new types.
Swift provides a rich system of type constraints, which lets you specify what types are allowed for various type parameters.
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.