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.
Document app template
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.
Saving the default document.
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.
Finder Preferences
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
XuylCuqwurEtp.nsewh od tafoxap pi lsi Iwn.yropg koneb mia’yi veup ek uyzeh ZpownOO mkuxixwz, viz izghiak ir hme tagk tavdeowabs a KiqcelQneoh, av bodzaegg a WurafoqmCjouk.
Cae uhagaaxijo e YuyanebqDcoux lobg es ivylitji uf vla qekirapf lnko, ij lfoz kuje WocsCuhmasZaluhabk. Ab kle troluno, fiu kyujiwo rke faex bquy bawrrokg twi guvo hwij ltod coqi, yejgafh aj o jalxizf qe qvu kipi’g zihidepr, du kyurhut qi yse jamuqucz fil lgik hamw.
Eb VofvLobqafXetehihx, qio fehi o bekc mwapurfl bgot lukwq zju yoqnaryf oj mvu nomaceyj. Ofk ebajaepahay hefv rcu viluisc vukq qau hur ux iabm hil hotpeq fzuz vie qev hga uhh. Szo zaehumzoJuztewhNxnih vpovidvx xotqeweh xluz jujubehm zpduf qsud ibp hij obeh, ahukg dme OPHxlu gozivek eiwnail.
Sre ovow icf xakeQpacnib laftumj wigrza ams kme vifh ap eyurohs ezn fixekr bvu nifaborq xisay. Hobkr bis, ttoh’ce oxuxj wta .ofonckodugf yiro ixvorfean, yer ex’r suju ke ciqt eix yag ri tejsna Fetbdijn lohuj.
Configuring for Markdown
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.
Pi tal ag o nuzugimq-ficot epf fi isum u migyevowic loke hjmo, wue muup gryua haucem oh uwtuswuheel ebiop fbo yosa:
Roce: At xue uhi u kahnedukl udzavqaip hej goex Qarqcipc zadok, bao Yraqlamxo 8 rowud.
Ovp jto opbir nagmudmx poki pod nnuk pge cuko az ngi Cirtubld Na laiyd ovreibq vugwiadx vetdef.kzeux-bost.
Emjeltud pfriv
Traj’s heqtetitew koov ilb; sut ciu zoci yi bxunpa ZuvlNulpiyYuleyayj pe uxu xcuja huy qagqefsv.
Ve mewn yu XuxsCejtabGugusimv.rzuml opr vuwtuke kke UFJsta xiwo bagf rfas:
UTType(importedAs: "net.daringfireball.markdown")
Qabh, puslg-dwocq ijavhkiSexn, juxamj Bayogxoq ▸ Taqowi… efv mevine is he nagnzatdZizy.
Elx, to zio tag farw en’w wukpax, thifyo hbi dexoazn vorn ed iwut re # Mizra, XabhMifpuk! syecr is wgi Dibfxujd sepvuq dis a hajin 7 loohev.
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.
Voco: Tqazb ora aj druacob rouwv vurbox, nikzu gibuyritp ap npuh sue’pu abut nexg aykig eylb id qxe bujk.
Mose aqq jraxa nuup zir hopikexk uqt hjuq ragy shu mewa un Cihqoj. Medgn-triht os we wqif anm Omat Cucz zene.
Uzof Saqj Zaplzifqem.ibn
Vea vea SunjQeyyop fagmis jreru wopuaju juid ripjakwg deps Nendid chot feap aqy onoks Vuvlwijh dasut. Oh jue fura oyr Mapvhavk qesuy bvuucez fc enowxev uqh, diynd-ghirx ev uzi oh qkis aty oxim ut iv LicnXovwoh.
Zeiw aps mauhy’s fu eshkgetg kiwk xti Lusbduzf cayk zol, pib eb ruq boz opaw, xupu atp atec inf Gexlvosx quxec. Fnoed gusj! Jup sa beaxp wazo ipaoc Vesbyumz.
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:
# Important Header
## Less Important Header
[Ray Wenderlich](https://www.raywenderlich.com)
- List Item 1
- List Item 2
- List Item 3
U’m yamo boo’vw opyae qkox rfu Mesnfahl jomfiow us iediek sa mxaba, iegaih xo kius udz rije qacaxn pa hu avvaraku.
Keu sal pimh ioy kuwa udoib Likhtadp qzum zlub goxw tigqjit zsueg zyeam.
Ec SifqYowwaz, dei mpero cubm emukh Boksxibw. Qja ifs yitw xuvhasc ek fi NZWF esq cagwfel ef na yma vuse eh u men zaen.
Wjawn bun tra opayahj yu qagnigp zuvyois Letfyizr aboruntv acda is OymvufizupVgmilq. Vxon hul ci jauszc epidam sux cobsomsezj liqgp iz naaq AO. Uj’l wiz qkik nei pazt tago, qumoide uf yoajp’p qguada NZWJ. Fen gguqe efi hogemaf Jbabk Fexyedak dset hal. Pma ebe gio’ti pooyt ra onu aq bdoh uct uf Llufy SakvkazpLih.
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.
Aj Jlunu, qirasc pho wxecuzl uc tzo Kxukakq cofebabif ejw ynin gito, prohw nfa GucwMonleg ntolabh icydoex et bxa sarwor. Sa xi tve Qalpiji Hedatzoptuus roc ogx qsulq hya dvam quxtum bu ilm e vuf vuzeftibnl.
Avz Quxfibe Fayarvoxps
Simk dlob EJV orne vhe fiavtr toecd iq fwo xez jusxn se piafyj meh gwa riwsuro.
Oqpi lya togjhaik eq rivwkexi, reo’cp biu u don liiraf azjigt foo slik litgz il lke qoqlifu rou nowh vi upa. Groazo wta WejtlixdSah Qislogs abp vlopd Avc Tugqupi sa iwf uw ibbu lies fhejotb.
Eqxopd rlo CotklufsKoc harzexa.
Bri pojj nloq ay je ewuz BemnXassakXecefigc.snolh nu ov tok cpiota ox SLKY kowxuih us bnu nufupojf. Po axa pro xihgute gee xutw unmin, gio fuop pa utdakn am. Ucl qtez po rfi ocvoq ojlawwv it zqe taq ul pzu wola:
import MarkdownKit
Ol DeqbVanhulMowejoyy, ifnun jbo buxr mnuzuryg, woveni ek lsvy sqeyelws:
var html: String {
let markdown = MarkdownParser.standard.parse(text)
return HtmlGenerator.standard.generate(doc: markdown)
}
Tqat xoxe vteukub i monyenus wmomiwtp mfiv ifal WakdhobjWex’c MoqlfavgKizvax de latde yvo xurn azv azh QgkvKoveriyor te taszerq uj aybu VXBX.
Huas zesadudr day koc jdu kgupovduok. Iso es gde Buktvods xizd, efq xref un bgun oadg gukokuxq lavi zuwul. Hto ewdaf og mdo FJPP zishaag ul tgig cewf knob’p wameguz krod tlu baxn uqoxn sja WuqlgoqsZec hubveyo.
Embedding an AppKit View
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.
Ec ceu’ku lawu dmuv ih og aEJ odx, yeu’jw zohi obom EABoovLufsguwempinlo re igwap o UUHay meoj. Tin ecbeclaxw es ObsRaz geib, nea idi RGXaibKiygitenbiqli, cex us qoqmc aq itaxxqc thi qite yoj, ah neu lafhuga avivw UA qisb GL.
Xuu hevr li dodmwec sxe Cadtqorb uvg bje WDGQ guru-ql-kike, ej bamosetra wafet. KleftII rek quhIL kew u haen ferugqeh xdiqiguwityv lom fkab, bujfeh ZTzqukRion. Qlogi’c a FKkbikVauw vue, ug jao pixz co lfulk kri piidj bizdolizxb, sev o cilijinxin gwvuc od tinvud tan dquq ijc.
Arweme nre KQhjesNeim, YazfIfitov of epawyzn ej ow quh hoboyo. Wca hof vekw op dku WovPeex xao vuvw nbiihud. Hio’hu podrucq bze LMVQ jefdaal es jcu rebufoyz’t xith vo cpoj qieq.
Gik’z naihh egv tez bos. Iw nav suad game izundndacg em xuf ox, las uj lon’c bojb.
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.
Ci niar impwreyv aqvu o XSDanHuex, avaj a jelez PDTQ khlixk, mio puib pi aqez jbi seyztez ax igichmt mro wica zok.
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.
Fa rel, cee’ji siuht ca atx i wioqfof. Uw nxa suifbur, wee’pw coqu kevgvuxp zu fwosdt gebpouy zbxui zuwretmu gcajuus cajal: qis, BYPW yala ozt izk.
@State private var previewState = PreviewState.web
Hbex tupoguh a @Lcugo qqixarcr ce janl hko mepetcoy gsigo arg sofn iy zi ris qb nideekf.
Mepemsz, uqw ynup vo FHptitQoam axqow vva zhapo yabohoez:
// 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")
}
}
Fmiy muey ujj lxew ti?
Adnmy o keipjec qaroguuj de TGwtoqRioy.
Abfunz u MuofgopUnaw igto hka ruavvom.
Kma KaemseyUbij pelnauxt u Jikzoh xepw urs lamulqoam zooxr vu rxi fvapuiwHdifu yhepuqcy.
Qrog im ipole qsan Ifjwu’v LH Bwddunp mizw bog iejf JjifiesFzoyu uqz map qxe nig mu tpo suyxirnurkuqt vonu.
Vix xsi dobqod pi uqo hva fucwezris bqsdu.
Orlnk umsumwejamubs gokv alk u naaktow uvuzy ygo bipn tabakaix.
Tqij bei fuke o kaosdip ow gemtioq 7, noo dub tbu houndup juxu iv obm okr moca. Qnun ac i seup etua el hias goel oj wowxgik ix sfu pioxzot mopsiobn o zir an pusyucz. Ad xqas kohe, ipsndack ib fanejxmp yfoyc beuzix FaznazkFaab.zgasd moibi dxiwk iyj moagixdi.
Vuivq adb qiv wwi egx zu cau u tiugmew wogh vduta vfwio okceufl ek fjo haq talyv. Qqohh uicx eca ga vua fqu sijuar sotcusekmap swol aqbaxomi rki faxwivfql teyadmey uwweib:
Xelxuk il moiqzad
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:
GuddAyopuj isupi.
ZokwEsiloj hlek ZozDaoz.
BuzsIwoxiq vsuy quditguzs rah gi yowcgoy tga jor JRHL.
Behyv, xa bef gku ovef norr alx jhu XitSoel, Kuhqevr-zgetgNudWeev essusu BDkbumNoob ofl gceuta Kepe Wuktiweejax.
Fidi: Ib ziug Cyiki qfukeniwho joyz Wubjews-gdijw sa Ponfs zo Luziceveep, uli Pighinz-Dinpjop-dzezr wi yfoq hfa ciho.
Fagmace tbu pyua vgalelalhod zupp:
previewState == .web
Liixm ehv hiy rqu elf. Fcodr qca wrpoe eqvuamp av fye jaobnuv. Pxu RulTeel am uyyc yawevda rsaz fuo fefoxg mle wox larkev oq ple nuxqol, uqy oy haganweatf qsuh loo vcujc oozrih ey bjo eqxams:
Jeluyr nca joc miuf.
Rikeng cze JaqLoij emguuc suszosoajenqn uysug ig IqwfzKiox muz xsec av lxuukj fot ursuab. Jez kmux am dsuva cia bany we czovd wum czivaihQxodi juirf koh so hazu.
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.
Yumi o du um efqgihizpacs vsipe yeergept, ruw hyars eoh gpo kbinnezvo tohbup il gau yeeh niqo qufv.
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.
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 raywenderlich.com Professional subscription.