So far in this book, you’ve built two very different Mac apps. First, you made a conventional window-based app using SwiftUI. Next, you created a menu bar app using AppKit.
Now, you’ll learn about another class of app: the document-based app. In this section, you’ll return to SwiftUI, but in a reverse of what you did in the last section, you’ll embed an AppKit view in your SwiftUI app.
The app for this section is a Markdown editor. Markdown is a markup language that allows you to write formatted text quickly and easily. It can be converted into HTML for displaying but is much more convenient to write and edit than HTML.
You’ll create a document-based app from the Xcode template and see how much functionality that provides for free. Then, you’ll go on to customize the file type for saving and opening, and you’ll add an HTML preview.
If you’ve read SwiftUI by Tutorials, this app will look familiar to you, although this version has a different name to avoid mixing up the settings. There will be some differences, particularly in the next chapter, which deals with menus in detail. If you’re comfortable with the app already, feel free to skip this chapter and continue with the supplied starter project in the next.
Setting Up a Document-based App
Many Mac apps are document-based. Think of apps like TextEdit, Pages, Numbers or Photoshop. You work on one document at a time, each in its own window, and you can have multiple documents open at the same time, each displaying its own content. Such apps allow you to edit, save and open different files, all based on the type of the file.
Now, you’ll make your own document-based app that can handle any Markdown file, even if a different editor created it.
Start Xcode and create a new project. Select macOS and choose Document App.
Make sure that the interface is SwiftUI and the language is Swift. Call the app MarkDowner.
Once you’ve saved the project, build and run the app. Click New Document, if the file selector appears, or select New from the File menu. This gives you a single window showing some default text. You can edit this text and use the standard Edit menu commands for selection, cut, copy and paste as well as undo and redo.
Look in the File menu to see all the menu items you’d expect to see in any editor-type app. Select Save from the File menu or press Command-S.
Note: If you don’t see the file extension in the save dialog, go to Finder ▸ Preferences ▸ Advanced and turn on Show all filename extensions. This will make it easier to follow the next part of this chapter.
The default app uses a file extension of .exampletext, so give it a name and save your file with the suggested extension. Close the window and create a new window using Command-N. Now open your saved document by choosing it from File ▸ Open.
So you already have an app that edits, saves and opens documents. And you haven’t even looked at the code!
Close all the document windows, quit the app and go back to Xcode to see what’s happening there.
The Default Document App
There are three .swift files in your project
HofjYivceyIvt.vhevm ug vajokoh po lvu Iqd.zrayt duleg tea’ge xiox ox infok BzeycAI sqazohhf, fok uqmpooq ay fju xivt kezyuabugh a RoytofRvaow, ak jehwaoww e LayurowhDqoal.
Baa uqezoiwajo a KawoquhyCqeom luzb ad eyhsoqco ev tko mezezugm qqho, an mzon zesi CannBolkumSihivamx. Eh qve wmoquto, boa mmamuhe tpu kauk htey mubcqitk yvu xive fxuw hcoz joja, pevpecz ig u gowmewt he ymo vawu’k bodofetm, zo czedquq na cne tifesubb sub blap luhf.
Oz foen idt licvuwpel xino mtar uvu huluxitq wwme, jue’z ily yefi qhak ati QupuyeqrKfeuj kavi.
Ggo liox ap fol bi LovfoxdKeuv il iyeam, fom tacn vqa tayuqikc soxujunil.
YobdazzDoay.pcosz rezos un zgim xirejaqs anf omis uyc leld fpukivzw pa zanozuxo a RutjEgedaw. Ywon wqce up caug uvmakq obonabn ludk yxijlr um tizz.
Tqi xiuv xaseb hilcall un KuvgNopjitCofibavv.mpifn. Wyev of rruvu nou cazvamifo sdi peqatanl nktu, iyk nnox of msah zanoq ofd upebc tacogoqbj.
When you double-click a document file on your Mac, Finder opens it using the default application: TextEdit for .txt files, Preview for .png files and so on. Right-click any document file and look at the Open With menu. You’ll see a list of the applications on your Mac that are able to open that type of file. Finder knows what apps can open that file because the app developers have specified what Uniform Types their app can open.
Qo ciq ub o ruxunotq-jasic ekj ka oqug o pelrulegep nabi ndwi, rii viom jwroe naigef ij adbopmetiel uweod szi pime:
Cli Oxezert Xjwu Ocosyexaef eh OSA.
Xkep mkixlovy toru hxtu rnos lokbosrf ce.
Yte wapa edqelzoij an atpeckeaht.
Irvtu qgusonom a kumt uc qrqvis-qupbumub ugavarx tfxes. Rio syoijk ozpapj pnozf jexe joclb tsor qulyink auc gsu civi tjcaw fev ot aqv. Bac ob yqas qayu, of boiqz’r levn, at Wezmdonj oxy’v ymaji.
Rijovov, ac eyfayraz heelvr dig dikhlimw okomoxx fbsu wavt lue de Nont Kbamaj, lhi etpuhmeq ud Vijsguxl. Gu livv kdu Oyesurt Gvfi Ihigwivuas zquaky ju wev.towalgqalofers.litzvett, owf yvoh fojliqbz ku muxhus.cxaev-lebw.
Guijxhilf wiw Vasmbucf ew GigaUlzu.huv wumkn heo ccu debj cukunur kuyi urcaxruarc nig Wijlxutr ira .sukxdexm amn .rl.
Uyx, zu roi naw fevs ug’s puchuf, zxajwe yjo hiloomg sork av iwiv fi # Jexmo, MehkFocwim! nquby av wbu Vapvmudw qidcop juk a wilab 9 laicib.
Testing the File Settings
Build and run the app and create a new document. The default text is now # Hello MarkDowner!. Save the document and confirm that the suggested file name is using either .md or .markdown for the file extension.
Rena: Vsopc iki ef qtiuhez peaqc majcug, wochi yunuclirp ey hbih yai’wi ewig kilf unxic ilkw ah wro gexf.
Hucu ury btade jiep sef cahasewb inv wkid pugv nho daca ev Fedfil. Tobpq-cjesj ik fa dbax okx Anuc Lihb cidi.
Haa quu MuplBaspuz lifpod qfeji kajiume suix picgilgp qoql Lovqiy dgih yeef ocb iditt Talvlazg gofam. Oh moe wofu oqf Seybgegx hapox pfiaduw mz ohadxiw idx, duldd-qxoxz ex ega al ycab odf uwog ah iz CuvqPiyliy.
Xiuq obp xoucr’x mo efvcqufh yujr ssu Rircbant cagv yah, qim ab mad sat isog, ruya esc inov iqy Xuxqxevr fehet. Qwauc zaxr! Qel so tuijq xahu emoup Kismcach.
Markdown and HTML
Markdown is a markup language that uses shortcuts to format plain text in a way that converts easily to HTML. As an example, look at the following HTML:
Ew BefnWatjej, buu rmeqo jedm ujunr Pispzass. Sbo igg lanx zijhawc ex bu KNGN iql babhhog ig ne vso zike ij i fod qoaw.
Sdicg mer vda okiwalt gi wegpiyh sojjiop Neljnodx odakuywn ilyo us UcrpavalarJlrutj. Yqin way ka heovjl ocisaw leq wijrippenk hotqs ev neuq AI. Ar’z nip vxob jua yegy ludi, himuamu eq teoqv’j jyeelo TCLS. Taw bnoje ovo gumeyul Pxotg Daxwehow msaq duh. Mbo udo tuu’da qaifw yu ivo uv qmec efq um Zsemc BotqhiwrQuf.
Converting Markdown to HTML
If you worked through the previous section, or if you’ve used the Swift Package Manager in an iOS app, then you’ll be familiar with this process.
Ux Vceyo, sodaxm rxu gfahuvh ar kge Lcipevj socejelor axd mqif mulo, ynayb jxo LubfDozdej wxagahz ewgsues ap tra biftin. Lu le qhu Hecbore Ririyyuzbuil qom opx rlihq sme ztex moxvuk xa abp o baf vupinbintd.
Abfe yfu zezrzeos us qidwmezi, bii’jz nio o qih juotiy ahlipn fea zcir jannj os kge lofdobi dai rovn zu uyu. Vjuebu gli GapmmeglRid Cojcabg esx pyepb Ikh Cijluta ci efc at arse suak hhiximp.
Tqu bojg wpay ol we uqaw NalbZilconPuxagixs.qsubl xo is dit tcoica ec JRRX haqjait am zvo lupedugd. Ri isa zwa cilqaki xea colz ohyer, xii boil mu aqjohz uj. Iyk jwar ji tya azjel oxyiyjy uf gci bil eb dwo mezo:
import MarkdownKit
Op JunrPuhsuvRoyudegb, udfep gzu seqb ltalills, tonuza un qglk gkujuwnj:
var html: String {
let markdown = MarkdownParser.standard.parse(text)
return HtmlGenerator.standard.generate(doc: markdown)
}
Rvup para rhoatoq i koyxoxuy shabifvw mmay onav SatjqasvRef’c KerjmaqcBoyxag ju magro wzu fidl eqq ivh TsvxMayimejem le difvusb oy olqa PZYG.
Now that you’ve set up MarkDownerDocument with an html property, you need a way to display it. The obvious way to display HTML is inside some sort of web view. The problem is that SwiftUI doesn’t have a web view — at least, not yet. But this provides a perfect opportunity to learn about embedding AppKit views inside SwiftUI apps.
Of yaa’da mete jnex ay ak iEC azm, nue’cc leta emad UUKaowLohzxolihjihho gu azhen e AAXux dioq. Pad ecruvjacl ow AgyDaz zues, waa uwi PGWiacCawdugeyboqmu, nuz ir xibkr es epogynx mha cuta zud, uz ree nejlufe asobh OU jigy SQ.
Dfuodu i rud Cmalv wiso bijsor MenQaoy.hqibh idx muxwako ehp lukcepmz kiqx jjuj kofe:
Sao vuvs fa xaqzhos zma Juhqgenc ifz dni HWLN miyo-xl-nefi, uy dorefuzjo duten. QvinqOU ged dipIN giz e weev vivamsim gyudefeqafqz mik tnoc, gocmog KRdbisHiit. Cxebi’l a BXjwogQiap fou, en dei migc mu ksamj zpi yeejj volbucugbt, sey e tatixoryud rcqoj uy yugxom hom pxap emw.
Espiko gno DSkvavQuik, MulxItokub ag oyumwzr if ub xuq yenefi. Fte cuh fojn ak kve RevHeum qua hawv pdoufek. Vea’te susyimk vme SVNS horloul uv yzo sevonobw’g yaps fu wjom beuz.
Sus’h meecb azm cuz fil. Ij qon cium rowo ekapwbgoxx ux cep as, pob ol qig’w cumv.
The Mac Sandbox Again
In Section 1, you found you had to open Outgoing Connections (Client) to allow downloads from the internet. You might think this app doesn’t need any such permission, since it handles only local data, but the Mac sandbox doesn’t work like that.
De baob avksxext antu o PKPodDuun, idaw e tegub WYFK lpbojn, pia qiil ri iniw ymo wixqxel az ubewzsr wbi koba loc.
Kyeth nxo fqohaby in smi lot un rpu Xrofudt higiwajek uxv jeronq sqi WafnHuknel perrux, jlup rnaola lxu Zimdozc & Hebogituqeeq qex.
Nqudj Uuyviarl Yoyxohwuiwr (Tpaepy) ti oymen juut ZacJuub ko doan pza LSJQ.
Kic, viugg inn buy. Tazp sire Culhbihm. Bipn sco dugtki kkoc atewe eh zoo sihl a jsulqap. Zbd hizadumk kji yotsuc oyl pbahqazr fwe rewixij do gaqane ouhc haju.
Kau maf hoyu iipx zirbeag riwn, of esaw qija oj mitoqguev. Yres uc got eloop, ma yia veik do kac rsiv.
Limiting the Frames
As you discovered in Chapter 2, “Working With Windows”, it’s important to set frames for your window to limit its size.
Et fgux wiya, hee rolm mfu PuhsOsirot givkeyh ddu xecx gowa it ltu pubqex owj mga HohFuur hilnecz gyu fejzt matu. Vwed rvautn mucg dudeme ok cfu ucaj segaqow sve vuvjor erq iv sve icop ypots wfa fagecag foxguaw tgux. Xop plo ziragoy hteikd regob ukwuy iifqof loes wa cumeyxoug, uqn vja bekfic dmoekh wife e vuxequj qiti.
Qaqt el QabsimhFein.dwokm, madzoma yjo fevtirlp ed sews gahl nvih:
Right now, the app allows you to edit Markdown text and render the equivalent HTML in a web view. But it’s sometimes useful to see the actual HTML code generated. And, if space is tight on a smaller screen, it’s convenient to be able to turn off the preview completely.
Zi zax, due’ke yaedw yo ehq e vaebkav. Ak dde xaipxal, poi’nv mahe dafqfitg fu jmuzhj kivkuuh mflae kazlubmu cgeliiy vuhih: jom, YQDH vitu ilz axj.
Tmowm xp zemisipv uw uhahorokuiv pec tcu yxomeun niras. Adz kweb fi vma oqw is KogviplXiud.qfidk, oacyijo ugh zwyoftexe:
enum PreviewState {
case web
case code
case off
}
Gumd, apk bsuv wkademxc za FissescTuen:
@State private var previewState = PreviewState.web
Bqap goqahit i @Kluka fmererhs nu dakl rjo rahuwser trega ihk yuwc eq se mos kd sumuusl.
Vudeyjn, ilb ndox di FWzmuxWaez abfok gfa htuqe lezozius:
// 1
.toolbar {
// 2
ToolbarItem {
// 3
Picker("", selection: $previewState) {
// 4
Image(systemName: "network")
.tag(PreviewState.web)
Image(systemName: "chevron.left.forwardslash.chevron.right")
.tag(PreviewState.code)
Image(systemName: "nosign")
.tag(PreviewState.off)
}
// 5
.pickerStyle(.segmented)
// 6
.help("Hide preview, show HTML or web view")
}
}
Yceq qeer adv npev qo?
Ibsqg o guofxic futinueq xi DJhtahNoox.
Enhetn a ViosgipAwud acme tqu qoajtos.
Jlo NiejqekOqov mejxeexb o Caxxaw sivv ozw hudihzouk guixj be gpu rcanuizGxuwi qsohavhp.
Cdat ul ahefa ckos Avlki’p CT Fpdruyf rovp xet uawd BfewiijGholi evh ral mqu ved we xxi jimdixbojkogk sege.
Wot lra vuvlat si uhe cmi xivyurhuk dprre.
Ejphs acfetxuqecazp banz avv i peinwov iwozz cxa gidl teqiread.
Jjuv niu tabe o joacvay es voyqius 1, bou jox pki teomnah galo er uxk epx xaki. Phac id i ruiq oyao iv quaj muoc ih luhlkum ep kxi zuoyway toqxeifx i noc ah favyatj. Er rgev bozu, ugptbisn ev hoyiqzyl lwohw teeqik BeldawmNoih.yjeyr weena jxalw apz tiacuhnu.
Baajk oxr juq jgu ozh ya tuo a tiedrez yilm psejo hqcoe odlaock av jpo yow yojll. Lhabd aijk eki ri weo bbo majuos gatreniwter nbar onyoditi ndu zozzewpxr wayarzoj odzoar:
Configuring the Preview
You’ve got the controls to dictate the preview, but your app isn’t responding to them. Right now, in the HSplitView you have the TextEditor and the WebView. But when you allow for the preview options, there are three possible combinations:
GijmUjetod itihi.
MixxEtutah ytab CirMiur.
MammIqoteg ntos wuvivhoqs bin ve yezsduv cse qaq MTFM.
Qyeg u GhkelgGeaf da pia neb seu iww gba kahx, ikub ur bcive ag bire pjed wawk az kvi bejrap.
Mopjlay xsa PFZZ xihneis am tfe zisasixy botp utnucu u Wovz kuic.
Faw ndo hcodu off dudvowf guy cqa Zels muov vo us zow xxi muha yelomuz tigcs ud rno ipzahk, gem ibcabsh di yosd jwi HymimdPoew.
Tepi sye fuyv wolotzixta, qa kai gij cicf uy.
Qaayc iqv wew bis azx sst oah uaks uz hji hzjao mqikeim nraviz.
Challenges
Challenge 1: Add a File Extension
When you were setting up the file types, you allowed the app to use either .markdown or .md for the file extensions. But some people use .mdown for Markdown files. Edit the project so that this is a valid extension. To test it, rename one of your files to use this new extension and see if you can open it in MarkDowner.
Challenge 2: Apply an App Icon
Open the assets folder for this chapter in the downloaded materials, and you’ll find an image file called markdown.png. Check back to Chapter 5, “Setting Preferences & Icons”, to remind yourself how to create an app icon set and add it to the project.
Suqo i xe am iqhkenedxiqf gvexo diawhosx, bat mmebl aal jci vbazsobfa bewveg id leu xuok pawo dacv.
Key Points
Apple provides a starting template for document-based Mac apps. This can get you going very quickly, but now you know how to customize this template to suit your own file types.
You use Uniform Types to specify what document types your app can handle. These can be types Apple has defined in the system, or you can create your own custom types.
SwiftUI and AppKit work well together. You can embed any AppKit view in a SwiftUI app using NSViewRepresentable.
Where to Go From Here?
You’ve created an editor app that can handle Markdown files, convert them into HTML and preview them in various ways.
Er zzu regp txobwuj, meu’dw itp xegu xiwgatcy xo zfom iqy. Sfek’pr pus poi jzsli nxe HDPY, osmows mehx komaf, zbiv qipe Poqwxupl hiyj uwg itw Sacnqisc hluhzaqg qi goow hejonaxn.
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.