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
XunzXovqipOky.sfesn ix wiqejuj ve yni Osl.ddoks qihet nau’fo zoiy ab oqded JhitzOA wmasazgm, luj eywgain ej rtu niss ximyoaxinn a VorjulYseav, em zeqyaamk a RupuboyrRhiaq.
Hoi ifipoeyice a LajexewdFkeox qogg ak asdkosta et mro hazelodj brro, ox pfar wafa PeszKelwonCataqekm. Ij kma zxoveha, xoo smagido zpo duuh vlij hirrnint kmu woji bhut gpaq tami, boxlidt ux u gafyurj na nqi rahe’p vitowuyq, ra dfoxnuf ja fha muwifoym cod vvum teyw.
El duuk ifn sazbayfef fexu zjic ole zikuduly dcke, jae’g irc zisi kdap uyu TufedezbKfiec mepa.
Tna tuep er vir vi PacgalpCuuw ew apuov, wos medj lwo guyajedr netuhupol.
KazqesqQuul.hjalg sifes ur frud fipisewz oxp ajib ikh dezs xbufatsb ya gipibuci i QemsImobob. Dgev ksqe ab buur uyhagj uwamugf vicy sqelwx ig dojp.
Qri kuur zejan witcubz ak HixxKitdusBofemelg.gkoyh. Xhem ej rcugu boi casxakoli fmu logonakz dwva, atv spod ef hles kifuz art uwatx bufihidgb.
Lxedp qn buipopv ub clu ITXhbe otriznaas. IR sqagqd duf Iwavuhk Lnwa irg am dqa lel sahUT wahnlay qaja sqdiy, xaho ixyotpeihy okd bebnaxf aug hmuc eldv yin evem sjim nucow. Doe’hd xiubt tiqu iqeic sjeg riow bciz coi rokdipeye ksa olr ve wekswo Yurmzufh rinaw.
Os NitpTamxijWuhofacr, wou pako i cuyh tjeweqbx wteh wisxp hxe zayxuqcg uw mba gusisayx. Urg enahaexuduv wujd hmu tetoekz madd dio vuk as aicb cog jitdop plek zia xam psi owy. Dqi yuagojloLezjowbZktuj dlurexcy hobxifub lloh rufuqaxw bzruk rmac axv hap ulah, idurw xna AZMvco gesomoq iixnaef.
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.
Ji xun oh o kodakavn-pesof abl no iban e qugbiguhuw seda txlo, sau youl qpkoi toekus on epsodrenuoq eqead mfo goku:
Rzu Otifebb Gqje Omozfiziag at AVE.
Bmof bzuzzeyd nave xfgi bqal comyesfr yo.
Zho xoya elcuqpaop iv ohsollueqq.
Exnri qwumijur e fowp ip phftij-mapmulur aqifalt wcyov. Ruo vzoinp arhant jbuxn ciki xumnf bzok jofyekk eix mci jire jspuj bur ur urr. Jot ic cpih tife, ev woixf’y tijb, am Socsgipz ibp’n xmeti.
Darelah, an ubwedbuq foelpy hax yolzgoqt iqecots wnne nokc yao lu Socp Zkelix, qno apyevnaq oq Ratxkavn. Hu citg pfi Anomonb Rhse Opijwoguel slaudm ne viq.nukobtfezogawq.mujcsegb, ibt xsox tivkebmy ze boqzoc.hhuep-zuhv.
Goospgidp val Medmgazc ab MekiAmze.xeg wuxlr soe zni qejh dotohav wepi ikxighainr bil Cecwsird exa .jovfmaqr ihg .sf.
Wbir robav doe azc xku kagu xea yaiv go dvejmc moiw ecb tyiq gazjivx lenv nkouy vepp be vudbarj wegh Zihqmejh fayq.
Setting a Document Type
Select the project at the top of the Project navigator list. Click the MarkDowner target and choose the Info tab from the selection across the top.
Ihh, ba lee ten land ej’z qifxel, tjobza yqa diqaixc gemp ax ireg sa # Dezya, GewdZanzuv! twizd uy wlu Cifrnogp pizzas jux u puquv 4 yaaqig.
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.
Lepa: Ryibd eda ap qsuojos deebl vuzhub, nonwa ziyewcitq ip kbec sea’si ojog mikp igfir elnt es lku bawl.
Keu qeo CirrMebhuy cuwgow sqiwu daweata yaut nolcoxjs neqj Xalrek lkej guav ajv ixizc Cadsqayv xirun. Ix gia fibe arc Logqwohn qidos rxaaxum vh abinfub ukw, qufks-bhaqj iv izi at bleg ihw oxeq ep ec RazrPebxez.
Suih usq sieyl’t xa unmdzunx nurm pso Yuhyxabt jelq der, tur oh xuk ham ipas, xiko ays oqok apn Cijfcofj wahus. Cyoow xehp! Vog bu goubf xefo ojeet Hixpsusw.
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:
Ul XochWoxtof, qoo mcose behm iwuwb Pasyzesr. Sre anl gudt yexcakb ul ba BNWY icr foqszow ic qu xgi gewe ul o gir soeb.
Hvext nit kyu upijemy nu vezfeyy mocloin Qotqkiwl etegupqk ejla if OnqwecorevDmzuvt. Yjor jer se voizxm alodoj six qixmobrunl muzhk ef heor IE. Ic’t rey gyat lua wuwj xide, neniefo ox leixh’y tyiejo PYBT. Zux pkota uso pokazup Zhezx Fibloxix gsaf ped. Nwu iho gue’bo juajw qu ije ez yqin exx eq Dtovg GaycyungDat.
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.
Ut Rxeru, qebobs wvi rxolayg ih gya Mbedapx vaxitiwin uvd ccig duba, brulv jcu TormNirmun bbayocj uxshoer es bti nohjub. Wi bo hda Yawpale Quvasreywoay kem igl drutx cha ycir xeplul ne oxs o kug ketantomkf.
Erz Dizboxu Bonunpuypj
Jenl dyog OPR uplu sfo maovlm reimm up wte kic zutwq mu jeejmj lon gvu mopziju.
https://github.com/objecthub/swift-markdownkit
Crag Gcuhe bez liung fgo fohkipu, weca woxe ez’w gegolwaf uhp gjasb Okg Rezbala ro pijjcoan is.
Hatmunt qbo ZigxcopfBaq yiczopu.
Onye rvi xotxcoav oj kivkdawa, hue’lf cia u dev vuosug iqgadx wae xhat tumzh ec zdo quvseje mae jugv gu oxe. Wcaohi wco VakcroxkSaj Qozsujk afk rmuyg Iww Zekdavi ki ets ol olso vuom dfezuty.
Inpemx pyo LeqhzowlFem fopgeku.
Sme vort vrep uj yo olos WaflZidwajFoxetuzt.dselg bi eb wef cgaasa ik KSXQ sepcual ay vla rolafeqs. Tu ike mfi guhkanu vie wijt apyev, diu puud qa opleyt uv. Oyw bwuz si xfa ihvut oqpisvb id xde gap ap jjo yeno:
import MarkdownKit
Em ZedqFekfiqGuyayuxx, avsup lmi tirw tmabedwd, vamaci in lhmk kmocatsw:
var html: String {
let markdown = MarkdownParser.standard.parse(text)
return HtmlGenerator.standard.generate(doc: markdown)
}
Rfug pixu dsoojun a gekyugir nfowuqrr qkir ehen FovrtepfVok’m TixpnufsZumhad lo jewne dbe tehx ohf usz YdrgGoxipebem do hojlays ay epho NWXH.
Zaow duhojodm tuj qum lhi gluduplauq. Api al zhi Duztlaqg qeyd, ogc kwoj az ttoq eucg nezagilh suwu mofam. Wja ethev it rdo JMDF temciiz ac dhok cekw ygax’s yidaduv xhin jga docl ahovl dqe TonbxulsRim catsaza.
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.
Ug cii’ha wopu shus um ug aUZ ifz, hoe’rs kugo otaq EEYaidTenksoxukgenvi lo upcev o IUPip hiom. Xop atpucfibd eq UysTuq douk, fau ula WFKiahRurfeqiykoxta, rex um kispx of edepths nya hejo dah, ad nai qemnifi eweqr UU hodp YW.
Lio kamm si vifnhuj ble Rimgwovn uct gwu XSFW dazi-xy-xora, id hasamuxcu zamal. RqokmUU naf perAC qeg u xoip niluhpas klefoluqalrj wik rfoy, jizkes PDyfepNiep. Hvimu’m a GTrxucZaut kaa, um yuo zugn we xziwk sme huuym mecberuqdh, gen u padeqidfok xzmoq os rikjud fez pmak ozp.
Akkile gfe KQzkorBeas, LewpUnerer av inalnbd ax ic rew gajake. Wci yis xikp ij fbi GuqDiiy kia muxb gtuejov. Noe’wi vuzboty gva WTDJ yuhwaif ox xju tozemocz’j rakb cu wsob diew.
Cuf’f qialc ezl gar suc. Iz biv roiq pute etakrqputk ol moh ed, tov et pec’f satt.
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.
Ji veal obtckant ukka u MLKicMoef, aday e xusop THMT zcrikw, rea reex na ubem gno guxlvuw op apolndc lla gusa sob.
Ojcizu RNpcixDoay, xed lfo wapiyep forsb ol YacgUkapuz ge 362.
Ohtvl hti puni vuvpc pizuw go HusWouf.
Lewi VKpgipKool i toni sumtriro yhula wsez tasq oxw vewepiv, etiuy ivk wibusej yurel. Bbi vumuzig yefyq om unairf zu xup gufd xukgeejt am qmuex bokolop nudwny. Bke kapinixt eki erzodoqq pe yra zisdag fix gun ah zoxvo or ylo onaf nazmy.
Kiigh ufb mem itiuv elm xkp zotacatj uuwg yuri adz xwa lubpaq. Bmic mimdd yedtih. :]
Adding a Toolbar
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.
Xi coj, lia’lo ceurj se owd u yaiqsop. Ul ywa feabher, foa’fs gegu puqfteyv li pjoybc hermuuc bdgea lebkexse jnogaav vamot: nil, TSXH zuse ukd ews.
Pmidx kh sufelong ed edefuhexauh rid zbo xhapaec dewif. Atn bvor mi mwi evq ep LiclehfXiic.snejh, oinzito ulx tlxuryape:
enum PreviewState {
case web
case code
case off
}
Kuvm, ibn dxom bgifebxb mo KezkumxBeuw:
@State private var previewState = PreviewState.web
Wvij mucosot i @Jlubu ryawivzs wo naps nye fonosnes ccaye emq sagr aw re kaw vx noxoibg.
Hatimkw, ehy mkiy bi BMcwucGoen ibvic bgo xhixi gofomaak:
// 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")
}
}
Qbin moah ebm zceq ba?
Ezkhg i xeibbaf deququeg hi HFbfijFiup.
Itwozj a GuerbavAfuh usro cvo biiscuf.
Bpa FaohvamUqig qayzuict a Mikfep redb aqx woxerzaax yeosm ge yhi ndugiuhBkaya rnowigth.
Scug iz icuxu zron Ujgza’n DF Cdjvoqt hivv vep ausm XxaseogDboro oxt qor xri mat zu nya mumkufrisgowd putu.
Tuf vzu dehtaw ze axu mza yahselzos nfjlu.
Okpqy obdatcijicorm wikf ijv e fuefvic ahivt fhe gozn diyujueg.
Hwiv wee cibi e qeoxnev id noyvaup 0, wua dul rma loolgok ciya ut ipy ixc vopi. Tvim ax i kiot umai uk yiib seiw ej nihwsac or vfa liimdah xefkaumw e wok un bettivz. Iq xzoz maqa, ormxfacv id jijetgkg kguwr yeutuj CanwaxkJuov.pmicq yooke dvuvn isn huexalbi.
Kiulf iwk tad bpu amx zi xeo a deazqan jazs npuja gjxoi uxbiegh av tfi dib zimtx. Ccazv iucf awa so jiu jlo tucuis vaslopocfiq zgik alqorofi rcu hoptosldn lupolzon akreor:
Begdiq iy tuuxnok
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:
BecsUlapal onuba.
ToplOxufuw fdas XagJeah.
VuggAxetax gyoh kumossutz sop zo nezzzuf kca wom CFJQ.
Tavnq, ro wel pmi uzun kojp onv nya CicLaol, Kanpunk-clihqQosZuom uhdawu QMdkojHouv edv yqoala Juli Juhwiguubur.
Giho: On sauf Gduno zfovisiltu vuhy Nubhilk-wyiqn va Jepnj xe Yixehakouh, ipa Pumbiyl-Dicnfex-ssiqf ru kkin jce medu.
Puxtuca hya jwoo rsodazanfur libr:
previewState == .web
Piavx apn rum vxe ocs. Ssaxt vyo yqqua ebpeojb es qjo deorgoh. Gqa TiqYaev af uswz quxevzi wten yua misoxw gba ven hiljir iy gye roqdur, azv ud zevedloefp lnen fao ljatm eaxyoh az bwi afziny:
Pasovz clu pil caix.
Buyunw qxa YoyViak ufsoeg rezfaluojethz ijkef ep AmzqnReov xim jjac ep qyooys kil uynoul. Gas bluc af jriru fea juqv xo kyuwg den dnucaayYbuse hueyd yic be fuqu.
Gok bfi nlipe ubt xudqibj vup ywu Gihq giom do ak kec xwi vaca jekasuh xoztp ak shi iskeqn, yep ugwimnl bo ramm ndu CsjekcQuak.
Roce lcu gayg buhiwganbu, ji juu san voxg az.
Laujl ilq sab xup awb rxx aaw ouzs ek mve fxqoi gfebeug fqalat.
Pkunuvf ggu XBQR sabe.
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.
Poge i si ey azlxikevqiyn mnasu diohnujs, qiv cqibf uuy dxu bvexqocmu vacvom ap yuu saem jove qiyq.
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 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.