If you have worked through the previous chapters, you will have made several iOS apps. You may have used Catalyst to run an iOS app on your Mac, or you may have created a multi-platform iOS/macOS app. But in this chapter, you are going to write a purely Mac app. The app you will create is going to be the class of app that is very common on Macs - 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.
In this chapter, you are going to 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 displaying but is much more convenient to write and edit than HTML.
You will create a document-based app from the Xcode template and see how much functionality that provides for free. Then you will go on to customize the file type for saving and opening as well as adding the HTML preview, a toolbar and menus.
The default document app
Open up Xcode and create a new project. Select macOS and choose Document App. Make sure that the interface is SwiftUI, the life cycle is SwiftUI App and the language is Swift. Call the app MacMarkDown.
Once you have saved the project, build and run the app.
The app will open with 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 do not 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 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 take a look at MacMarkDownApp.swift. Instead of the app body containing a WindowGroup, it contains a DocumentGroup which has a newDocument parameter that is set to an instance of MacMarkDownDocument. The ContentView is passed a reference to this document.
If you look in ContentView.swift you will see that the only view inside the body is a TextEditor. This view is new in macOS 11 and iOS 14 and allows editing long chunks of text. It has a text property which is 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 will learn more about this in the next section when you customize the app to handle Markdown files.
In the MacMarkDownDocument struct, there is a text property that holds the contents of the document and is initialized with the default text you saw in each new window when you ran the app. 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.
Setting up the app for Markdown
When you double-click a document file on your Mac, Finder will open it with the default application: TextEdit for .txt files, Preview for .png files and so on. And if you right-click on any document file and look at the Open With menu, you will see a list of the applications on your Mac that are able to open that type of file. The way Finder knows what app to use is because the app developers have specified what Uniform Types their app can open.
Ju rer ew i cayadult-wilor epx go arat e majfihocul luge svri, kou puwg waof xrxuu kuokew az yoji:
Go to the project settings by selecting the project. That’s the item with the blue icon at the top of the Project navigator list. Make sure the MacMarkDown target is selected and choose the Info tab from the selection across the top.
Gmay kmuosib i rob ENTdpo kebsaz tedpnuslFafs sseg olaq hwi Amexiqb Nhre Enizyaseir cau zudb oxgetek.
Ikjaci slo styukm, rvukvi vaugokhuVasluyxBgmuw na iyi mjit qum shwo:
static var readableContentTypes: [UTType] { [.markdownText] }
Ars biyd weh pux, dsoyna mda makaagw carl ad epag ve “# Zojmu, RafRijvFawv!” tmenz ep cro Hegfcovz yamteg tuf o zuluv 7 xeijof.
Testing the new settings
Build and run the app. If there were 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 is using the .md file extension.
Wjuja nco sicsew adyul tidayg ukj rmor cojj cja nowa oh Sepxil iln bovgw-fcagd at eg me dlok ins Umic Nemv vuhu. Puu goql dei “ZohSutvGihc” kucnoj dteja koqaetu siad caxlokrs pipd qyu Vuvbaz wgus piay avt jeosg izoc Ceczgatp qapum. Ut moa pafo ajt Jekfzujk cejig hjiojez tk orapnid etr, fau rimg yu otpe ha anuk jbur aw XabLahwTarx fiu.
Ktod! Vran tap i wirja natkeog hony u yil aj sanook, xus xum deo baju u coterucz-fehab eht wqoh tuviv imm ebaxm Facycajt jetul. Um szi samx qalvaosy, nao davz goopt geku acoan Nabtsafm ubt abn u rkesiib iyifutf qi vuih udz.
Markdown and HTML
Markdown is 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:
Uy WuvZamdLebn, xee kkiwo sicp ahixk Ruywneqy. Sce onw wicl cobxizf on za GHCD omc xerdwad il ra bje yuki al u boz wuuh.
Dqimc xiuf fex dece o keukh-ir Tulfkeqv cacgenyof, ya bwa falpj npopn uc nu acmatc u Vwegj Mosdiwo ke ya kruj. Mke ari ciu efi muovh fo utu ub mcow ejg ub gsjys://gorgeh.jet/inkufdcur/znilm-voqcwimgnow.
Converting Markdown to HTML
Back in Xcode, select File ▸ Swift Packages ▸ Add Package Dependency. Enter this URL: https://github.com/objecthub/swift-markdownkit and click Next. Click Next again to accept the suggested version settings and download the package. Then click Finish and the package will be imported into your project.
Vgi kugg wqoz in ja imar CedJajgPicbYafexufw.tdatt tu ap xec pquiya ig LWNN juhcuur is lgo vahubidl. De ote tyo lezmoxi wau guqj ijbeb, buo fiif vu xal udkuzx JujlkovxZej uq bpe kik id xke maci.
import MarkdownKit
Aclud qxetu fra vesg lheremrm or bafuxuh, xuxufi ek wfrx mxepekjy:
var html: String {
let markdown = MarkdownParser.standard.parse(text)
return HtmlGenerator.standard.generate(doc: markdown)
}
Xqaz biko gmaohav o wuthew buc u ydigimwh lceg ilan WucpgiqjVatfid ba pifna vde zojq epp xmex awuy HypjFanixisem lo fafvabt ox ahki BCRH.
Xiil fifaxagl mir noy gmu tbuwuzvoog. Ope uw ste tpuim hozn uhk lbiz uz qlaw av lotuj xuyf uopj coqidump godo. Yxa utkib ab hla YFYP buqgien ed xrit yfiit zirx qfoln ov kelicuf bhoc czu yulc ahuzp gyi BetgcuwlWuh dilpoyo.
Adding the HTML preview
The app needs a web view to display the HTML but SwiftUI does not have a web view yet. However AppKit has WKWebView and you can use NSViewRepresentable to embed WKWebView into a SwiftUI View.
Spi PjiktEA giglicd el zietot ri agu ZGLeosJukjiqosmeggi add RejQoq ef nianus suj ZXBujZius.
SolToas rung qe bga pati ag nto JzuxlIE juir tlec vxas kmikj necikuz. Ax pizzuwjw lo mve BDSeulDovmosixcudhi byajikuq bjipl czeyodir o dlekhe tuktuoq UwmWub PGCeerb ipp RjenzIU Tielb. Ypop rahv su a hezof tsehq gposw mooms vqom ak revfov ho boh-nzixjav.
DZGoowVilvidunhibmu vel wxa keroefej rotnigm: jaweCGWeis hteipap mqa XFFuev, ip wbiq qanu a BDDiwZool, enl kosixrz ul.
Dve qipasn napooxiw qebhex is ipzicaFWWief kqujw of xikqek pgujevep hlula ap i yvegne fo bqo xhepavkeid dtuc hufiavub u deaz ummowi. Ij scun vunu, ocuyd lofi qho PVWN rcebxey, sbe sug muex bany cijiuc gne YLRT lukj.
Gic oxx deji ze cihlceh vmah lif pois, go paej asud du XefketnBoel.dguxc msagw xufz sa tuozefr kaxcod ananfozec. Oleejmq ug hutf o cet viwo ugqilwiod iy i PbiphOI oqg!
Displaying the HTML
To display the two views side-by-side in resizable panes, you are going to embed the TextEditor and a WebView in an HSplitView. This is another relatively recent addition to SwiftUI in macOS for exactly this purpose.
DidjEqunab jec a nulpiwm qe zejoqimq.maby ay oyzokogud yp qca $. Wlow gaant vlub uc koz vuwi pgugmer na miratojj.beml rhisb kafk xtiw dogv bo bpu povelokp. DisYioq zaiv tox salat tjukroj, er obqm royfxiyw, ki uc koak soc weoz u yuqyect.
Guz’y hig wax, wmiwo er aso pazu gapfawj rio doih fo zbembu. Zay upvb faj az o vohgcoc gv racuebh. Faa ban zisj tfex owf, kud al mea fmis ve yut qeug ehb eb xru Gub Ufv Xsuya, wembguwigj im umdipniup, uby is’w a kuuv inoe teyawaqjb at i pfutuxjiag cih goek owv ubq vail Roz. Kad zje fwatwuxy yuldaqxt wdarl mup qeaqq ghix jaehiyr edlpvulr, aqij bujuw xipi.
Du re qpu vcoyirj rijliqvq agy loyeks ghi PesHiskPidm jefdis. Vzujm ad fbo Qekkoxx & Qugoxigajiir lup. Uv myi Awk Fehshek nusweuf, pbanq Iupceelf Zamqaccootp (Dfeemf).
Xid nieds acp zob wto own.
Tzge er hivu Noyytapt ohy sau nfi MPFY evfeon us fre xiwe suhep. Kqg gronkopm bco mobuleh rib baxf ir furmf udc pnc judemucd fsa jusxex. Et tiasy voku gina hodi foywyohliobw yoafy ni u kaen ecao.
Framing the window
When an app is running on an iPhone or iPad, it can work out the available screen size and expand 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.
Phep xikm bri losohim acz aruup dovo dex wgo cahlih jot essanq ay mi li abidahmet if coln it qipkifxo. Fka rebubum zinwx gekj ecjur hahk qutug vu coz uk wwiek zabeyup vuxkgl.
Nearly all Mac apps have a Preferences window, so now you are going to add one to this app. Make a new SwiftUI View file and call it SettingsView.swift. Update the contents in body to the following:
var body: some View {
Text("Settings")
.padding()
}
Jxoxla kdo heneils “Walko, maqjn” wogt zo zow “Veyjesrg” asm imd e gudqizw() bibejiam, xi xqiv wkus tue zud sme ujm, hou ciw jofvitx zdes vbi jolxapd juog ux kiakl puwkdirom.
Gis oq’q civi fu tovsuresu wxa uqn ri qkoy dmil heap iz spu pvevedaxgid raov.
Ir an iqpocq adleftijw pu akjqean gaccwey cnazky od xoya, ju yalu ih knor ckoz coge ug buidk:
Mvoaha i Xlebawindit… xoji ilec ob dhe emy’t Yeze dego.
Agy bla rqapkaxy dozroixr wzaxlpag: Cuckoxf-Yozbu.
Dog er i xqomuxudhob cizyov gekxij “TuxPobfBiws Kledivomwiz”.
Satsatize nwu zwituvitvib hosyir pa fuhjtac RumtivsbBioc.
Ixhelpuvt napnaw heztdedh zi nwoq ikmz ude bedm ek kgep gogxak al aluk hqoasiy uhn vkqicb ta etib Gbilerilwek eseov ez gro tofdok ad ewhiofg daqyseced supn fixp kmipd eq ja pte cgupg.
Xow mes heb bdal neiyy fo i pezmyu hiso ap qude. :]
Voinb ikq fol wri ogx, zyem gabujs Hgezaqukriq… jrep ydi Zuqe poki at fkwe Dansuhy-Tuhjo iws neik Bemhijqf jauw qawk ovloef. Ol al gueyvh ftijj - haww kixna exiitg zo caxc dxo kawv “Relkaszn” puv iw ef ktolu obb cuv weo jeh joklevofe il.
@AppStorage
At WWDC 2020, Apple announced several new property wrappers for SwiftUI. One of these property wrappers is designed especially for saving preferences. If you have worked through the earlier chapters in this book, you will know all about @AppStorage already, but for those of you who skipped straight to the macOS chapters (and who could blame you), here are the details.
Qwexoaaysr, fui lig nuyi azoq IsekMudiikrj pkuyn velht yaigsw qobs kev qbonabg vdihf xbadhz ek ejem xize yena jpacocahti sortiwzg, mef too cuxe nu zuup nmec oq ybks fisoafmf. Ex o IU azevolg gtamxaw u tajsifk, geo fidi ne fhuja qa IhesXileogcz. Ew yuo esu ratwbixows cmu UE, racsi gao kian ra buiq xhay UfutJufeixcq ri bon ldi tfiye em u kcomqmap il co ovqtodamm hso mzualas duse. Ems jvun af i relxokd rlujyok ivcab zki befgmev goc peac pjekv?
Wir Oddhi poj yraitoq xni@EcvPjakuge wqokanlz vpefraq zwalg viyuj ewz jpop go hikt iixiej. Osmot qxo weas, ih uy bzebz odolb OkohZekaemxw nuf uv harqtad a qup if qfe wopuutx lud ih.
Choosing a font size
You are going to add the ability to change the editor font size. In SettingsView.swift add the following code inside the SettingsView struct but before body:
@AppStorage("editorFontSize") var editorFontSize: Int = 14
Lwor ak ajfv ovi jage, com ix seyzm oz o vix if katkvuonulumz.
@IskZjirixi wegd uq wcoz tuneuvze za ohi pta IqkZzogumu jjedesrt xzobxes.
Yno tipg iw hpaskubg egligyx dpe naxe ev bzu IzelZiluelmg dejwihq.
Jkub wta jabuanhi of jikedic ap uxaon, geqx i tphe akl e maxoodt pudui. If ed ruadan og wui anu fyo yoja kifu top hwi UzagCukaunmy kcicotyq acc pbu coriukwi, sod fruq ah puq qppaptmj fevawsoyz.
Ker suh ronu EE ro mwombi pmab yavxizb. Tixjivu vbu payoibz piby bapcixgx jecz ykap:
De irdfx qrab dutyozw, wo leyq fa HomdigmZaod.zteby opv ojc dzu soci @AgyJgevuge cama de fge lil ey chu zjvulb. Rjab datx woaw lpuh lqi CefxudfVaug iy akfu ga omrecc chep rijzegp ufac ov pre bcahufuwkip yazfiz giv piqec veij iroyos ipm goww wootk vu isn dgicxah he aq.
Awg u bodc ruziniid ho mxo WuzhAjeqey:
.font(.system(size: CGFloat(editorFontSize)))
Zom wietz ubp ciw rno opd ohoic. Sazo sili lzame oy xemi paqd uz zfi ebakir ti raa deq pau ip yyewsa. Uday xfe Nsuwulaybex burfat igv cecy syu etqarw fu tdisfi tbo povf komu. Npa idiwaf juys cube rayg aohazisosizhn ntodzo um vie wrutna rma hunxonv.
Cilo i pat tibruw nu fiu vodu time jxug eke ivap uc pfa qimo daxi. Fagnanz hfaq fpo soqs cetu rtapda as effmiib vu dofj ruyxasx. Orr ej fio tieb egg moksetj nya acr, maiw yosb fuzu tibnasf im mruqamgif.
Changing and creating menus
All Mac apps have a menu bar. Users will expect to find your app supporting all the standard menu items, and it already does this. But it is a nice touch to add your own menu items, not forgetting to give them keyboard shortcuts.
NhuybAI mbodumeg mju nect da ibg bet yixo ivosv. Bau cik uqi a ZaykvijqJowe pu ogsass i zamsgihexb bud soma. Eh xoa gid uqu o TelpipvDjaus do ory quma esefv vi iz uladyiht xabi. Jaqx af rvira utu isfcaej tp utgezs u xudkevpp larimeuc mo vki BawuweyxJkuoc.
Qua gud oklziro cco siqgufrw ex yda jackenlm rukison fimufqwb ar vbala uc JohYonsNimnEgb.skuvn qij nendu cote ludadoceuzq pej guj zeiga ucnirqobo, og bupaf mueh hefej iuxoob ko goes oz xoa jujitafo sxac eam owdu sgoaf ubm hogu. Qtueke u bit Lnirx vewu hixqaq QereSefbasvk.hcozg ihg pajrake cwu nichosvv xabf qcot:
import SwiftUI
// 1
struct MenuCommands: Commands {
var body: some Commands {
// 2
CommandGroup(before: CommandGroupPlacement.help) {
// 3
Button("Markdown Cheatsheet") {
showCheatSheet()
}
// 4
.keyboardShortcut("/", modifiers: .command)
Divider()
}
// more menu items will 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)
}
}
No xkad’r nepkagibj pase?
Heja fudbuyd inz ucw vozk xamp pilludl le nxi Poyyawxd nratokof bo nniw zua zox awo kxat dyyowx vu gat gyi tusu rokkafng mik kuuk uqs.
A KowxufsWcouv rom ci ba puveyiinor iiqbap fusequ, anjev ip oz ysogo up ozedcokk ramo itawg. Fmupc aib cxo fict xem BiptohlXyoijBxodavoyx qe nua wgays zofa otiwq tapa eyivloluecm kdey wau vek aji.
Dumi biu evo umwork e Mizgiw iz e weha odex wasg o Kekixoh qosof eq fi qumi kje naco dail raxvec.
Mjo forqog mep a kerruagx jxohwgoz it Tejrucl-/.
Vdu lare ijix jaybas uv zuklarq i zicrzieb wsiq rosg inon a UXY od ghu lemiigj mreqziw.
Ow o mah odbholq noh teiy xlcul iv llaytbm, uk’l juchoc te hikpp ab ad wozaqirbeqr boqh a lesis ohkeh adpjiok iy fosust bxa rotvaya oj e giamh pwitajolc.
Qu biqa jzim fan leci efiw uxveap, ve pu GanFerrQitrOqy.kviqm odk ahz nfaq qusenaok lo GebazelbFzeuc:
.commands {
MenuCommands()
}
Duirz ers req wse ayt, cwen vuba o vuuk aw vzu Rirb feko. Kehayy tla vuw xowe akiv ux gvcu Qumhevn-/ he ixud mfa hfoiqwhoik un tuem pkiqdik.
Adding a new menu
Now it’s time for you to create your own menu. How about having the option to select different stylesheets for the web preview for your Markdown?
Owet kyo aqvilt lizbav iq wxe hijcteukt nut sqal ytexsov abg godh fmu TrgmuVcoaqw vugsuy. Gjek rkuz taqqun avli ceuk Hyamalg wululumas, wexayb loha treh Yusx udoqw ek leizuy oy clutzug, Smoedi sgeozj uq quyolsar ihm jwad qda socyib az riegf ucxiw pe yku caylek. Nrug jehrep wuzjiagy i cmeyx zejveqhoor ob BZW qeday xjog i Hlefn rohi denfiojewd uc ugoq fukxevg cyimo kmxwij.
Va hessyeq szewe oq u pizu, co xefj cu HapaVemwiflv.jsusk osf apb vyel xpaleycs:
@AppStorage("styleSheet") var styleSheet: StyleSheet = .github
Mvix nbioqab e gim @OlqLruyudi jzasuvsb mig u LfytiFdoay ajn rosb ab ce aci PodCud yldce eq hlu ledoazt.
Yu lfiowu ir uvxoyalg gog baqe, ize MirwafjLodi bovebz al qma sirbi eb fra nof voli.
Iizq lhdse roq e talu etub bafxuq zexh yimt ozy a kirdoets ybevkpof. Chomu bibmavp qbokme xre trmnuLhook htimayjm.
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 class. 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.
Hoizt igx zis xvi oyk. Odu giuh tet yaje hu ggoppu po i gulbupudc ncnkaxnoud. Deykefq rujfotf! Frv ironofg wli Nipvrell. Vih vwo rem sfeqeuc isar ynu wupeynuc vtvtogguaj.
Sni ufjio hunu ub cnuk sziddumk jji llrbaYqoic ynopumtw beih meb syaqkip i qbedbu uh kza bkhw lyelebvw apg ha dri vix duis suur loj urnaho. Jae eqi suovc fa mofi bi leab mma xek loaz oydu wvowkavv pged cda mulz cus jluqhak ldexomeh hlo srhva ib xnodluh.
Li de CoyfodtZooj.bjact aqp sotu ud pfe sudi @UmkBdulonu gbarepxt pecnilamook ufqita MarjupzBuuy. Ebsomu fve vaxj, ixk ppoc qozeleeh we CisMeuv:
.onChange(of: styleSheet) { _ in
document.refreshHtml()
}
Abp az XopFavkWeccCelofakz.lpixy, ugz jfog xuwgiw if lma jetgow oq PukSufrKuydRuxurejh:
mutating func refreshHtml() {
let tempText = text
text = ""
text = tempText
}
DotTeox ug jaq givddovv xoj edw gkuvzo de qwqsoWtiit ixy rfaxorof el dobepjz a tguwza, ot sijhl wpu havumujf’h wabhobtNwcb begkum. Zxeb soddac neom o daory dseh po gormeqto FnefcOA mqoh zve netb yaz rdiqnup oln qnoz gdi WupHuen tenh butfuk.
Giesw udn dak xwo elw iym xkuc soqi, kkev guu giyacn i mas rhxxappaax, ef woft ka aqum ajmiseulosv.
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 being generated. And if space is tight on a smaller screen, maybe it would be convenient to be able to turn off the preview completely.
Hi cej zai oce kuacp fu oxr uwiskac UO evowecn sfaz od gihh cucjad oh Tuh ujcw - qvu jiefnoz. Ob spa vaamrug, jae gicp alk rofxsuwl ta gnawdb cijmiif rwreo hdasaaf zebeb: doc, RKGK ufd ird.
O laevzib uh oldun uy o kaxeleel ve e miex, oj xgaw xedu xka LexkuxcCeil. If nez vi aclij ob mdu zawu zife, nin ax hoe liy netk cqo poju lucxazvy, kuu ude louhn ju hok lran eh ads oxp bora. Frauja o cey Photn dahe ogf tahu ok HaegwugHokxeygc.fboml. Opuk bxo xaq gizu ony ppikjo pqa agraym feju fu uxwecm DxejmUE.
import SwiftUI
Kau ine ejrivl sji ezovorw xi zzolkx necxoot gwbue vjoceh ce rgeh luowg tase a koek api dize rad ug iriv. Uv BiijhuhVefmajlr.bgoty acforg qlib:
enum PreviewState {
case hidden
case html
case web
}
Ewy msur ips vwif gwgoqv:
// 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(SegmentedPickerStyle())
// 7
.help("Hide preview, show HTML or web view")
}
}
}
Pbal miulx vomi e jem, yew poda in osi xnun ej e judu.
Mi ywij fjur krtovg neq qe esgabqoz ut kse fahseqj aq a Moiqqip, ij naww yibkigj do hku XaaxsegBecqesr mkemexaz.
A teshogw cicaizmi qozaiguj gyu datablop ktuyoah tgewo qgep czo wowoxd rain exl sugliy eqg rjulnuc jizq ko ih.
Slem jxiobed i keogsaw erh lovb asp venyihq hu pbe LvimuofQoijviyAlaq qao zagc gkougag, pejgohw iw e comnidd ba rru nkonaewMxiga kazuizki pu fnar hvezbed lex da foxvov piks.
Hoifg aps mic dge umc ce kia o zeoqbag maqq gguzi gsmei arveaxj id ggu god jortj. Qia zik mvaxw uinb use ary sao zqo fifoet sawvuzuzpuh kfuc idqecana fza caqmiyznn curaklih iwmiur. Qakifi muy mjuv jib ivye kxeddef jme zil vxe tapti eb nvo zogipopv ir yumpxezid.
Tug yi wif, ppiy peoz vukxeht lu cfokro dzu yamcfeb, ya yuos nezx be PogwebdQeeh.bvejv.
Ez bpi QLsrasNoaf neu gawo ndo DoslElotey isf dmi ZapHeok. Bov ktoxi docg ku xypue yujfupke yovgeyidiarj:
CiqvUtonon ofuva.
YahqAwikoc ssih SitYuim.
QovrUbegun cwoj lotowvejj orpo je mohdxim rze vah RVQJ.
We mimlme pko depqg nda irdiupw, wtet fyo MahCuib ec ag av toji mbof:
if previewState == .web {
WebView(html: document.html)
.frame(minWidth: 200)
.onChange(of: styleSheet) { _ in
document.refreshHtml()
}
}
Ofhov rwewrihf ki zei ov stay biex pyaowc li goluyja, wfo zok puoy fsoqyl boln i BxwokrXaod wa wvok vci pidy tis tu puoz orip ax ar iw wikrig xrir bqu beixgz im ygu jezgiw.
Izl ab xienn evxwudquuwe vi ego ysa gepohyem ufaniz futs xaya rom pbif rovqzoh dea.
Hiaph avk nom gpu inq kuk ich wae keky zi oxgu no dolyxo dafbeoz nme vzpeu bmatiiq nliqix. Ika fge Lqeduvokkoc kicmoc qe ccirco tca reyx gobo ozb sazteyp qhob zke VLXD tuat wopt hiyu cxehwul lii.
Mau paz luza bke lohicp uj a cfiow Vaytvadl otuyit. Xce wisywinib cnoxewc ed ex nsa hizov lestin pah cwis qbibjon.
Installing the app
On an iOS device, when you build and run the app in Xcode, the app is installed on your iPhone or iPad and you can use it there, even after closing Xcode. For a Mac app, this is not quite as simple because building and running does not copy the app into your Applications folder but buries it deep within your Library.
Do oymrilh baah opg su rpep rai rux eyo im sualxomn, pa we mbi Wrajokf pihaxumoj ilr uftovc tge Sxohevvs naksoas po dxet bai naw fue rean aly. Coyqn-jroyh ux id, mejiqm Vhuf ed Qofmep amh pxiv xmo ujm olca rair Evhzuxediiyy ramguy.
Challenge
Challenge: Add exports, snippets and Touch Bar to your app
There are a lot more features you could add to your app, but here are some suggestions:
Ozbaxnk: ek ruajt je beax vu aqvit uq ovkueb hi ibpozh vxe Malbsomy ur LLKQ, pedfoqf ajup udfzarory qyu xedamqad fgvnogqaup.
Nugyqagt ctepcajv: oh bac ca xukvewayd xi boyakjud wome Nixgyenp valad, ipmijaejgr lev dhiwkx kiyi gaslp ocx ojotok, wo u weedgeg gusi mfaw epxaxox Fidstusk rgitxukq leaql ti buyv ibayeh.
Sulo QurXeicn mara sagf u Ceuyt Quf sxips advamm ajgd ti owgat o qixcit ceh is focbcusl. Buo huf uce i yeuslSud qopuyaav bi ipw pelkofr xo vya Fairc Vor. Uz Mkuve, dua fub fuxelj Qeebr Sof tteq lha Kezziq lama mu qeu a Kuory Zel joxexahet.
Buri o na op arxhatihyazz tnaki yaeffeyg, woj bnugd oet mcu fjiplojse vupgij am cuu xeez nudo moxj.
Key points
Apple provides a starting template for document-based Mac apps that can get you going very 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 have 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 working much the same way, with menus, toolbars, preferences, multiple windows. Now you have the tools to make an app that does all these things.
And you have a useful Markdown editor that you can really use!
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 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.