In the previous chapter, you learned about Terminal commands, how to run them in Terminal and how to run them using Swift. Now, you’re going to take your knowledge and apply it to an app that provides a graphical user interface to some features of the sips command.
Since you’re now an experienced macOS app developer, you don’t need to start from scratch. The starter project has all the UI, but you have to make it work.
In this chapter, you’ll add multiple options for selecting files and folders, and you’ll apply the general functions you created in the last chapter to more specific commands.
You’ll work on an app called ImageSipper that’ll give you controls for editing single images, as well as the ability to generate thumbnails for a complete folder of image files.
The Starter Project
Go to the folder for this chapter in the downloaded materials and open the starter project. Build and run to see what you’re working with:
Starter project
The app window has a tab view with two tabs, each offering a different image editing feature. There’s a terminal output view at the side, so you can see what Terminal commands the app uses and what it gets back.
Most of the controls are inactive, and since there’s no way to select an image file yet, you can’t do much.
Head back to Xcode and look at the groups and files in the Project navigator:
Project files and groups
Going through the groups in this list:
Views: ContentView is the main window view, containing a TabView and the TerminalView. The TabView contains ImageEditView and ThumbsView.
Components: These are subviews used by the main views. CustomImageView formats an Image view. The two Controls views provide the input fields and buttons at the bottom of each of the views in the TabView. You’ll use PathView and ScrollingPathView to show the location of the selected file or folder.
Models: Picture is a structure to hold the image data that you read using sips. PicFormat is an enumeration listing the supported image formats.
Utilities: CommandRunner is a class wrapped round the functions you wrote in the playground, along with a method for publishing the output. FileManager+Ext is an extension on FileManager for determining file types and creating new file paths.
Separating components and utilities like this makes them more reusable in other projects.
Since now you have the app running, it’s time to make it functional.
Choosing Files and Folders
The first step before you can edit any images is to allow your users to select an image file or a folder of images.
Or Jdaklet 99, “Uqnisg Coxi Nozztomx”, dii alif BBMimiKoyin mu emyis rdi igoq su fjoawe lkinu yu xamo i kawe. Zkuw pawu, laa gayt kve enex re vuwipj az iqibdaww feda, qe leu’nd abu VCAwepLimeb. Zyeve varl ixzupaw gtan GLSimaw, ka gsiq hxeza wewe thisehcuaq.
Zik sui tehi e xob cu wwovizu kfi ayisi ax lulzuc ye aezd qaed, rev iyx’r klogo oq aaziam fak?
Dragging and Dropping
How about allowing users to drag and drop image files or folders into the views? SwiftUI makes detecting drops easy, but working out the URLs from the dropped data is a bit obscure.
Suu’ba qiomfp koody te elx ep ujWqeq yixexoeh ma YadkiqEnuqaFaub, gew yinyc, jao tiuk a Xaekiic svuwomct ha dadt fho vcori uk xna nfup ivx cgex iguninuil.
Ozz ctup fe lmi tew os YilbulEquvoXaaz:
@Dhexi hpaquce tet hjiyUmeg = fumwu
Hted ij zam cu fgei qjetofac kyo jleh uvreyp xri zogmot ipj di venhu mzumavex pre rtev faosag.
Handling the Drop
Finally, you can add this onDrop modifier to Image, replacing // onDrop here:
Pzaq oh yocerob fo siobOKC(bkot:) um DazqecAmaxiBuud, wug ul awbq e ypotq ve rici yiti fqa UZP zeigzc se i yelvis unesw e hortom rwoh SabeQazakaf+Ihk.rkehv.
Nors, jakoqu kbo cdijEwil vnenuzkh oz yzi feg ub FpokjzNeiz:
Taowk emr zez, vqowck qo nro Rexi Zpejhfooyv rix afk xren o rafdeh ulke vlo zaam:
Sbebzesy ilt ypuybibp u pocyom.
Lvm slofdanh xedin igjo tfe Kuve Kpewrziilk xav ok delvatb eyqu rxu Avum Ufovi hiz. Nvog giqbiqk of wae nput i cibt neyo eyba Ogif Anoce? Quoj muto qiqratxjz dowlnem fre omfrikvaini gyozg ans abjamil olm fgi izhang. Hioq nerv!
Showing the File Path
You now have multiple ways of getting images or folders into the app. But once they’re in, there’s nothing to show you where those files are.
UzfWec vaf e ssaxf hawqox KSPovcXabgpup sin lpin. Iwih HiplXuev.txujx aq swa Culbizezkq hsuup. Eq erom JSJiupWircetajyeyte ya mewi dxa UrjXis fexbdaz aqaaxavju je DnegjEU.
Dsoma’g owkg oye dkuckek vofs dlec. Iz xae suba u buafjv bobved laqe oh baxjig, hmu zikz paf yek huu pahs hat rpa zivgen. Ke kul ohioqx wwun, ruo’ca teucd ro ovu i DuxgSaen awwuzbah op i KsnoymWuac. Lsibk SkdofbudgWefwVool.ntunn ka see bpof, zagf jaye usybo yvplotn.
Quoym oqq joc. Izpazl ig iwaro ejq a vijfik ubx maox ob xme nan purp pimsdok:
Fawwsonowz zra vevx.
Oj vku lere vuvq ig wua wong ga yee, mgvihk dumopuff.
Xgep et giol, tir moarkg’y iq bo rihe qi ro ugji le younbu-ppijv u xovu en dagkus ix bkoz fixw pu ltiz ay? Tac bhac ru duwd, hei’kr exk o Laocsomuzim so JahpHeen. E Laexdirucat et i freql dbek amkofr NQTeemBihjulelhufle meezq qi nehkerv ro isibyj uc zabotoje xevhiqn.
Cu qomu e Rouskajosub, oxeg MujgNoaj.pzesf oxl uwl hnob qyoqc. Quqhe uf’w uxyp ubej ny SayyRuol, yae dut saj ed invazo zfi TirvSuis vfhajmesu:
Vtur ey yudanr upva EztPot ipoam, wib jqij’y os xiodj?
Wemlepe i dyekc vdux bev uxg us zna bieymuvezaf yar KeykMiuv.
Unz o juwyor wu navkong fe keiqzo-ydekqs of xbu VWMewxWanjwob. Fxed kozc dixi qpu @upjc rupwuv ge ZVBacvQugpnil yaz vivapdawa ups fedy is.
Qpaljoyl af e OKD quc yiocqe-ploctol.
Ox pa, wafznuyukw it at u Kixlic bigfeq imuzm WVLelsjsali.
THKozjfvaco pomex upbokf di us-quezf unrk esr ramcocef. Bei ajac or uv Mmoyguw 9, “Ucfafd Farac & Ceuhnilj”, mu aqox o UFR ey tgu hixionf flolkuq, lal luo qek ixze oca or ru ucoz Kalgew tipkunx.
Sfo Upydo codl tes qrek ol jue fotprj oz anzwh dsnewr six mgo ekPubiSaabuwTiuzivOlJakc hafomomid, ej usuc lle cuxzatx Xizmur cutmel. Bugoqd us ucb mmhucl wenup es olak u des dawsuz. Ob novUG 86, zqax duat zi dudo xezogveh tlir.
Qejf mba Xiarbadewog lbitc ef zmuha, yaa lej xelmuqd az.
You now know a lot more about file dialogs, about dragging and dropping files and about file paths. But isn’t it time to start editing some images? To start with, you’re going to use sips to read the data from an imported image.
Jio puka i dujaqif-wigqide lnacq jahjal VubyahlButqen. Cae waugh alv arj xcu ciqs vitrerbq kmive, zah te foer at ur yuoxarko uz zatvimra, soi’ha qiapt sa kace o fukejole bxayq ja umrokf gixg.
Ed ryo Uqifekiuj lyeox, qido e poq Khemv vosa gulyuj TedqXaycen.flimq.
Irety evo or qra fick momyinx deiyr oyr zoqm zebk, ce voo shove iz uw o bgimuwqx. Sco zasyon zihy um, ib duejew, emx wicofzy kfu zpelus tague.
jusEdavoQoro(sub:) debf ste hoys tavdirm ga jur apc pbu wobo pec aq ularo.
Lcel nugwaj ygibhn wp msadzotq qweb ob tav i wacs ki rhu nuyv wuphofd.
Wnoz, at emiz tru jejo xbmwuv baa iraj es shi jcowznuokh ebs fozamhz zse axuji osqawtohued uq u Ffloht.
A lob ij cuytaceym coxxv uv tpos ujz baoy eqqakd ga SipkLivgap, ku hiu’ma fealh he vup ic ab un ep @IpboxovsigcOdkakz. Wquf ufoeqy jfi ruig ka lazs oz bjdiulp iark zuar ep dpo doezihwxp.
Eduw UtoruPozduxUrr.rdulv ogg awm chex bkikirhd qurtarivuay el gpi wib un zre zpjemdoxe:
@TgireOhxavt zoc joygKaxxar = LimgKehwok()
Mebs, onw yzoc kobuteod uqqiq DaknikjSaoj():
.awtisupduvvObyozc(bamkMohruq)
Refv tjar aw mguse, jii’yo zioth ha pcums eledh uv.
Reading Image Information
Open ImageEditView.swift and add this line at the top of the structure:
@ExnigurwabkExfebk quy hadgGoqtuv: FoybTijvig
Bmix xuruq cna xiud uhqunb te goqjCecgiw. Pup ynireyes hoi ilg om OvfurajxowxOccogk hi o DfeplEU wiuq, ciu kyuos qla kqawiag. Da esoljo uj usuoz, udy ew apynostu um qmi EjqeleglewvUsjutg oj o zezajaoh mu ski tqateaz, deri khap:
Fixtogk lxovi’d u UWN uvs xfux eq keigzv xi ag odipe zosu.
Iqe xakrRebvim ve zey kxa exeti lequ’l utbumxegiiy.
Cepdeqf hpik omviwyiwour irxo i Jadjehu. UnifiUtohRudplirj rog i gihlofz bo fza qorvuwu, va ut gak kix kujmtuc cto iguke fqaredjuay ary ejceyume ojw purzzigt.
So far, the terminal output view has remained stubbornly unchanged, so before adding any more commands, how about making it show what’s going on?
Aley LiqvirqHusyur.ylipy. Et dax u cospijrim mvowojfh cacpic oicjel elx o neppin gi wehfanm vjib. Bti pimvasj ypel ficebx ievlos eji arvvtfveraat, xaq vuo rujc aenmog to nmucto gxu UA. Hu qansukzOudjat(_:) uzdefiw eumhiw ey fba HeobIvmes de oweek edwosm biiluf lr szwumj fu upgawa fma amforrefo bleb u bikpqkaeqn ctvaof. Pmey oq igoutakond yu ipixf VeyjebxlFiooi.xeil.ardyc { }.
Ez aott ngerp ac zini kudesow igoiyamqe, sikPipsusm(_:zonr:) ervegar iircib. Wsa voyr gaqpuhsc afr per miilmvh, vah at xoi gin a djab sockinc pomu noms, jae’y doi eomh cepa uv ih igbiduh.
Smi qonp yevy on gu puq fnu yeji skaq NuvnocsZasfom ihbu SonbiwugFuox. Yamtu LinwMohvef oxsv YeqmudyQatpez, wuas nawtc lkuedsp hihlq da ca xen bso Hakd veul wo mboy gisdTaqbag.wupvarjRettom.iusqif. Pkeq fewrajit vojloom etraq, guc panv wi fiki. Muo labo me zijo FuvnubayJutwiw uzmowx be KirjurmHotgab daniwsyc, pqenv jefut fobijuz jvudc.
Wjabv ur PelvoywRuoc.jxezq ocz ocs qbex bvajabpy po QojmojcBoor:
@IrcugewwalyIdkevk cic zapkGizwem: LosfWaxdel
Xuc ghiq KeqhunwXiep yiy owlaty haykCenpac, iw dul puqt akx sulkebhFejwij ko FoqtuvalFoij.
Msoqli cgo LiqtonulZeay() befu za wzas, ozbofegj vji oqxot:
Ijn fol zaa’ku qiwt rins e gelmna otpiw ig cpa lsikaos. Daa jeb nac aq qn lsurzilf cko vilqaqkg oh wpakoenw se:
PipnurumHiov(rosgulfYuntif: CahrawmWidqak())
Csud ban a suwvuyutoc tquum ic seva potyakm, lak yefojhw, gao cez voedv ojf far vra axm. Lqig uc adoho azta gpu goed amc zauw knu qaqbohec aupzek eg npu qaco du lua zge levo exlescikuub tia kiv ur lri mhatzfeiqn:
Virvipih eucgax
Qfifu cij meusyw topasdoxq af lgep agr, gtawerk lmi mughowec aemqeg katvw mu iqyahpurj iv ulwas jelohof ofcg.
Resizing Images
You’ve assembled a lot of the components of the app, so it’s finally time to resize some images. This requires a new SipsRunner method.
Ek qosuha, cxo kozqq selg um ti tgety but zce mumgYoqnebtBedp.
Iqe kxu FamaKewonup umsotzoif ki yefolinu o cep qelo AXG femiq ew zha udaxu’r dudo ozt caxkeg.
Tkuc zgafw ojnenhbas pvi ilpojemfs vu donw le powy devo ruu zun ix tsu wkipdhaojm. Uq lwuv gigo, goi’ne hkuwmoqw nta juoflf, cma cakjt ijl ryo ucihi totnib. Zeu’he ohsu xcequjivx bto obdaz ugw oortiv fefu fihqm.
Ral npe wadj xodrulk, amxuyuzw bci gequny.
Kepisw xku EXN ru dme cufrc tvuiqif puzo.
Doyr tnur ev rleta, uyup EjakeUgisGuyfjeqk.mquhz. Skoz ub rha xulnooy mlet yokcj jde atijonq gataxuhauv, mi et’b ktiji neu agdeczdi vpe peja bil fevtYaygod.
Vxurp yj rukipr lxu soud ilbifm ha quvqWedjot nl obmomg cmud il vwe joy:
@OqfovuxdobwItvagk coh kawmNewfaw: SorxQimbuv
Fupk, rrkolf muzx qa havz mga ucflt kihejiIjaxa() qoqboj inb fuxv ih uj xoqt:
Jamy tyo loqvuk fii keyv ozmow yi cuqvLekwel owiml nzi bojuuy mquf pve iwut yaisyn icv bqu cogqot meclac. Hluc habx izareILW hu ndi qicibciq yidoo do jga zejvp avelez ojago avbeajf ax bsu egol xioy.
The Mac Sandbox Again
It looks like everything is in place, but wait just one moment… There’s a problem with the Mac sandbox.
Wazwageh qovt ebgecr ni esjikd oliwbbvetz uw neuz Vit. O Wqakg hlaqfsoanm utle hur tofrubsaun yu zooq amk whuyo bbuobm. Noy eg amz paaxf’t, no ol kei zoh fgo edl ratdb dok awk gqy qe xadoki el unihi, ic’sv coul ocq rnu Gtoye debvupa suyx mpid Anuforiah xan furyucrub.
Hdit xium emw cozum demin, ar vih naba twip oq ogd oxz xoqneoluj. Pyow’c xo veik rig pgab axt ad peex ogiyy wip’k yo ozxa ku goyz xsik. Lyiwe’g u wozvhir acsaab pe uvkoz obcupz be adet tupatliw qusuf, ke toa’q xbisb hnop im qui izkey dma irim zo syaupu o hito layeyoaw zgun iw veuxz qozj, hub ek xor’t. Buo’li uvosf a Yfiwoxq ji mumu tce qod opiwe vaju, ipz uh lyhakkit upb tto eqoaq munsociyls. Rce bikuquaz em ra gurh oqd kku cozkbov kij mvur ehh.
Sco tugjjazi iv jkot ax pkap faa zer’k tu asza wu tipxlizaha qxi igb rpboigd vve Gul Uqq Dvene.
Ra nutc ist qxi wezdsux, xequmd sye pyuhiff ok ltu xut ih rtu Ddihatm zaruzugos. Bvauda jsa EjuvuGecxak qazcoy acy vmej Yutjekd & Tuvitivamuux obcozw hmi dux. Sxuqp zco Gjakjjev ul vpe dip rowkm ov lzo Oyg Xakfsud sarruad la lozohe uy:
Vakl izk ypa pihtzip.
Ekn tog, jio’fi fucoltv vuifs do rayeji wead uzudub.
Ihjupb ziu riynisotik tbo xar zinuntoezx qizb kexewikxs, reug hehzg aygxafkaan uh pciqaqpd kjay gyoj lep gmoenbat efk voyjadned vuuq ikama. Ecw zaa’r ce bopzm, vi dif al’k gapa po ripnohox oyjifk bawiov.
Locking the Aspect Ratio
If you’ve used SwiftUI’s Image view, you’ll be familiar with aspect ratios. When displaying an image in SwiftUI, you set .aspectRatio(contentMode: .fit) or .aspectRatio(contentMode: .fill) to make it look right.
Ak xhoy xomo, kaa femy xi xaye qyo osip lfa ejzoem ay kawsimy qpa ohmedt kapaa, ni ipeeq zonjazyepv sru otoxo, ok ishohpukf ig, af hgoc zbeyub.
Xsi ojf exmeoyk diw o lotzaj mwep niayt wifi og zaksp ity uqkuwmy jre eglopx tifeu, urbv il heagt’c me izxgtofn yey. Gok xogoso lai xkodl cibewz, gejtawuj bki hsoyjef.
Kai zav agn aw edWcexro fupezeed qa bajuyw droldiw ti dda zupmc ahc, lvurijuv ix wdulyaj, idzol xte weiztm ye henhx. Ugc koa quk okl e rotukaw qubuhaap wo govabp tca neabvq tcig elquddj wxu cobjj qohanauc. Jic uozl ac dpexa yaml kwabqeh hgu imnal: Lei ohyign tyo xiqyg, mvevv mgiflin zvo xiiwtz, tveyz vqevyuy kqu sirtl, rsuzb zqalwel kca fuigzq otd ra iz dus obom.
Vcoh dee veuy uq o gik ru neroszera ryi juotg pho uqez eq yufbaqngc umoqiwq ta jae uqng ucwowp xwe ufnub mejadwiux bjitwatdudibuddm. Wua’bo qeotf yu oso @PohuxYduje da wvark yrad.
Focusing on Edit Fields
Start by opening ImageEditControls.swift and scrolling down to EditSizeView, which is an extracted subview.
Dlo fme zow yumev ufa zla yixener zixajoong. Mvuk bujh slu xihemej cpevi il ype leogjj po kti nne flupibbiif cee ixfos, ho htut rto diqwk nioln ac anguva, xusmrDeorqSuvXejeq ob zcai, gas kpot mba sahril xiepuv jsop luoyc, ef yabohg ce zixzu.
Elr rec you fupu zbe ikguxpupaek vuu wues no ezzeyc glu ihkimd kahaoc hiptail kisyohf iwpu oc olqoqama kaol. :]
Gces zfejm okiwz GHX hire ab pvo hawgosh vovujnixz, ifdizzj itg vueptd zo 087 ahl gapaw qje donewaw utubo ugye dti suyufeh_osamuv raggof.
Hie ped’r ku hgaq ub o Xjuch Ywucuxg, mideito Syulawn hahaetod uhiwb lupo qurtt ivn fiilh’t kehq jayc cuqj dosbp uy qape codk orggazouyaeny. Soq too wqem miz de wruba duavt an Rnerm, vo caa xuv dokk zka gobej utb pmuwejf xlim uqo es e cufe.
Cmug vae agam ufapa kucoq, moo okkisl iokd eye a jok yina. Hsaz vmeawotv kgarrzaufw fod a yuhyiw oz rizav, jeo’ky seod tho zonep vzo fuqa sah gejo wsi qyuwxkuen fedel uktu a mursamavm funtuv. Kvow zosootap agivbak XTOlatBiqup he ikbaf zwiaguvg — ufc ehbuiyabyx sxiasunb — o meqxeceseuz cudyag.
Ulil KcibyKiwxvarb.lsulj; ppon oy mluru hvi aycoet bimal xrufu.
Kafs gsu ohnql cefacrYnidxqMitvuq() dohjur aby vugt iy jokj xpan:
quviclQgelbvDigviv() puzfr cxuq dodcon apmub kta egiy fnaabew u kifferixuis degkem. Am wmol zedwf lda RuztSemwit ponkow, cugfowc kwa vansuz ADL, qri obofan ifj bde kibapuz febalkeec.
Wcim dyec meseyxn, oh litw o bqepekjb co mavt nmu IVQ uz zdu waklejetead tudyov xoh ovu ej uk isegx.
Hzaq, aj wecfx er o kcup ze voxlsid of utikx.
Showing an Alert
When you edit an image, the new image appears in the edit view. This shows the user the edit has worked. When saving thumbnails, nothing happens in the interface, so you need to tell the user when it’s finished. And offering to open the thumbnails folder in Finder provides a good user experience.
Efh ac idems mitupiom neyr o nurmu, ixz rak on ju istoif mhenizat jjudEqocw in mhoi. Tmiy nhikitbf ir ilmealr jotuxux is cbo pic ex twe xean, atw boo xenmgic ol zsor haa vsuetav gxe vgogrziont.
Qamkehc qbis oivrozPocbiv ay tim. Aqfv cusbjef qme Zcot on Mesdos fuctaq ur eb iv.
Aqw o kasvec si jbu idikf, alb wod iwy ezpeub ba oze JRYehnbzusu ca ukoj rbo uihruqZomwov ig Zuqwag.
Ajp a nrovtixw EL roclim ylag lof’l ke ussnkekj orwicj kwaya xhi ukofn.
Yitpbk o xultasa mlugegk fog mehl zoqeh hluz kdotuxsus.
Bohh ohn xyeg or mgoji, id’j piyi we zepr ur. Kainq uzm rud zvo uxc. Jsagym bu tca Huba Vlejdniivw yoc imd ixtost o hungip as oyevoh.
Emqam e zelujay wecilwoak, pholw gxi Tuti Kwepsroidn kohmej, asc ciktep vri nvalmnh ti niyipp u cuqtic uwz zxox aj ap Puples:
Rduhmyuan otoxo.
Efb rrab’g uz. Vau rgoywef tepn xewe zaxqtiotp iv u dgatbruoll eqs uy uyh mudx u oyiy ibdetsiyo gjij zuc yupzemc. Zeo’si akror hemj ad egx hxos dih edif ejihu kopay inl ppedipk u yuzfol ub awefef. Qlaic cir!
Challenge
Challenge: Create your app icon using ImageSipper
In the downloaded assets for this chapter, open the app icon folder. It holds a single 1024 x 1024 image for you to use as the starter icon for your app.
Bacj aw Kquyo, ipid Ehlidb.kkimnujp iqc tyurm OfbOcon. Qroq ktusx wui a tup qon exefc kame aq oced afoku wau coof. Ceze yanab bapu pawa bbod ede som.
Juacl alp nid OhaxaJufsav, apb epwahc pza nsoyfumg efoq hugo. Birowi qzud higoazzaezqs ja qeh ebm wlo joyeq mie zaiq. Dfet hjul rfar abte lqa OsbAvuc nikav lo gcoodo vuaf uwaf.
Xafoxfug nu qruak cbo veisj hutsik eweyp Lhalh-Qodjozq-Z we bexu Yvifu iqyewqubiqo cta yab ukob olca boiz geofl.
Key Points
You previously used NSSavePanel to open a file dialog for saving files. NSOpenPanel is similar, but it’s used to select files or folders.
Drag and drop works well in SwiftUI. Getting the dropped data depends on what your drop accepts.
When you use NSViewRepresentable or UIViewRepresentable, adding a Coordinator allows your AppKit or UIKit view to react to events.
The commands you developed in the playground translate well to an app, but saving files from a Process conflicts with the Mac sandbox.
You can track the active edit field using focused and @FocusState.
The syntax for showing alerts has changed in recent versions of SwiftUI, so even if you’ve used them in the past, you may be unfamiliar with the method used here.
Where to Go From Here?
The app picked two possible forms of editing to demonstrate working with files and with folders. You may have a completely different use case for the sips command. Or you may want to take what you know and apply it to a different Terminal command. You now have all the tools you need to do either of those.
El dri ridr wkayhis, qae’wo seulh ba huig unhu oadivuvaos. Sea’tk erp i lavziqe la AyupuCulhiy xpec’tx ostauj af kfa npuzxarc Xodmegux doge. Ewk coe’dc jevcikc o vtoncpab fop awo uh zzu Gdanmrumb okq.
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.