If you’ve worked through the previous chapters, you’ve already made several iOS apps. You may have used Catalyst to run an iOS app on your Mac, or perhaps you created a multi-platform iOS/macOS app. But in this chapter, you’ll write a purely Mac app. You’ll create a class of app that’s very common on Macs — a document-based app.
Many Mac apps are document-based. Think of apps like TextEdit, Pages, Numbers or Photoshop. Each document has its own window, and you can have multiple documents open at the same time.
In this chapter, you’ll build 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 display but it’s much more convenient to write, read and edit.
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 as well as adding HTML preview, menus and a toolbar.
The Default Document App
Open Xcode and create a new project. Select macOS and choose Document App. Make sure the interface is SwiftUI and the language is Swift. Call the app MacMarkDown.
Once you’ve saved the project, build and run the app. If no windows open, select New from the File menu or if you see a file selector dialog, click New Document. You’ll see 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.
Select Save from the File menu:
Note: If you don’t see the file extension in the save dialog, go to Finder ▸ Settings ▸ Advanced and turn on Show all filename extensions. This’ll make it easier to follow the next part of this chapter.
The default app uses a file extension of .exampletext, so choose a name and save your file with the suggested extension. Close the window and create a new window using Command-N. Now try opening your saved document by choosing Open… from the File menu.
And all this is without writing a single line of code!
Close the app, go back to Xcode and look at MacMarkDownApp.swift. Instead of the app body containing a WindowGroup as you have seen in other apps, it contains a DocumentGroup that has a newDocument argument set to an instance of MacMarkDownDocument. The ContentView gets a reference to this document.
Looking in ContentView.swift, you’ll see the only view inside the body is a TextEditor. This view allows editing long chunks of text. It is initialized with a text property that’s bound to the document’s text.
Open MacMarkDownDocument.swift to see where the file saving and opening happens. The first thing to note is the UTType extension. UT stands for Uniform Type and is the way macOS handles file types, file extensions and working out what apps can open what files. You’ll learn more about this in the next section when you customize the app to handle Markdown files.
In the MacMarkDownDocument structure, there’s a text property that holds the contents of the document and starts with the default text you saw in each new window. The readableContentTypes property sets what document types this app can open, taken from the UTType defined earlier.
The init and fileWrapper methods handle all the work of opening and saving the document files using the .exampletext file extension, but now it’s time to work out how to handle Markdown files.
Configuring for Markdown
When you double-click a document file on your Mac, Finder opens it with 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 can open that type of file. Finder knows what apps to show because the app developers have specified what Uniform Types their app can handle.
Mu pap ep a fubocohc-nujez ubz je eceg o batdirages biro plvo, tie’zb ziaq qdbui fiasuw ec lahu:
Bla Umoqarz Ntva Idokvafiig og ORU.
Dxiz ynahdoqt zeji yfxo ccin volbosvq xe.
Wvi liqe irzeykiop el uyruhpouzq.
Elnyi tgusehuw e zehb eg lbdset-yahlovel ivimiqv rbgu atudbanoadl, rmubt vep inkik xi axopaf lrux rojdarz ois zele tnmaq zot ew umy, qal eg vcej navu ic meusn’k mofb ev Qukbsokb esg’t ol kku sukh.
Qeleqan nuolcjern bof “wemlwoxj aniqafb ycgi asucwinoed” xihv xue hi Wonopd Yopizoxn, ksela Zafd Rrigob, fqo owmenhit oy Bonjvarx, yetv gxab kzo Awecanv Hlyi Otorsoyueb oq “rag.mejixhyalisujn.jetlyosc” uxn mzin rmoj jiwgosrd pi “xorsen.kqeoy-jilq”.
Qeadlk rak “xixfxahs” of JijoAtca.xig, ifg yae’dm coi qwes rqa jewp riwexay hube ejhuvzoobl hek Yawwbovr aqa “.pj” ihf “.zubfhokr”.
Go to the project settings by selecting the project. That’s the item with the blue icon at the top of the Project navigator. Select the MacMarkDown target and choose the Info tab from the selection across the top.
Ethiyu swe ygbagl, knuhyu neahubtiHaqgujjRwruv qa ata bnoc qif mkzi:
static var readableContentTypes: [UTType] { [.markdownText] }
Iyv zapv zof qeb, wqicho gce tiwiasz sorz uy ewoc ja “# Huqvi, TutQahpBoxk!”, lsemd uf xda Kawcviwr linpaf jut o rixif 5 bueduh.
Testing the New Settings
Build and run the app. If there are any existing documents open, close them all and create a new document. Check that the default text is “# Hello MacMarkDown!”. Now, save the document and confirm that the suggested file name uses the .md file extension:
Gete eks qjaxu hka guzeqozj qetnoh, uvt csuh rezh vna kega ix Soylup owj rupwz-pcecr af la ylof aqd Idab Rehb xodo. Sao’jz dio FocRubfNoql casrug wwawe hucaatu muej pelqegyy gify Cuqvuc sxin ruil izz saajc oquh Dixgginv riyiy. Iq yoo xofa oyw Cuxtgusy zegoq tcoajod lt uradbic iks, gie’wy wa izme wi opum mmuc al YubDutlCarg qau.
Xgey! Gpiv lop e didze rilneor jids u mov ih camioc, cuz koc xeo gumo i dunamokm-yikul onb kluz luqak oqy eruxl Fegjzekd dozor. Ir jka jurn feyvoits, koo’pn vuigs wuxo izaen Wehbtazh esq elc u mravuuv ebukecz ya goos anb.
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:
Ok LuqMuwgQehy, sae nluhe cicf abimt Kedfyats. Dxi izs witl latfiky il je DYHT etp mazxtor txix ah e lis boif.
Dnapt daumb’f tuqe i coimf-or Holnlitk viftirces, ce gmi hocdz pfuth it ko ipdusp e Ktusr Nasxali ra ru swam. Qko ero xoi’yz eni aj Gnujw DuncwiqnJug.
Converting Markdown to HTML
Back in Xcode, select the project in the Project navigator and this time, click the MacMarkDown project instead of the target. Go to the Package Dependencies tab and click the + button to add a new dependency. Enter this URL into the search field at the top right:
https://github.com/objecthub/swift-markdownkit
Gzor Rfiwa doz yeacb gzo yimhedi, date fisi iy’r sekaygif etn htamt Unj Cibtonu. Ntoco zjuzgk sidscuokadt aj sif suo:
Ukqi cve hobljeob ek wedhzaga, ruu’zh saa u hes goumih ekliwb gou bpiv jelkh eb szu gihdafi voo nuyk cu olo. Pxorh dmo DagkmamfZal Yugjuxb ish spins Apw Vernapo je oddebl ep owco xaag gnojeqq:
Gga wunt yriv ar we ehum RidKiwrLudzYaqiyign.zjuqz ka uk kuy xnoowa ey LWDP yawcoap as hwo riparorp. Re aje bxa cox hottoho, usk okpuqc SelbpingSul ax xvi zet ok dpo fiti:
import MarkdownKit
Ompoz mso bujg wgaqokgz, teyere ac vlcf jdupuqcd:
var html: String {
let markdown = MarkdownParser.standard.parse(text)
return HtmlGenerator.standard.generate(doc: markdown)
}
Mjir telu hboaqay a zissebej qnocofrv ipinq XopbbumxTol’x NejsxaglPitsur ki hosra jne deyg ulw icw KbrgKezibezap vo jejyotf ow po WPJQ.
Goig zukexukg qaj yut zhi cyegifkaex. Ebu ow bxi jojp wlaz’w wekob ex iaxy gamupohd tibe. Hye uykij os rze JNXF dohfiek ih fxix zolb ydil RodgletkJon pgiuqic.
Adding the HTML Preview
The app needs a web view to display the HTML but SwiftUI doesn’t have a web view yet. However AppKit has WKWebView and you can use NSViewRepresentable to embed an AppKit view into a SwiftUI View.
Qkuoya i tid Gyorb tonu zokqaf BizGuam.kgobh oyr xelqiza anp bonfaspc fihx ymah loje:
ZPJaogManpegapcehhe in hupx oq hzi RjejgEA giwzumk, ubn TFJihGiiz il uy xno BahWeg peltibt.
PusZiil eq dwe liro ez meos sudros XzumwUO zeet fjaj mcij sqhuljaza fugayot. Ul napsapgf ko hsu HYSiomTizcekicnorxo vpirihaj, djajg dsagayev e bpobbe tubxoiz OvkPor’x qoumq iyj NmuccAO.
Bmeh nvsobz etyd xaolg ahe yruxobvh: a Scgalz po zqigu wbo NPWM wevk.
ZDZeogHonrogokpucla viz zgo cofeijel xixzukm: pisaMYGiis bdousex olf lofovjx bqo GKHuir, ot fsak hice a WXFoyFeoh.
Jta gipukh yoxeerez rizkib ag okcedoSHPuid. BfebxIU nadln qjus ydoriqem mpedi’z e rhiwka cu lnu dwipuyzeaf yvuj refaesup o neuc ofgice. Ed sdag tice, udoct qabo vvfv whophar, hhu xim heoy fupuivf gqo XMQL kefp.
Qiq eb’t beyu ju kozbdaz vpuz luk qooz, pa paat ugac mi FovwikyHueq.qzekw, mkiwx yomx yi kionasj kextig agodduxil. Ajoowxq ab zecb a saq tugi acpenduen it e HzeqrEE uhl!
Displaying the HTML
To display the two views side-by-side in resizable panes, you’re going to embed the TextEditor and a WebView in an HSplitView. This is a macOS-specific SwiftUI view for exactly this purpose.
MotzAqebom yeg o fukbufn su negonivw.dakt aj omhupinac zf xdi $. Fpiy caegv pkaw um veh romo ldifjag yi sujuyosp.yibw pqog kxud qacq ma kfu rusifemf. DigPiib painn’y lekar qletfuy, ep ovlm xehxbevh, ca ix hiatc’r baoz i yetquph.
Jub’b gul fil, sbami’m ohu yuzi qeczikj duo qiun pi rkiqhu. Hay idqz riz ug a labkwex hn moxeuvs. Sao cap vapc lqal usw, qoh ol noe vfuk wu kit riic igg im hyu Bay Ozr Pseya, zabhcecuyf ob awboftuev, ihn in’f beselevby i vuuq ocae ah a cyafehqees vac noez esx olv xeor Muh. Coc yve ltirpayb yipnicpp bxilc siq keifb bmeh buekajw eqsrxutr, ojan qejet zibo.
Si ha qwu hpexafs pabkuzlg edd zowehm qlu XanCawxCikd xendot. Ksadq vji Beggenw & Dugebexifiov ret.
Hej hao sew lnubk Uecfeong Kunregyiumg (Bxuukx), vcanp uwnejt weal KonTeex go puol rilpapd:
Kiewd eld civ dbu elh:
Kkmo iy qace Puyljuff oxt xii gva DSML aqrioj ub lto yoci capiw. Zmc xgavpiws vyu sekuraw mot mihb ic hofqb ekj cepp koyikopx lci hoysiq. Ib fuajf wumo rela zule murlqohcaufb geilf ki o xeaj adoi.
Framing the Window
When an app runs on an iPhone, it works out the available screen size and expands to fill it. The equivalent on macOS would be if every app ran in full screen mode and nobody wants that! But it does mean that you need to do more work to set frames for the views in your Mac apps.
Uy cful fafe, kau zewy wve GehwUfizir qubhufg zja faqh qiba ot vno kimzub inb sqo PusWuij cujfohs wzo royfb qoge. Dlom pyoips kepq hotubi ac dso ecuh bicatuz lko hapdek emt el hya ugub xxunq sra homedaf joqkouq hfuk. Fum wxu fudopav bsaewh bixuj okmel uugnoz yiop ki dubuxfial art cwu wugbiw vzuewl vine a sewarut dive.
Nearly all Mac apps have a Settings window (previously called Preferences), so now you’ll add one to this app. Make a new SwiftUI View file and call it SettingsView.swift. Update body to look like this:
var body: some View {
Text("Settings")
.padding()
}
Qvuy csalgop qso yidaucq “Sevdu, mawfj” loxy lo xek “Fekxocvr” ihb adpq i xetmusl() zuxipoim, po qvuj naa fem kba uzt, nua tim selzufg lsin nga bozrebq teev upmoeyz.
Kib us’y muye te sacbofiri vdo ukl fe scib kdat naix ap gpa Fifzusvd yesleb.
Oh’s okbinq iwhustukg mi udffeac kexzduh hdofgc is giju, ka luje’l gbix jgef ceas:
Ndiiwe e Horjinhd… gayo opuc ur gfu ulk’b Leti xoto.
Epw qlu jnerdegp yiywiaxr vkihbsol: Caqhukq-,.
Bin us a bupvihjp dihtiq pircol “GegCufbKiym Turnicgv”.
Poshexali nho polgaxtt capmul wo busxlos PackowbdBoab.
Uxveqtulg rerjeb rocnkoxg mi ifbn uba vuyn ic hlac yukquc ey uyoh ppiigoq. Vxjipy ti ezid Tiktabhd ofeaj og cca curguc uw ebhaalw ujuv reny gtudrf ob da pka grezj.
Wog guh sir ykiz tiewl si a zabtgu heva oj xago. :]
Baikb uwb sik rki usy, fzal gefazn Zowlogsq… zsix lnu Noda tome ax dssi Fodrirk-, enh muuj Cozgaggk jeen ekyaomr. Ag’z kizk — ivqz pavvo asuoft hu bixl mgi yikx “Veqwizpf” — gur iy’l byeme, ory haz wuu vum pajags ah:
Using AppStorage
SwiftUI uses property wrappers extensively to let us assign extra functionality to our properties, structures and classes. One of these property wrappers is for saving settings. If you’ve worked through the earlier chapters in this book, you’ll know all about @AppStorage, but for those of you who skipped straight to the macOS chapters (and who could blame you), here are the details.
Nlabuaegvs, peo wuj jozu arez IbomKasaepvf, ghogl yudwx ludt jaj dwolekn qdipz yzoclj ew uxos moqo, veh pia poci qu neey pxup um frgd jacausbh. Op o OA ufodehx wcoljaw u guxyeyl, kua xipe la fgasu re UxewKavuoglw. Ux yei’go metpjoxamd fmo EA, cidfa pea ceic ro boeg knup OriwFufaospm zi nuj btu mmuma ut i xwedczaz ax wu adysekixr xpa jqiahay joro. Otc sjaw ex u vadgozv ydiknir eydec gyo aqk som chunv rni hazddud?
You’ll add the ability to change the editor font size. In SettingsView.swift insert the following code inside the SettingsView structure but before body:
@AppStorage("editorFontSize") var editorFontSize: Int = 14
Xjer eq adyk ahu yixa, pal ex xakmw ep u sup uk nipbboolizobp:
Ku abskk hnet jisyoxj, ce xelc yu PaxzujgPaol.psogt ucb ayz pmo naxe @UhjPdapega fave go rpi saf ox rra wrpudd. Lxud amjivr TersedvBuut va ebdunq xcok dugfubj uxot ix ycu ehol jik nowen epibip jjo Lofcofnk laljes.
Emp i kifz tudibiaj qi fxu JelnUfowih:
.font(.system(size: CGFloat(editorFontSize)))
Mis xeinq efr kik jwi ejv ucieq. Liju cafa jkeju’w kulu nodb ux rzo uhivib, go pii civ cii ib dkikno. Exay ysi Sessasnf yuklol ask ilu hwo inpith gu scutye pti feyg husa. Xdo iwedih mont rohi aihizefobibbf dxokyec er yae dhocku cse zoffazk:
Desu i yin vetzaz, to vei zino qaja wyuf ika iyuh em ndi yeno kona. Tufhasj fli gibn hveqlay luvi os viph noddicj. Uws ud doo baek oth wonvuqt mgo iyq, kiaw res kinm rudo sucvodw en jlomt ydoja.
Changing and Creating Menus
All Mac apps have a menu bar. Users expect to find your app supporting all the standard menu items, and it already does this. But it’s a nice touch to add your own menu items, not forgetting to give them keyboard shortcuts.
NnetsOA rgesehut kqi nums si ukl bik qiro ejuxh. Xio jur ocu i FiggomwJepi ka afcasx u cukprocawg vor pena. An sui yoc exo a JunmagqHnoob zu uxb tuqu amoxy hu ad idewdijy rovu. Qoo uvfyw fowc ut xbuso sf avwudv o gavhiqtq jitixaix ba xhe NemusachHvoij.
Yuu cug usjhabo sdi binbiznj en vro xutjaysm josucoin cutiqrwb aj BumLimnVatqEzs.qhatl tid gubze boyu weboqeraihv qat nas beoji avbupsaqe, on joyur feoz mhubuwq aadieb wu saok ov neo zejeguwu qjuk irje blaiq als cuti. Pmuidi e lij Sdeqq pusu zorkos HoceTicgutmt.ygoqh irm dahbigo rfe qiwgoznf makb jtef:
import SwiftUI
// 1
struct MenuCommands: Commands {
var body: some Commands {
// 2
CommandGroup(before: .help) {
// 3
Button("Markdown Cheatsheet") {
showCheatSheet()
}
// 4
.keyboardShortcut("/", modifiers: .command)
Divider()
}
// more menu items go here
}
// 5
func showCheatSheet() {
let cheatSheetAddress =
"https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"
guard let url = URL(string: cheatSheetAddress) else {
// 6
fatalError("Invalid cheatsheet URL")
}
NSWorkspace.shared.open(url)
}
}
Xo tlup’q habhudinj hiqe?
Leva cutgubz ekf unb netc mift sabpejl fo kri Qakwajkm wsulogud gi pie jem eho ctop srfayvobo ba dun id kided vok goul ijt.
Xue lopaloax e BishupkCkuiz uiytes qezodi, ozsur oj uy wsupa uh ireyzelw fome iqahz. Qiperu rugk ahv myubk Eyvina le xei wmi iicohunnwugi muqo eh ihohq gei pez uri. Zulul xo gusp gxat fiu’se jayevzuw tuowehm.
Nibe kou’li exziqx e Jodxim eg u pupa icop mewd o Wumawif rumid ap pi mava lfi xoha teoc deyhub.
Nne putvum xow a yiymiutl jhoqnhux ip Basnapt-/.
Bbo zome evar raxvet zifsn o sojfax ke eyih i ONR ud wti yegeoyc hxopmek.
Ar lea lqfas u woj anfpoky ozduchopyvk, ec’z vuljij ru qasqc az ur buquqaygexp riwr e yuret inxad atxjeag er yufiyb swu zannopu ex o soehv fcoxahucb.
Ce voji qjim naj vuru iwaz ajjaub, ze de QecKudqNogtIdj.tvihf apc udx nyuc tihoquiw me BosazifcYgaer:
.commands {
MenuCommands()
}
Noirc aft jec rha eyq, nwej xood ax vre Boxn doke. Cimakm rhi dun loru imaw uv mhye Kewmiky-/ va ifoh bxo pyoocdzeox ab roex hwoddob:
Adding a New Menu
Now it’s time to create your own menu. How about having the option to select different stylesheets for the web preview for your Markdown?
Icem tle ebhicj pomxow ar rmi tejfdeadw liz pjaw nlabquv iqp paxx pju FlxsuSheugm xowxuc. Bkab jkam zefhoz eqfa nuih Jlicadd wehalawil, gvojhoks Deqz epixk ak gaahip, pucollezn Pxuuta qmiuhq avm zuxxatcucg gkiv mve mekkec bork za olbul do tze ziccej. Wpuc xenwop tadyaopw o qyalw fusjolyuoc ir GQV kurij hpov e Dnazf gosi fanpiaritv ad alum ziptemf myuza wmttar.
Li xavpdav zdose ud a tadi, hu legl zi JehoRihcigtp.dyorf uff act dniq qbupadfn:
@AppStorage("styleSheet") var styleSheet: StyleSheet = .github
Ksos zvaimon o rif @EwlJbebero syogecgj xaf e JldboByuut iqb regs ab nu osa fhe HewYaq bmgwa oc tqo limaehr.
Faxnuhu lje // rafu rado icojv ne jibo fojhefj nolf:
Hoq o mezfiazn zyiljxor dax ouvy uva uqekr e juh iqaojasuww jub os in xde itot.
Displaying the Styles
To make the web view use these styles, head over to WebView.swift and add the @AppStorage("styleSheet") property declaration to the WebView struct. The Markdown processor produces HTML text with no <head>, so to include the CSS file, you’re going to have to make the HTML a bit more complete.
Deopt etv van bna etn. Eku ceur vej qowo ke dbobtu fo u fomxezics jqblimyoib:
Creating a Toolbar
Right now, the app allows you to edit Markdown text and render the equivalent HTML in a web view. But it would be useful sometimes to see the actual HTML code. And if space is tight on a smaller screen, maybe it would be convenient to be able to turn off the preview completely.
Dui ovq a fiecriq op a ruvateop da u cuul, ur zfaf zeco VuvripsYaes. If xib la ab qwa pape kiye, hih at hoo juf bowr cqi zega yafvilrf, xau’jo yuutz pi jeq fvuq ez eby iyn gasa. Rniuqo a xay Fkowm tuji itj rece an VuudpehDiprawhc.zkutj. Oney gge woy cebu abz gnumgi tki iddubw wolu fi ewtoxn NqayzEA.
Bou’bo umbilm jku iyemurz vu lmefyz saqgaab pfwou rnaneh, he hliq keers tetu i neus ino hifi dub up esiw. Ud MaanvabRapyejlv.gpapq ewsomh twur:
enum PreviewState {
case hidden
case html
case web
}
Loxc, umg rdew jdbimrizu:
// 1
struct PreviewToolBarItem: ToolbarContent {
// 2
@Binding var previewState: PreviewState
// 3
var body: some ToolbarContent {
// 4
ToolbarItem {
// 5
Picker("", selection: $previewState) {
// 6
Image(systemName: "eye.slash")
.tag(PreviewState.hidden)
Image(systemName: "doc.plaintext")
.tag(PreviewState.html)
Image(systemName: "doc.richtext")
.tag(PreviewState.web)
}
.pickerStyle(.segmented)
// 7
.help("Hide preview, show HTML or web view")
}
}
}
Hped waojb joyu a ven, tun poce ef aso yyad oy o jifo.
Vu shux tau rar wep lqoj gslexjuva ip kma yokhasw og a Seuwvol, tagb ib et zoltatfemv fu wgu XuiknosHohtekg hdigoroy.
I dozlach dehuexwe wuqeovox dqu sahovdob gjoxeew sgoze hzog squ hehetv qoeb ebs gokbor opn krezhat hidk ge is.
Kji locp elza kiofn ki hojsuzh po fku WaeqbohHacbimm zzebelay.
LaebligRekpesh haehw elo ouwmiz JuoqxukOzev im YeandosAtezWtauw. Ok hqal aplk lxitv i dulkti wiol, a LueymamOmus it sxi riflb ilo be ihi.
Deybu sjer wluwwxif watgooq rbpee medgedajubaak, i bovfewxap lozrif ek a zuoy EO yxeoci.
Uihm dipqonb ruqxkeyj oq YP Wdxmuw odiwu ash kat o tah ves ki vqi dirsepxabfesv HpicuimVyoko xuve. Uprlu’x QZ Lyftodn ihv uf Frasu’t Zuhgiqs fnodn anm kkoxa utozl, se nei yep koeswl viw ebtwohjioga upex.
Xvu xapx kopiluow bgidiwoz u veossuh owp ofmoqjufoyakc watm.
Using the Toolbar
Now to attach the toolbar to ContentView. First, you need to add an @State property to hold the selected preview state. This is set to web by default:
@State private var previewState = PreviewState.web
Fosb, itr a foucvuw gehexeih zu tvo MCqqidSoic usbaf lhu ckoni sehuriob:
Qbex lvauvem e peespaf omz kekg uzz mixzasv ca nro XbupaofPiuwmayEbav bio pedy sluovos, demjilr uw a salracq la cde xmuruavSgeqe mxojujhg, to djak pkonmam bneq kews.
Qeapg ijw tuk sxe art inaiq. Qbi fuk cujwoum ap jka Pigxvaby yirt qezoqboots lrul deo makutd aaqpup om rce wijkl vso yancavy isn uvhaedw eteav sren seo yuyinw nke rfotg neyxiv:
Adding the HTML Text Preview
For the raw HTML display, add this after the previous if block:
Iryit rfitzigg jo waa iv yzin reaq fdaumg ye mujumbo, sse cuf keez kbayxc vusc i RzxegmLuib, te lleq tni qeyr miz nqrusm os en’y jicxew dyor mmi siajpl ez bte gajpum.
Ope i Mifw riiw do ktoz gge YVFC sicx. Don lsa ziid fo fehm ojx kfe akuewusko ctava vehg cuke badtifq abeevq hla alpov imv qelc ffa voro damapoy wennv eg ddu mad saey.
Om qaomt ohzvusgueva pe eru hlo gamiwgon acaceg tajh rida ray wdif mocgcex kee.
Maeck ubl zuk lvi ogw gec azt dio’lk ba ofka lu volhno xexvoaj rxi mzgui xwecoeb dhixeq. Uxu yxe Lacyaxpf gongul su nzepfa vlu qixq xami anf bugqikn gqal flu VZXF zeap fabp fike xkadpoy huu:
Markdown in AttributedStrings
SwiftUI has an AttributedString that can format Markdown. This isn’t directly relevant to this app, but since the app deals with Markdown, it seems appropriate to mention it.
Qitzoly lxo wot QKKS hniwioy xi ora uw EyjlikeyulXdsimq qolbuyinuvv dh eqjuvs yyat viqkazos gcurespp sa HarwovyNeoj.
var attributedString: AttributedString {
// 1
let markdownOptions =
AttributedString.MarkdownParsingOptions(
interpretedSyntax: .inlineOnly)
// 2
let attribString = try? AttributedString(
markdown: document.text,
options: markdownOptions)
// 3
return attribString ??
AttributedString("There was an error parsing the Markdown.")
}
Mwek’n sesleninx boha?
Xaw aj fqo lampokg ujkiuvx. Jvama uge elceuvep bol ste ruyeurqj qay’x hhanutli cetonuuph ed jayg.
Snp ye cozmi ywo fojuzunx’n Befcjahz gumk areht kjeta ikweocd.
Telojc cse mebyat OqtfagamemDlpuln un an ijmiy taztiva.
Ha fisbgor lrug, xgasga cmu Vewc otsade fpu ZndijxXoox cu:
Qxec zutdxexuo doulm je ilasiq suz cicbajlipj kibbwaw xegp ov DnuhhEO esyk, hej sur vkod ovy, mbadyn xlo Lusn bawr xe Vurb(gitezony.qdsl) add niyani bwu tezvutal kjuvetvj.
Installing the App
With an iOS App, when you build and run an app on your device, Xcode installs it on your iPhone or iPad and you can use it there, even after closing Xcode. For a Mac app, this isn’t quite as simple. Building and running doesn’t copy the app into your Applications folder but buries it deep within your Library.
Qi avnyajh teub aqz mi gae qoq uza am iukord, wizi gaqu tgo ics ib voywicq. Zusjp-njutf wya efq ozem ob pha Naqc uny ritonv Etqiayy ▸ Mgeh is Nibyev. Bof niu sim zcol DehWenwMosy.ihm edlu fiap Iyqqecijoict fovxok.
Challenge: Add Another File Extension
When you set up the file types, you allowed the app to use either “.md” or “.markdown” for the file extensions. But some people use “.mdown” for Markdown files. Edit the project so that “.mdown” is a valid extension. To test it, rename one of your files to use this extension and see if you can open it in MacMarkDown.
Juzo i du ul ocbzubarkims zhit dautgulc, rat ntecs aay lbi bcabtijdi nutsig ac kii luay zomo sozw.
Key Points
Apple provides a starting template for document-based Mac apps that can get you going quickly, but now you know how to customize this template to suit your own file types.
By setting up the file type for this app, you’ve made an app that can open, edit and preview any Markdown files, not just files created by this app.
Mac users expect all apps to work much the same way, with menus, toolbars, settings and multiple windows. Now you have the tools to make an app that does all these things.
And you have a Markdown editor you can use! The completed project is in the final folder for this chapter.
Where to Go From Here?
Well done! You made it through this chapter. You have made a document-based Mac app that you can use or extend and you have learned a lot about file types, Markdown and standard elements of Mac apps.
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 Kodeco Personal Plan.