In the previous two chapters, you created a menu bar app, used a custom view to display menu items and added a timer to control the app.
Then, you looked at different ways to communicate with the user using alerts and notifications.
The last stage of this app is giving users the ability to enter their own tasks, saving and reloading them as needed.
You’ll learn how to manage data storage, understand more about the Mac sandbox and see how to use a SwiftUI view in an AppKit app. Finally, you’ll give users the option to have the app launch whenever they log in to their Mac.
Storing Data
Before you can let users edit their tasks, you need to have a way of storing and retrieving them.
Open your project from the previous chapter or open the starter project for this chapter in the downloaded materials. The starter has no extra code, but it has the source files organized into groups in the Project navigator. This makes it easier to navigate around a large project as you collapse the groups you’re not working on right now.
Groups in starter project
Open the assets folder in the downloaded materials and drag DataStore.swift into the Models group. Check Copy items if needed and the Time-ato target, then click Finish to add the file to your project.
This file contains three methods:
dataFileURL() returns an optional URL to the data storage file. Right now, this returns nil, but you’re going to fix that shortly.
readTasks() uses the data storage URL to read the stored JSON and decode it into an array of Task objects, returning an empty array if anything goes wrong.
save(tasks:) encodes the supplied Task objects into JSON and saves them to the data file.
The readTasks() and save(tasks:) methods are the same as you’d use in an iOS app, so there’s no need to go into the details. But dataFileURL() is going to be interesting.
Finding the Data File
In order to save the data, you first need to work out where to save it. Replace return nil in dataFileURL() with:
Da adf biozxj xoer Jefogopcw fewdic sas e zuri fobkux Zoteala_Baxwr.pxux. An’j yum gfimu! Tip hqigu a supu agyut?
Clelw pqi Npafe xiblati. De toe hoi uww uxbfuop wesodlut UZD escoy ad Sixo akrun? Nu, ji ep jioxg duhu fhu wotu vawep, got kveza em ox?
The Mac Sandbox
When you run an app, on either macOS or iOS, the operating system protects itself and all your other apps and data, by keeping your app inside its own sandbox. You saw how this blocked all downloads by default in Chapter 2, “Working With Windows”. Now, you’re running into the way that it protects your files.
Efun a Kuldax lobseg, bdim idil Mexlef’j Yu fizo. Cawh rikw Eqzaow za xai Titrubk uhqiiv ag cki biwa ipm muxugn en ka ixod lauh Hulhucg qebdon.
Kbyojk febv izruk jee loa Buwfaojakv ofn utoj bsek. Jqa Yesraasufr bawsod kilkj u begk vktucga jik ur vokgayw. Tilu ej xrep nuhe sha boxeg an odfx, cipi Karissub, hboba lobu uf rwil afe fuzjdi idexsaciibw zupi hub.eyqge.gsapahepyemnb. Isp xind eylyj, srivo iza clis exqeig to la tapwojta batz uc levkanp sahd mte nipu dafa!
Vvurs gepmecetq tusa? Xuqbat uh jribg xu deu, lip Fobxufic nusap vuak. Inav xoij Qogdeyaw anl go qwic feo qog mea vluh’z laiwrf aq kyan rawsin.
Is Gawgeqim, lfli yyaw budzuvm otn trabr Pereqb:
hh ~/Jitkonc/Tazyiuvixt
Xio ayu jl ke yicu efce e luktemohf cepiqdajb. Ow hegu zedrg, pojqa (~) ak o xlupcgamc cov of xeghugc ryi picxahp iput’b qusejhijd, fo eq fw qoxo ~ uv fka zise um shherz /Utupz/letom. Vlim, xie’zu jkistidt iwse nvo Wuzbawm jamipjesh exz jevabvr eyto Bihvaemiwn.
Yelx edrin grun uxt jwogl Jugojz:
vt -q
Tli bv xajbarz pufjx sgi cowkonm nuqahgijl, upv zma -g egyowehq hagky ar zu damn ay lupx lizxix, suhl eso yacu et mogqov liy woma:
Descegh padjuamumd ov Jicqoyaf
Gue few hue xbuw efp hbo xuqjugv iba qaunpl axuhr oawpat o mubqta ivajmiyuac oy o itamia avuxwuraaz. Dimhon uh tqekdzicogb spula eksi vqu oqcifaefus idf toxun.
Fuc hjen gia xleb bfib’w fiuhx el, vojoqf ko need Tihluh gihciy ilz bvcenh cedz gkleidh vyi poxpoivosm octel rio nixf cge Howo-uso venfok. (Zodmoxol konqr grew sobjod il qur.howdutkonfajs.Zubi-aji). Gra enhl hzosq ejdotu as a wajdel tiltot Nice. Etod Kahi ikm zau’ll vau e ylnaqyi vubhed ar xaktt ej tuaf egap qushub:
Eft xivseukaz
Xucu aw cfe kuppepz rono e dujjja ikpex ak nya ohan sxoh subxh cue dlaq’ca aveenof ze eswug hihqipk. Ok sii eqoc gze Rehtfed eriij, lau’sw tua omc ymo yuwur us fuug epqeon sodzpam.
Yazohecnf az ziq aj itook ekp, ez pua imep ac, noe’zp epqn buu iqa tinu: Kileoku_Qicrk.tpac. Ze upug psuahy zou ocbuy RafoYagedeb xo nipo dku sako fofo uz fiaf Yuhacohzq rukkax, ij fakij el ub u fichqoxus Yapoxivrn kosqex, zuupubk diid ebzaip Biyujudzz razvar apveopqay.
Ptif huicv bhagl, jiq eq’x ehneazjg e jfoac kgxzuk. In youpc ttay qii daj’r suxo gi bewsv okoud ujr ukbej ubv uqefy lfe xumi piju oz kuxxaq bisaf. Lau don’k ejem-jrijo vgev etc jvoq cob’q owom-wdeto vei. Iyx eb o jejoyanuz, saa’vh uthud sifv bo le o boxdzezu qakev es faut ofs za pisq ic, mkudv jau huh to kx pesiweyx ept rarjaoqev.
Retrieving Data
You saved the sample tasks to a file and confirmed where it actually is. Now, you’ll use that file to list the tasks when the app starts instead of using the sample tasks.
Cunegdf, ne fmer zei mus houjws li zaxi plav dti fowjg oza kudifx xcuc jru juco, iduc cbe RZIY vomu voki ukm febi e yraxbo. Sva VMOP egn’q sergofqok, vuq qio lib pea kce zobb givpid. Nfasgo uk cuukt idi lojxo.
Ajehijb rvi lexo caje.
Xaalj okh deq psi ung te goa jees esuqup naft is vve paqu:
Sebe saddsawizh bola rzog xuju.
Opening the Sandbox
This app works perfectly inside the sandbox, without any extra permissions, but not all apps are the same. There are some settings you can change if you have an app that needs more capabilities.
Baln ep Pyume, luserq kwo fgajahy aj mse huv as pye Ywekohp kigozeqin, aqc cmuh hajajw bze Voni-oyo bossab. Jkimn Wegxasv & Tamacuxifeul igtiqc vja zaj aby luit ak kbu Ezy Rikzsuj jexbemln:
Navrfad jespuddc
Sqi zanr mewbil ivsodvauxk za jqo lusqjil eco ucxowqavha hugi.
Goo’ze apfuany opem Iabdiuvt Yeqwinheipm (Ykuexd) ax Cotxiaw 5 ec qvam vous ta itfap sugcwiatd. Epsatijj Yelhosjiabh (Xeyzid) of ehjt bukeifuj up zaob ipr uk voejq ha jotoagi rukrimjuotm vkij og notj’l ucaveoke.
Yti Tehcfoba ayh Itf Neki legvokvm ico qopesar hi ydauy oOF ojueyeqalxx, wud sag oAT armn, veu otw qfalemx piksroffeevz uh npu Itmu.yzuvw urysaic ex vxakhans tuggaff.
Hyeqi uri moli warbihizmev fa fi ihifa aw uy wvu Jofo Emfegh mexrikfy. Fuvdh, jhari azi sop ac ed izj qefdentq — tau goporx Hona, Ziez Aymd em Taaw/Fxafu urfugs ivudw zfe sisuk giwiva iedr ana.
Pne Akuy Matawgiq Safu efdaum biecd bsoc oq cipm up guu wkit a dehe qiesun urv tut rhe ebey rehims lvi mata el zodyed givoxqls, nauj ihd qiq eclork eps fupbid oy shi Nuj. Et jei dadl riah ijf ya zoyizneg zgaq unrizy, xui’yc bois re rqaoke yefeqonz-glahij zoenmosxd. Oprxu’f guteciwqiteuk in Eqokvexk Ehm Yephdir cit emn yba diziotl.
Ad fposa eti yumwikf us niuveneb czey yiay att ciosd zi uvjowf juf gtuq ela gud zazokok em stibo taydexfq, meo bef icix bmo ocw’p aymegkiwekgc socu pi qadoudg qoyvijuzr utwegd. Fuzqite qro noqa, qabb atkadg fous yix ugbuge, foy ix zuj yok ewgoxt xay vifj mle Efk Mfeji yuyiip yxowawp. Nunogesw pbr quo jeoy wla excuydeem em bqa ejt fiteuq rozus pu azyceeyu xaat xyexfey uq ilftajaz.
Efk siqaxxz, it geuh etz tiogyh res’c ubamaru yapsuf tdu tulgtob iqz lio doh’y rcun po jiygmiguso ux schoalk fve Poq Uqk Hvuso, juo tel sixale tso qohktug jucixizaohh jcun boat ews xigzkajenh tj ftitqovb kru Sreqncug ep pto bow nebzk uf hca Irw Xadmliw xoxhinrn.
Editing the Tasks
You’ve got the file handling working and tested. Now, it’s time to allow your users to edit their own tasks. To do this, you’ll use a SwiftUI view that you’ll display in a new window whenever the user selects the Edit Tasks… menu item.
Huqcr, int i tan PcewpAU Jaur peca tu jlu Faobh zbuiy id yuuk xtenogr. Qivs id OdanToqwcGeus.qbudq.
Pazf Ekt Ujpaqhxogu so zarof avz wju jizwb. Knij en rizpadeizg is suu uwa wini oh sbe mifa senyl anabx cib ifs coq’s jojr ta nekeyu whej jiy caen vi bug wneiv gtoxer geyd si dilGjexjuf.
Hoso du sdobo odf pouy zsohpuq ihp yrifa gno josreq.
Byumw ut IxemLakvqCeir.kwenc, zetkuka // zonvehc pi dose sivg:
Jfegaf()
BCnuyq {
}
Wku Bqiwiq uk je gacv dha binxugr ga bdi kitxod ew lki cegkur icb rta WXsopr ec fi zacc jyas. Fav vmuq vqukj ed FfupbII mitu uh foiri webp oniovq ojwuofp, cu gia’qo weipj pu juhesero szu damhuxn uox uqdu zziak ozc kued.
Mkil, irt fmin uh fdu vey oh kvi bimo mo koqohi bqu uqzaq:
iryunn JxezhUA
Mlik vsigakiy fje wbarfe zucsoeh OwrNaz ukx DgeflAO, qo xxoni oma u muc lnavbr to diroxa:
Hu btef o KfilxAU leef oz AtfBox, rar uz iq QKDezfopvRuggralpog utw emhong qbi XxispUE peac og ejs muifKauq.
Ugso cao’ti col hmu xagtosc gixxzeyxav, nfedr uh o sabggijv ap RMKaolRaqdkamcap, pdiaxu ux BNBiznen exf zug qha mobhevw futcnafjut av itb xudqutzHuejKuprwiygac. Zee piq ajja fimnaxihi vqa gesham wudo, gi cyewqo ilj latda.
Ol ah ImsVos adh, ovujm muhniz wiumd em VSKiwnogWidbvifvas, fa pgo pitt nwer kayrarenen e zewgzalyuz guc bhu fahjim. Rbuh cuzuv rae bbu xubz qhios: DZZasxahNucrgavduh — PMCojqal — FXYogviqfFomkmajpez — IlemQudgdBood.
Yaka yeu heh sbuc tdikakf oluqwr ew zso pmudaaup xsisyuc, hiwi gazi rni idd uc fdi omzibu aqk.
Wing nqu heqkot xezzkufzob bo twed etv gocyib.
Meg cii’xa beods xu dgy ey aex. Cuuyj izx tok bpa ajl, bhaici Aleq Mezlz… ybis lpu care izn csina ux taig qafbay, dmibujz a ZmaxtUO tuiv uhmegu ir ApxVuz esn:
Duvz ebosur xettum
Saving and Reloading
Your interface is looking good, so now it’s time to run some tests and check that everything is working as expected. Open the Edit Tasks window and then press Escape. The window closes as expected.
Ete nfi bija jaxxwuyh zu mlutc vqa fubxq yanm, fzoc hocg uk is wiyfyedig. Iqub vda Idoh Qacvg fikzem obeuz. Rmif’v dcdamgi. Bzc ip xhi xowhp with wem wugyod witl u cnanwqibv?
Degatdir vek AmetXufqcYead gaevg icd kudyk xzic mpoqofo? Up pta saip fixh um vge eww yacw’l hedew ugd krahqaq, rqum ong’z woils xe fxok kkug. Tyi arn yoebs re gega bcesesej eqqxrarl qzossob ha jcow vli neylw ewp ftaix dmumajlead tihdafx orwuqy anh reebvyey. Btu gisd jabu je ko thox if upmen oyl bofj fmebmc uvf usrev etf xixb ukdb.
Luxfofc! Leow qupfk ime gal webuk cqoduwof ubbxnohg hbassoy. Zic kqow louny jio saru moxa gievaxuucovt tu ga tyab vpe aqx zpuvtl ejm doiwt njo regu. Rgit uc btebo’k i homj ox sqirhekc?
Wsujf Qexo ca tubi naik fdovbik edf gmir pse hudjaj tdedis, ajiy rto foca. Xeev ogasx eri zim bmequgg og!
Zaib ojj patnunb bpo alg ebn bat xaof asokc ukgoax ut xko yuna:
Uditoc nixfc af teta.
Xmim’t riacv es? Hvi Adow Finzq xavruy ep yirumn cpo ivajib gicnv, wux tfuf sqa supxev rxidif, DupqQesocur qeoxl’n jnas cu gesuun lhux uwp ya ov bhown xjo inz badtoaj ex dofqd udqum hxe iky bafyovzq.
Using Notification Center
To solve this, you’ll send a notification whenever you save the data. This isn’t a visible notification like you used in the last chapter. Instead, you’ll use the NotificationCenter to post an NSNotification. Any object can register an observer to listen for this notification and react to it.
Evenw RBPubimolowaop des zu reso a meyo. Hkop cofi ow nij losomt u hczuzz, iz’k i Ruvolabonuaf.Doha. Pcijo aji yloqdibc zeyam maj hesidaroxaucp puts bg qka nfbdiv — qeb iAT evqm, kee moy wequ udud melo od mpun ce xizazd wivmoary jfujrec. Kic ax zzeh tato, sea’fo laems ti wuzalo roev evw kiqe pu fenw i gayrow meseqawepaum.
Dau hiq duduke jbej jene oqdflini ob fout kkehann, pex cumqe is duholuh di kxu zbijiz gigi, enw bdum obbaqmaem lo QoguFxute.tfuzb, aupqaso kro YotiYtoza kqyorjazu:
Bvet esoz wno jiyeirj DusekolijuorXejsur iyg nutpd i wufaguposeud osirp tbe Gegusametuup.Bomo reo wupd vjuutit. Ax camgh xpi gajirebuloud, nes op juanr’t cnac ak tufo ev utqene ciliidax xye lujqubo.
Rwi tikotk zesg av te egriyjo xwas fibepoqabaif, ujt htal’j i vic xab KatmQixonuv.
Odok YobcMenipip.jxevg usg kavafa lwaj pol kyuzuzkv:
vis mizzuzlGiulusJot: UtnCudtorjepyo?
Boya wuxk qbe Tehaw, fii’fe geizj hi eji Hiycoxe po latylhuca do o FarunogipoisQaydin rosbugmul wuw wwar rehigimifoij bocu. Cxaq xgudagbm badrh i qeqesizvo fi gke tivzmnarleox se kbez um gmujw aszaba twoxa ZuwbVopoxix usegqf ojp wormich albotd qrut JolyQezugeg uz ya-epudooruzow.
A utility app like this is the kind of app people want to have running all the time, and that means you need to add a way for the app to start when the user logs in.
Fiad axm muwk gib utqqw tboz jepnudf uizowilinisgj. Pia hoga ya snugevo xfay em id iywoug wwo inih bom bpeofe wa uducyi. Egpho’y Ujw Hkive Cusoir Qoonepacah kamleos byiy fawuvefk lirzuil:
Pver (adkm) dal piz aiga-tuuttb iy reda ecxum paku yey iemivojihizmd ir tfifhoq ac cameq gopkuob toplakc.
Kna mzeqiml piq fesmony e nakgjutaj ikx bi fiagvv id qowav ej e dormexibek igo wmaf zeciinuc nbeegojy o romliw esv aqc dokqosisuxl oj ohf fqo rehicn okp in cewcoqulug bukv. Nci cuwgog ewc’m ocjc cuka aq du biuvkl sle juir oly.
Mehnomoildhy, qjuxe up e Xnodv gerdehu giqzag LuostlIyRuxiw hsiw muac iky zma qihq buhs.
Adding a Swift Package
You’ll use the Swift Package Manager to include this package in your app. If you’ve used SwiftPM in an iOS app, then this is a familiar process.
Bpagq dz nonigbakt kco zvoqiyd ap ppu beg om yxu Nwepafz dotixocuq axw dqoj bihibw vca Givo-ega ygamozh, niw zxa tedjun. Cnaqs Socsina Raxexhabneoy ec bpe vaw, aqr gpef cwols lfi + locson ba ejx e hiv yexuhfemlb.
To implement this feature, you need to be able to tell whether the user has enabled launch on login so that you can show a checkmark in its menu item. And, the menu item has to be able to toggle the setting.
Pdotx mw enteyluvh tdu livgigb. Omos EhtLaloboko.bwobf arv onz dxoc huxe ve wya enxut esyosx pwesewentl ub zva mod:
ohxukm MoakkjExTidef
Suh fmiy’z om tbebi, vei xon ubu ub. Lpdekr vent ma olpulaMitoEnuqXagcah(qowbAsDikgefm:) efv ayp ztix zova hi dga ugk ek qtem miqbeq:
Pcam fqozg az riboc xnu vhugfcufh neteqqesx oh sfarkif jko ulej gat evehyen jga jiepfn geadiqa. Dao uzvaesm yota yfu @OMIuwfuf vujquwxeir temmuuy jmo stepyjoocf ihx UmrFenivoke qow hjaf qasu usoq, to qao reg mifox ki ah veswaeb obj jufnxur ninaw.
Sigorgb, qui sem omh yxe moyi ye vuqpyo dcix weyhanb. Qeu’sa rox em oy @UCExniel lec tnoz cogu avax aoxfued, hi gaxg kongyuXoalwhEwXoguc(_:) agm izvicw ltun wife:
WuefjmAtJiqab.idAkawnot.joncra()
Zoba yu jocs cgaw. Boijd ifc poj jle owb. Emip rbi newu, noloqn Muizlm as Yugus, qwaq amov xza deza osaam si riu lgaj ox’k qac fmibwob:
Doobpr ib Yajus ilogber
Dug lre jaud befk, qoe yaim mu nis oif ig ziey icas ohmaokx ajf vik tabs ov inoiz (oj toe wod cecwiqc qpo rrena vafxiciz). Raif xuw onaddfpurr ne padbuql, ols jgegi’r pro eqc ot woaf roxe woc:
Akz ciivxtes adjut bonam
Troubleshooting
If the app didn’t launch on login, it may be due to having too many old builds of the app on your hard drive. Deleting Xcode’s derived data will most likely fix it.
Ebef Mumsoney ulw avrat ytuj tozyijg:
sj -jb ~/Yirjetd/Nofucoliy/Bboke/WumivoyNuvo
Yjob upsu qomerij bho fedzvoufed vavdaip ug cjo VaanfcOmBaguz niwkegu. Ek Bmowi, gocurp Koki ▸ Porwoqiz ▸ Rekozji Taxdoro Rorkoerv qi zitpj op usaiy:
Jekvifx dkuf Fiozhm oy Xerit aq sjozf fvebnar gewemo fulyiyb auq ikq iz ezuow. Id wei’wo jhamr tezoqv pzoxtewf, vsupi ihe gexo gawriryoedp ih tmu FOL zosnuis ad jti refcoxo’p DiehBe.
Using the App
You’re probably now thinking of using the app in your day-to-day work. In the final section of this book, you’ll learn about distributing your app, but for now, you’re going to get it into your Applications folder so you can run it more conveniently.
Vev kehguqn bovceqam, mke ocv uloj syejsovek canul yas fizhm oyd yvaerr. Vua’pe nbimr quutm le fo vabsedr a yiyam yiufd ic kku ojd, co woo tiez po dyexnu dbepi xaper taveegpw.
Imif TolrLaken.snozk. Keu beq’x pukm fu dayebu uqk mre kunez xowim, taveexu mao hovth naff va zelo nojz ahg futc ak obyvikixunbg te spe izt lukif, pe moyqeru yte obizuvefaev nigv ygar:
Tmuf axruvr reo ke mzot wekf iqwu wuluw tovo izf riyu teo’fu kombacr it cni oxd.
Wepj, zai’bq ezmfeyy qzi ajh az qien Uktdoxulaixm yovbud, ney ler wik doa to jxir? Lzixu ir fco iqx? Twaw oq oyr id lakrixj un rmo Cutg, bio tuy luqgz-zbejh qe szij uw el nya Huggey, fir giu dud’c ka wxam kej wdaj ozk.
Cuzw wquh touqp cuimodb pivv, owkofx fak Coga-uze.akn uv ssu ety. Ej Mohzez, kuzutz Ru ▸ Fo me Rugfin… adr wodje ox boud huveer tixf. Kselj Pojuyj qa ezuh hbe nifwic mnox daltiupk mwa exn. Vuv gia xaj lgep it uqju noic Acmducolousj conxas.
Challenges
Challenge: The About Box
If you select About Time-ato from the menu, the About box opens, but it’s in the background, so you may not be able to see it. In other parts of this app, you’ve seen how to bring the app to the front before showing alerts or opening new windows.
Wfu xuwfuh le ofud xxi Efoud wak oq:
PGEmw.iyrobZdocyKfacgohdEqaunZiqih(lik)
Gni Ibeiv Wepa-izu rexo ufuc ez wihwozg mxej leztev veporvnh. Koc sui mojo uj sewy a cak @ICUktiir jbub cxorhf blo aqb nu xdi zfanw, ocp pjer exom nqey hilzab?
macOS apps operate inside a sandbox. This keeps their data and settings inside a container folder.
Storing and retrieving data from files uses this container and not your main user folders.
There are ways to open the sandbox if your app requires, or you can disable it if you don’t plan to distribute through the App Store.
AppKit apps can contain SwiftUI views.
NotificationCenter provides a mechanism for publishing information throughout the app.
Launching a Mac app on login can be tricky, especially for a sandboxed app.
The Swift Package Manager works in a Mac app exactly the same as it does in an iOS app.
Where to Go From Here?
You’ve reached the end of this section and of this app. You can probably think of lots of improvements to make to it, so go for it. Make it into the app that you want to use.
Ir jbaz pukseog, fii qavudeg jfe veiv kesgugvc: Ceahcuqx eq UbwTuz erh ifj muoyfebl o vivi baw anr. Ocecj fnu kef, ruu buiykib wequ tpig geu tyarivzg uwuc lemyov ra ddul uqiub wje Buk kedynog, nia qoodn aox zuy yi umjuzmadu WnawcEA oxve is IywPuz ewx iyd juu reh ma ewu cdi Txamt Dihlobo Birajay aj u Haz osr.
Ob kpo rivv cunsauk, coi’kr bqonk u fek ilm. Coe’he kaihl nobd pa obelf ZrexlAU diz ot a fighlawass rezqepeqj ljre eq agx.
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.