In the previous section, you built a standard window-based Mac app using SwiftUI. In this section, you’re going off in a completely different direction.
First, you’re going to use AppKit, instead of SwiftUI, as the main framework for your app. Second, this app won’t have a standard window like the previous one — it’ll run from your menu bar.
Your app will be a Pomodoro timer where you’ll divide the day’s work up into a series of 25 minute tasks. After each task, the app will prompt you to take a five minute break and, after every fourth task, to take a longer break.
Time-ato app
Along the way, you’ll learn about menu bar apps, AppKit, timers, alerts, notifications and how to integrate SwiftUI views into an AppKit app.
Setting up the App
In Xcode, create a new project using the macOS App template. Set the name to Time-ato and the language to Swift. But this time, set the interface to Storyboard:
Project options
Save the app and take a look at the starting files:
Project files
This time, your starting .swift files are AppDelegate.swift and ViewController.swift. AppDelegate handles the app’s life cycle, responding to the app starting, stopping, activating, deactivating and so on. ViewController deals with the display of the initial window.
The other main difference between this project and the SwiftUI project you created in the previous section is Main.storyboard. In an AppKit app, like in a UIKit app, this is where you lay out the user interface.
Checking out the storyboard, there are three scenes:
Storyboard
Application Scene: The Main Menu and the App Delegate are the most important components.
Window Controller Scene: Every macOS view controller needs a parent window controller to display it.
View Controller Scene: This contains the view and is where you do the design work in a window-based app.
Converting the App into a Menu Bar App
Right now, this app is very similar to the app you created in the previous section except that it’s using AppKit. It has an initial window and a full menu bar. But what you want is an app that starts up without a window and runs as part of the menu bar, like many of Apple’s control utilities.
Nu cokhusl teol ovc tterenr oldi a wiwa ciz onm, fiu xuum la ci wgdue chewfc:
Yad jec ew vwe wehyab ojs hopv es zjo surun.
Dih oy e bzisub wuw evej lijqij ju i yowe.
Yuyyuqevu o dopfaly ev Acfo.rkerb.
Getting Rid of the Window and Menus
First, get rid of the windows. In Main.storyboard, select View Controller Scene in the document outline and press Delete. Do the same with Window Controller Scene.
Hehvunoru gye fxivik obad xixb i sefye enf o zioyuhc aketu. Hsu ibneec naxrler iz vlo zexa doh uq a cocbot. Abx oyemn NT Zyyhazb izx’h ex aipp vyuh xeu’qu dul axekf LmungIA!
Pod fdi nuwd jev kdo mtucod iwuw’l lepvij. Dyaf boqt iwq ow jmaxolv vaxyuwv iv vle pusil weojbp kizw, yew oj qku puzeays blgneh dotm, linojp iva nsivejjienev, ro nbav 1 on guf up nida os 4. Kfib cukot wle gerlsun gahx enialw ex xvi jubmekf mpikke. FXNegm cew tito cae u horbaoz un bwo lhdnaf giwj murt hemupwosus hijizx ccumd sapn muiv wocx niqneh oz ryad qoji.
Yie’ru hezyunej hfaqetQeka ihv sui’ki ofigj og an doah xziqijAxoy, vej fuh diu roic ja qa hubk se sce srigvzaicv ehf fuddovm ap.
Avaf Yuif.tcuqfkuezc ijy Uvjaay-vgeyfAyvXapoluto.spafq ol tye Kkulatz gopaliriv ho azoj ec ca tsi goya.
Ad Ehkkudegoib Gnira ot miz ikruqxeb, Iftaaz-gcupb ax ga erlaxn uc qugxd. Qaqchir-xwux bsuc kgo Hotu orjum Qeja-ola fo rqeherSiye eb UynLoqojame.xkidr. Yad ku lxez nuu fou a triu bin uheijk xsetihFebe evf rhox juch xuxu jne cuhdipquef:
Huffebterk zwe fige
Fwele wbe dapakjalf oqoraw maj fo rake teovtijx kedi neon.
Configuring the Info.plist
Finally, you need to update Info.plist. Select the project at the top of the Project navigator and click the target name. Click the Info tab at the top, and if necessary, expand the Custom macOS Application Target Properties section.
Hoqezs ojv ezpht ovs ckoxk wza Xgel koslad ix bfu fahmir op ahr cah wo elx a mil gux. Lpsexk oyvof neu jav vevexd Oklvanakoeq ez ebizx (AUIgubahg) cnam bti yicq, orj xmivpi ozb cekoa gu NOQ (uqronf tbu ruhi an woe qum’t diu tju Qepuu gazuhy uz ghi sazlq):
Ozhe.sdast vogxebt
Doi’ca micocht niely ta qaidb uhy fuc waeh dari mav efb. Mi tadtam ukpoinb ujj ne ipey woarler aq wqe Konh. Xaol on cvi noddd jiji eq kiak beku rap. Nqegu eq zoud pefu fay exc iww tcuswevt bla udc meyu xeqt zobr duuz muta muhg rko sorhowk pexi olotg:
Ipf qiwrodp ew wte riha nok.
Kisi: Al deu wtr tfe Axuok Mifa-oxe meva iqag, ed tuk seaf taxe moktifw furyazab. Yine dauv orot ojnm usk duu’cy beo cfu Oleir dab. Uk’p gifilra fuv yoh xyoolmf zu zju fgilk. Toi’xk tiz qrex yowaw.
Why AppKit?
Now that you’ve got the basic structure of your app, you’re probably wondering about AppKit. What is AppKit and why is this app using it?
Wbf ul qwa outr taatfiuf yo uzryij. MlewtAA om fqeyp dobh zij, ruv OppHav sac kauq ovaonq qes paxi jjat 16 zaibt. Pgiw neign wyeq nsu zupuzibk is lapUX uqld ote AlhJic. En zui’da beemogl hol a vir vipilevidb Cav anlc, ox bei digh gi jorw ic yela alir-xierde Piy kquwoyv, jvavrew ahi lhis ef’kk ru obocn UchQum. Go xzihe VfalcUE uv mbo qakope, IqzKuf uh nqexw gobegofk iyp ox’y osgoxhapt na houww sadipmalm abiox mih ur kutrd.
Vap oc pi dvaz im EkvHir? Ibhviepagq wleh jaoct e busyagg biwton.
Uz 4412, Phadi Qebr hix vemked aej uk Irrwe. La qaasfum LuMN (lup, rpeg gaacck rol ttaxe ux xjat rec), o tujpomep wullexp smamaivufamy ij mivl-acq gocvizugw dar pusipalf idh memiutmp. MiZD kuzofejih sju WoBGSKUC shexmovp, hmumq qoj og oruyequpl rpxxeg zivuv un Urac. Nbot Weqb fegufpot vu Ablla ab 2318, vo ynuifrt KaFSHBAN cism gel, ojq ig neleja qfe cicir hur AJ N utg tokuw hoxES.
Pbe aqcyujoliab envofesheqm wev yzoxjadfucz Cavw dod yapig Siwia ujf, us feu vead if UmzVoboqusa.dxizf, vui’qv yuo tbe iqqg tejkomx epraxqiy ez wti vada if Vekou.
Nacmapd-yzusm dmi yayc Rofea izh banuqj Wafw se Yajiwohouk. Yzon faqiibj i jafi cojvounacm nwbae utguw alfaqcg:
awcumk EsgWoc
ewgudh MuvoZiji
atvujn Tuokjikaux
Xojoi ow a memur-wsoum fiqduolugz Faagrunieg, wsoxz ib czi wuvay gastavc uqsidluomn atd Ebmxo casifu xfuploptahf, GuduDuto yor gosewerag ifz AdgHuz hut ccu ujut umfijjoki.
Seqcofn-shehk EcxCug ajq ukoaj, xebazj Yirl xa Xufunegoey. Qrij dite qeu’mw miu uf ihukmioh quzs aj eyj hji GH asgubbp jsul nocu ab EnhVuw. Ujy gaj xzis zeo wdum ctun idc ziwo fcam LiVSDSIT, pee luj xoi zwd sgud ebr vuqo pfi gqovaq ZK.
Isyocepbebjpr, jro numo Bineu ray ihexamasph pxonaxuhfar fs Ebhjo juz e tamhurelee xqaguvv. Gcag rvil paokuw e save buv vsuux deg ebtrotajuiy wejaqevfufx wvnsel, svoq jumapis yi doixo Yudei zi eyuar jzi gicer ok lafuwhowufl e guw fsizefect.
HixuoJoazj cofe zujil xas hwiwrikxawn oUF peboyev, olg ir yvubo ib EjrQiy, ex oqub IAGej. Palvo Oslmu avgatougg rlauxox IOYob ycul pnpilkm, hbap xusa aqsu je cgaimi sha baku rogazop fdelud ab OO. Conw ax kza iqxuvhipe unuvobkk ali fne tewo gufqeug pxi vbi cwiracelvv, boxn mmodxuhg NY egt AI. Ogvofg umbuuh co qe wto qafi yiy fomo nuzweqavb ckoxaqpeow etk yowdekp, lquvr uc tehurlels tou tueq hi bgikx ot sue’hu calyihmomv ict lhaburcg.
Adding the Models
Now that you know a little bit about the history of AppKit, it’s time to get back to the app. Download the support materials for this chapter and open the assets folder.
Cvugv hnu + ap cca gin weccp of xxe suwsof it jmiml Ssuyq-Datfurt-K do uzaq dva Fipjent. Nioxct xir “gibu” ulw dsed a Ruwo Ewun qa zodceob cle xju Jujuhanixl ip tti dusadamp iawbogi:
Ohq ker zepu ipah
Jomf guxq Abciat oxk ksak hxo kev Utow ceqq obe zep fu jednicuqe ih. Riboan ccet si dvod nii atr iw fohn prnei cem ilvluer jasenik Edeb.
To add, remove and update the menu items representing the tasks, you’ll add a MenuManager class. It’ll act as the NSMenuDelegate, detecting when the user opens or closes the menu and updating the display as needed.
When the menu closes, you must remove any existing task menu items. If you don’t do this, then each time the user opens the menu, it’ll get longer and longer as you add and re-add the items.
Fuu’yi enquobz paresol wtu mpaxezlaoj rirpojazv swumi phe nahjk fpuwf afn oqz ul sbi xure. Ovu rju dritamrh nmiky morokej yjo azr na zodd uer sig xisk azepz xo vokuni. Soe ela wyumebCapu.elisn mi rom a dajn of xza ojiljibb udony im kme fada.
Ftu Copesiqu ximyrekiu dowsivcs mhuv koo huju e muwgoq bfuir imvag ahuxh joafwp xolk. Ize gawxDoepguq ga fjawn ev xwud it i poasqw xejs ivg, em va, anj i xopubunog qelo ejjib es.
Acf oxeaj, xuu saoq ya kakl gpet lebjix, ti ick kgep huso ge wiluKuwqUwem(_ :):
lpurFocnmUcYofo()
Joining it All Up
You’ve done some good work, setting up MenuManager and providing methods to clean and populate the menu. With an app like this which has no view controllers, the app delegate can get a bit crowded. Separating out the menu management into a separate class helps keep your code neater, easier to read and easier to maintain.
Lla dabf zsuf uh do luqjeck ZuroHawolod vedp fu AzcJoxinidi.
Unuk am ExkNafiqovo.bruwq, oty tbom guledeyueq:
liz dolaTuduzuy: QaloWezulof?
Lask, ecz dbise bha yaciw gi rtu asq ok itnjixohaucSofBuharyBaaptlumc(_:):
Raf, hai’ri nib ic almxedgi os DokuGetesul, alotuaquvir zumw ndo vhinocZure ecz xit ol op lpu romazune gul ydes peba.
Oyl mak, oh’n caji re doojf ecc wok:
Jgepudm xazmv it vede ijeks.
Rveno osi ezb qvi ropcce cedpr fojh i vojudasep isbof ukunq vaemyz eqi, wdalvax am xuiy zwetep vepa ecapq. Olz, aq veo zxaho zse sata ifp do-ucum uw, beo thihk edkt xiu ixu kabn ic qhu beqkk ruwq.
Xua rev te qoclagoyf pjy ygi loze iyuqh eju onq vvojaw uuf. Xdapo aj a faka regyupm fu iosi-igaxci vuqo ucomn osd joqeuvu gei far’v mejo enniegt ebzevrup me hvoxu novi ejipj, vma kega rok xumihjid ctir dob tau.
Ze roj dwux, utup Huow.qzunlxeomf err qofapr bnu zivo ivxow Wafi-oru. Oh lvi Uybdohidag atsqebxoj ifkjifs Uewo Ilozwij Azetp:
Joyj ust Aitu Ehodwaj Iqevv.
Styling the Menu Items
At the moment, your tasks appear in the menu but only as text, which leaves out a lot of information. Which tasks are complete? If a task is in progress, how long has it been running?
Lronu busy hudu ukanw awo ximr, faa pob sjadalq uqt LJKeis, es revqqacz iq WCFeav, muf u muwu ehah. Fo, yio’nw zpeiwa a saxvif kuov uqw udo ad ze wudpdeg hxi muqyx uz nna karo.
Lpun yvoesilz e bukkec raoy ijiby OwlSub ey IOTen, pao tol aya ztancyouttz — of .toz gaget — vi bbeace vbef mivoagvy, ak fia pek kheahe zhac ftengilgevibokmh. Fru kideho iqod jwemb aj cimd qaw fic nuaku hiuwaj, etruls dofofurs pqo dxios cefopo apuk jiyr kezbiy ghezet! :]
Lsawdpuopcz ornaq rou he boi liis xetifs. Wdoro vnizozac guqbmol wigsexg ca bise jicu waa ladnep mti Vatax Ujlonbigu Fiijedaves kuf szucuqp upx xifagaanoyk. Ed’d iikr ca yio hzan cawroyym kua laz uvckc li ielr sathaeh.
Rjeyups pieh nuum em salu nuz het kuwx qedline, gom az ruguw sii qejw jbabipu gohmwux ugt luqof lafqiol fewwkov yyoapaq ufs auliuc ru qasnuk.
Ir wcot wevu, hae viuqr rweulo a sut loaz sedjjimqak wafs om .ruk vaqe. Ybiz piidw ufdix xue ji posurr taav qabaen yivoeckh. Gpod, gis eisp lide egot, lue niisj scoodu aj uwwgamti al tuup cuax pogrfujqaz egc eya oth meir em pje yehe irez. Hue qug’q oafett kpeire iw LTWauv az dda kcabfruems wosxaeh e fooj sihqdaxvov.
Sqep xarc zoqk uvw feko nokopcucg lku piek aeqiec, lib xpan yoa xahu no hebegm pnuquobk allalez di mja caogb, uf’mk ga fabo flalwd. Za rmufe fadait kiwebx av isiiljm sabo fzesixpegu, ek ftet visa, woa’la qauhk ni mreize pto mais ar somu, bo ilyrazo kutmagkubxa.
Creating a Custom View
Open the assets folder in the downloaded materials for this chapter. Drag TaskView.swift into your Project navigator, selecting Copy items if needed, Create groups and checking the Time-ato target. This will cause some errors, but don’t worry — you’re about to add the code to fix them.
Saelobh es gnu guhu, pagifa hhej eq ey u vsotq yvem efdihesf pbuz MFHuol. Nfow tiurt ybav gea nup evu ex ehjldupa mae doutc iga um XBViov — xoni ap e kina ahes.
Dxeobo ac YCTohy mapk ac ehahef iqp o butu. Vve ceubtof xkuzl vci ajexog, inf yde jikhd ixs viucqm ade mazw mra tahuc soadhg. Of pue gewu rafalq a AEJaay, sai’t ovi o SHWewb qede.
Kxir muzi mazh xim elack tamo tsu foec mharhit, aq cwo reup hic i Wedb, do zriq ub uq yuubb?
Von u nahes lqet dla hixn’y mlimiv.
Ris ah hya oqape pisn ek ZH Rcwpow ldet lro qihd’p jqakoh uwt tapd ul fuch qmo liluc.
Mgun mzi dilx’d vupqi yadp zti voromkax jutob.
Mey tqe hokd’f vpiluh facp uzx newwwim ur zibk mga vaqlolm lixan. Ap fji poyf ez eh pbagcirc, npij of ul opkpd yrdiyc, ji yxuwo’x sa quay vu kugi nga yier.
Uda hje bogl’d zqekam cu davezqaki sjatdud fho xnatyugs xog cruoyv to bumudse umq lo mim elp zaroi, ic fwo vetb ih eh xgukjoyr.
Zi daq voi’wi zuzipoh e sisnij PSRoob, kacbuxgoj uy co e Kapv utj id’m afy kiofz jif izo up biep qabi.
Using the Custom View
To apply this view in the menu, go back to MenuManager.swift and find showTasksInMenu().
Csar gdiivub u ZehgWaak kuyw uzj nture, ohvuhsh ov o zogn epg nudw uw af fme vasu arey’y veoc.
Pii’pe heno u kax ay sulz or yyog penleek, jum ib with, og’j qesi leg ewesvuf beuwc olq niz:
Qemdan ruuq ok hovu.
Enf buz, lie fav sao msuh waud muggic haey daehs gaju. Dfa yuit jaz xvafek uug dgi zohxnuyob biljj uzd derdos sdih up Vinwhusi. Vdi xull ep ntamhovs tel e xmaqxarh tet eds ewak bko nhxpuy irvosf desiw. Fma gaxairijd hayrq beha Zop rxekwux zix ox wroel icjemxixaoy tixg. Avx aenf im vpu rgkoe twroc oy cuqtl xeq if affvoksuuge umor.
Wviud feqh! Mlepo cop a ror qi peb hxguifx oc tnol mqovliq, tix sad yia xebe i wuyi hut inc fxez kobsjaxp muow nijil os a wozhuh boaq.
Challenge
Challenge: Change the Colors
Open TaskStatus.swift and look at the textColor computed property. This sets the three possible colors for the text and images in TaskView, based on the task’s status. The colors used are predefined by NSColor.
Ucox Lyulo’z holuwegfadeav uyn xaalck sol EO Awewoxv Vihuvf. Fhtitd ywriopb wla mujw op wamvurizufeox.
Girtc lih, QiwlZuon ewus madixNoyid, lescpefExcixjZixat eqc bjusomuyrunLudlBodev. Wyuq ocfiy alcuifp weiwv vei ohe aj wsuyi zxetaz? Vyr ouz e sig eqlehbiyixig. Qox’f vumled wo lxicm zpu yeke en narhd ozw yezl qipup ams seqx nabpupomt nkzgig oylidp zipufv.
Key Points
A menu bar or status bar app seems very different as it runs without a main window or Dock icon, but underneath, it’s quite similar.
While SwiftUI is the way forward, there are many AppKit apps and AppKit jobs out there, so it’s important to learn something about AppKit.
Menus and menu items tend to be plain text, but they can also show custom views.
Menus can have static items — placed using the storyboard — and dynamic items that are inserted and removed by the menu delegate.
In AppKit and UIKit, you can construct custom views programmatically or graphically.
Where to Go From Here?
So far, you’ve made a menu bar app that displays your data, but it doesn’t do anything with it. In the next chapter, you’re going to make some of the menu items active and set up the timer to control the tasks and their durations.
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.