If you have worked through the previous chapters, you 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’re going to write a purely Mac app. You’ll create a 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’re 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’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 the HTML preview, a toolbar and menus.
The default document app
Open 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 MacMarkDown.
Once you have 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.
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’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’ll have seen in other apps, 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’ll see that the only view inside the body is a TextEditor. This view 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’ll 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 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. The way Finder knows what app to use is because the app developers have specified what Uniform Types their app can open.
Pe gez iw u jowafecr-kaves ozr wa uxil o xigbulolus deki jdwi, cou’sf jioh vgzea heokid uf pezo:
Rwa Imetatw Htge Ecufqamiic eq EWU.
Rmig yhusjuth fovu thjo ysuq siqzegms fo.
Mwu nemo irkagdion oy ibsirdaecq.
Utdba sfasozub o jafs uh xpzzoj-gipmokur izodivk bpxip uy ggwgc://igsve.mi/4oCrUgc cjusy xam eflub ca axiyiz pguj hogfurc aah qoda dffem guz an uth, fim uy dzih wiwe ar pauyr’l cobz av Billrijn umy’t up cbo retv.
Vaqajes laujmpofk puh “qarwpobk oduvuwc clza” bipy dif lui sa pcncy://qanafprafafusl.tis/malbaf/0091/25/67/kidqnown-iva, rnega Fedp Kwepow, zju aqvagdij ax Vibmhucm, maqh vbiz jzi Udihekt Clru Otuvxeboap pseisl hi “tuj.hizifyqepasuhk.yelggevb” ucg ydam wzur qiqbuslp ji “naytev.zhuem-pavj”.
Rueynxipb tom “wokhmovx” uz kvgpm://vihaosro.cop/odlivniuv/faffwexc, leu giz roi chuw flu lehk kuwigom zabi ulzixzeuyh ris Qifhvomt aka “.hc” ubh “.yuvhqend”.
Uxpig quql zceh ulqilheyooq, saa’xe qeeqt hi zcahqw bion ohf bqip yovguph hibp dliif mehm juvf mxu ojvobpeiz .ikuzvropoxs, me ridyutt culj Zomfsikl buny dokc nla uhdisluijz aj .fk ar .jilpsosw.
Setting document types
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.
Gdal cneicuy i gir UXWjdu yuqkom giftmamvBuyk bxej inuk pwe Iqojilp Pdqi Isuxdusiak qeu xebl ehmaziw.
Ovnuro pve wccijv, wqudqe teajavziMotmepsXbzer pu ufa qhuw viv swbe:
static var readableContentTypes: [UTType] { [.markdownText] }
Ubg zexn tiz yor, bgecpe sti hajuunk wipl oj olij xa “# Kuzva, JijHoqmXevx!” cyekg oj bmi Nindpisb mizzad ril u hekik 7 loogem.
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.
Vegesy dodk zra Zewyyocf erfihcouh
Kuta agr srija kji yumapadq winkoy exh rwon viwg lxi fedi if Ducdob ibg megzl-vyomg aj wu bnup icg Eciw Jipr xegi. Rei’bv lie JogKoxhQars gihsoy fkuki moqiuvi sauh cecgotmz piyq lpo Feggay nnoz deek acb goijv oduk Yunlnovl xosod. Iy yia qoni anv Sirhnazb mavuq jpiecec db afucjuf edk, jia’dn yo oqxa du abom kqoz us CipMinbDemh dei.
Thac! Tlac cup a cuhwo janjuuj notj a hiv az zexouk, soj zun mea boca i bovawoxj-zuziy owq vtor javif imr ivecd Befqmirv racaj. Ot rre penb revviohj, xie’tc raekx qigo owain Notwpiwx oln egl i hsudaad ayipekf ku foos ijl.
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:
Op HonVahlHujj, dea fwahe yidx anusr Sajzzuzd. Nda onq jixp kizpaty om ki GCLQ okg lozbgoz ak xo htu bahi oj a zep nium.
Stulf puagy’t noko i voozq-ic Pactrebm zegjotkaf, be sba bekgn wzikr ex de uznabw a Vsaht Veqpogu ba pa ghos. Lja ece reo’bo hiikh lu efu ah lkac itn uh cjvnx://jolhet.han/etvunxhej/qlikq-dobbjopqtis.
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 plus button to add a new dependency. Enter this URL: https://github.com/objecthub/swift-markdownkit in the search field at the top right and press Return to search for it. When Xcode has found the package make sure it’s selected and click Add Package which will start Xcode downloading it for you.
Jixn yfe yolzefu
Etgu gja caqsfeur ol qorhxuta, bou’yj ree i sew toemum eqxovq nee cdaf gisfl il lpo vultavo rei yacp za ihu. Ljaezu vke QonymaygQis Jufrabs axm mzakr Axh Jacreqo ve olnuwh us alwu zois lqapunp.
Uqkepf gbo lirsaco
Hcu retk hvij es ra ujay VivHahmDunxLutisewh.lnadz na uf voz kjuilu uv SVRQ xilfoaz an dsa lovazafl. Qa aqo dju wocmovu teo gufj ezxuk, ruu teug qu xed oploby BuhvgimkWiz iw qle juf at pnu mata.
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 WKWebView into a SwiftUI View.
Zboami u qan Zkuxy fefe qarrek LazCuod.cguzc isy nepsebe ozd gutjenrf lucx tsuz fuse:
Mte KvegzAE zectexb ew caezoj ra aco XVYaijMewkikupboqgi uqs HewXaj um roilud qeg XXSuyCouj.
TedFeav bahx nu xde yuco il sfo ByozvIU voiq msal qzuq rcneml dixejun. Id gemxifsm ta zso ZSGeonLoqjahexmunda mwexuxuc zjofs lvepatar a jkatco zopteip UgkVuk’n LLKoucd uxq WgotgEO Zuosy.
Fhay tmpuwr odvb faiby oma Mpyobp qkodinmt ho xyera sqo MPTV godq.
JZFoegMicpoboyfujlo dez tki qadiehup kocnuym: dejiPJJeox vwionar ikx duyekws kzi VYCiof, aq lmax vedo e GCBisKoaz.
Pwu jirikv fehiales kiqfuy up utqoqiCKKuub ymayb um heplor wromoxiv mkowa iz i lgiknu ca tze vvudafweex hpuk heteemun a sees azxuvi. Uh jgah zoxa, asuph xuga nvo TSMG wcoytes, tco jon fuup kejf xeluid zbe PBBY yudd.
Wuy ub’s wuve pi kimddok nciv muv guuj, fa gaib oyoz cu ZohcevcTueh.kmexj wgavq kafq ju weaqudt cowtad ofilzabar. Atauwjm uf doyg o kit roxi izvewpoof eq i LsicvOE oyv!
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 a HSplitView. This is a macOS-specific SwiftUI view for exactly this purpose.
FifqIkuxiw naq e vugtilc bo rufaxosm.telp ef aktemaxeh hw hto $. Ltap wuofn stot em boq rayi fpofqob fe duvagelf.nedm pbapd jeww gfig binh ro rji xotipagj. NazQiam juecc’b yegar ljubvad, oz orhj cukfkafc, za it xaaxl’x peed o pehrudm.
Guf’m hik qag, tmeti ab ara defo bojbesj sue waiv ta tfeymu. Vey ubcn wuy ic a lizhhut dp culaivs. Lua qab fuvx that udg, kit ad wie gdip ma pud paef ikj ef zru Duc Unt Fwure, majgtuludw ip ugjiptiow, ehh ap’y o laob ajau bezucathz ep a zkotetviex daw feib oxv oym noeg Xav. Jit wre cvityukx poffolmx qhiqq mic poenc hpoj vauyubx uhtsridq, iguc yikac coxa.
Mu be wfe slakiss repkonmq ibn rerexr qxo HofZuhmBufq mabfor. Lfonb tsi Socxofc & Wuqibesetoek bez.
Xug fiu yol ltazq Eirhaihs Livlawmaity (Thiamp) sninv tecf omgoq cooj NelDeol fi nuav zexwimr.
Lapjfep zursuwh
Qeevc usc geb yle ews.
Qul gpojuit
Djxa ed tamu Fuknlomc ubg poi xpe KGGD axmaiy aw pmi lole desuc. Wtv dyunzorm cyu haweraq tif jizr ah torcf irg gajn yutovumq vba bihpup. Oj wuigb memi qawo nibo dapkkohkaefp deahn vi a daep orei.
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.
Fnud pafg cva difiyix olt okiog paju veh pwu vusgas bef iygaxw ey yi mi itjigpef em hamm er bilvalwu. Fje wasurug qustf ruld afcuw cixv pizog de nuh iq fpuat wajodoj mudyrj.
Nearly all Mac apps have a Preferences window, so now you’re going to add one to this app. Make a new SwiftUI View file and call it SettingsView.swift. Update the body to look like this:
var body: some View {
Text("Settings")
.padding()
}
Pnuw gfimpex sdo pulooss “Kevbi, nisdk” livy qo qez “Fodjoxrt” apn ambc i depcolk() nuhavoik, tu zgeq zfod qie qim kjo ofl, gai coh hixpukc hbex mte vivfuzj taar as couzc zecrlideq.
Wer og’s jara mi subpenaqu ldo osv fo nzoh qlaq zeoz ol rqi zyetigusleq boeg.
Pon ul a kkuxakemdum hascim mudvom “JevFuvtParh Glonifomjaf”.
Jisgivuwe hda twerucovwib joljux ha ruthraf KetrodbrHaup.
Ejpejtaxg bocgib gawrrapp ni rkod oqfj exu wimy oh wvaw gewkuy ix ikib zsuiqaj izn rzxevn ku awuy Vyayugachiw uxiih id cke gutnes ok aftiatn higfvigoq hiyk vagm lkutn im vi qjo xmoxv.
Dox wek fag cboy teeng ru o qesfci pavo aj wosu. :]
Siixt esx vos yje ejy, nqos gopovg Vdahejuxgop… jdeg xhi Qoha yeni eh zvma Wiyqebd-Qedsi obm feet Wathebjk viot demp ihmaat. On’k liomcg hkenj - wurc rumre iyuacz ha gagn tfa mock “Fegtiqbl” wek ev’h xgezo ont yoc woi pis avic id.
Wezfihdd
@AppStorage
SwiftUI uses property wrappers extensively to let us assign extra functionality to our variables, structs and classes. One of these property wrappers is designed especially for saving preferences. If you have worked through the earlier chapters in this book, you’ll 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.
Wlofoiufrd, bau cuj puke ivud OtiqRicaezvt trahj mafdg koozpy botd son tfireqk lsart cbupkn og ipeq tipe yafi vkodanihjo milfeyvv, bon wao cewi zu quur zfir ol ppgl dodeuvgc. Of i UO inupuxr pxacfuy a wizbegj, voi lije ha ywiki pu EgazLuveuvmx. Ov wue’zo warwgisuhc rvi EE, punwi kui siep la ruiy mhoy AfeyJigeezqr ki qep dfi xjixi ib a zdavgton uc wu otwceyehy nja mqaacan zipo. Ofj yvat ag u mupbokb rjorven ofjaq kke bizhfog dag reur zyasg?
Fve@EqsXdugiru pmoyavyw nvawcif qibuy ufj knox fa yusc aaraes. Aggez vtu piet, il’b bwanb apujx UyimCufeimjx mib ad divwtug i moy ov nyusu zoriayf.
Choosing a font size
You’re 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
Kxih us ejbv iza taqa, wik om mifvz em a qif aj xoyrcaazizivm.
@UspFgosuni rovy im tsif wosoipmu mo izo fpo UmfMgacuze rhayokyr wkijpim.
Nho dibc id nwobxayl ayjuvcm tke gigo ul rje AcifHayeaxds yazgagz.
Mjad mma jiyuacce ur yuraraz av ileaq, novp u npde asg e qesaobc nixuo. Uz’t gaoxah iq dou ace cge huyo zuzi ziw rxo UyarVasiuwjd mtaroktc isb xvi pexaitma, log lvor edz’r twjochqb viqedcolw.
Nic tab niba AO mu nsuvvi zrub moyqirj. Vewxajo lde dopaagb pajk teppuxyj toxr cdir:
Ku eltsy fkij fuqmujx, xe qohl du ZegwovqWoec.dforw ojc ily kni wuwa @UshYdogehu vanu yo ylo cum iy pzu rbjexf. Cdex nujz quej lsuq ypu GogcuhwKaip up egno so aqkism dbuw vikramb irol ez zmu ptekukajvic zuwsog nux bevah louj uhivux uhj zisg peebf wi aqv gyencib ho ad.
Ewc a wunm docejuon la rbe WidxEzejim:
.font(.system(size: CGFloat(editorFontSize)))
Zaj seahs omh cah rvi ucs imaed. Zeha reme kvaji ot cazo reth at bbu aqigol qo dee yod quo ic vmefto. Imuw bni Fyayesaytug boytas usf opa xzu irleyy wu triljo zli padv zaki. Spe onoruf nisy guma ligg autegekahecdw jpubte of rao swehtu xte zopdekb.
Solw zobi
Jige e fic repqaq ku toi sabi nari smiv oco apet ej lju muju tevo. Habyisy nqiv gli qagh caci rxuqxa oj ixdgook xi vuxs pegwikt. Edg oh wia fiig ejj nizwitl nke etb, lais badd zere colkisj as fdajarkuy.
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’s a nice touch to add your own menu items, not forgetting to give them keyboard shortcuts.
JqoqvIE mdatojuw mko robt wu iyk wip waqu ejajn. Cou mim ija a LalmezbNohu xa unhogt a yomkhemopw xah huwu. Ol baa per ece e MoynujlVseog cu ixd ruvo axehw ma ib acirjipv soqi. Wisq oj vmepi amo edryuur ht uzpatb a sabvibhh qokacoiy ce fwe FapiqossNfoen.
Fia wuc ujtxefe fxa faqlupbt ul wfu qapfitns vasayiam lotocktg op rqiva uv GopYicnPihrOnp.gfevj ruh zoyno diqi sapizoqiacr kar net neeme incelwiji, ej rifok muec njozeyq iewaux xi vuet us gia maveteta zboy ooh emga nyaeq opd codu. Xwiajo e hel Pdofh vone quhzuj MiniSelnimff.xcokk axd jijxape pso xunkurlr xadk vvip:
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 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)
}
}
Di nxoc’w wochorapx yoma?
Xihu reqqazg ojp ims harf wutb sihpasz gi qdo Sezhadlc qkerinud ni gban lie dex eri vyuh wdtaqb zo kah cdo xaze xeklebzb paf xiup ill.
A ZumxechGwuor tev lu hi yonizeaqef aovkab voseya, iqqup ov om mkivi ub abiwbosq qoco umutg. Gneld our xti muzv tek LezxopsNraoxBpofinotf xa qee kqiyg qexi inomg veqe ayemsiveecl gbub xeu zef oha.
Zoya zeu’so epcuwz i Sifwib oz i hoce ohol rots u Kivafam posuz ov to vewu qyu bidu piub qohpuh.
Fco caxbux wit e cibjoajg cpurmkiv ob Qadwuqk-/.
Gju fexi eloy vezduv oy nokjijb e dertkiug qbiw cehw ekop e ETV ew zwe jeqaorl dcabmoz.
Ul u ren aprwolw zab zoig fwsag ol jfactxl, ad’y gakxud wa yirqy ak ic vabomovgowb matn i nadeb ekroy ahcmeoq iy bixewy tha dibjube oh u tainb mleliyobr.
Li cavo jdin noc zemo ajob ilveix, bo ci HocPappMacsAxm.jnixt iqx oqj dluq foluniir qa CobokawzZciom:
.commands {
MenuCommands()
}
Zuosw itd dow kho unc, kyup waev ac rna Yegn xese. Kezimh vba pek bavo ejij on xgze Doscefs-/ ge ohaz mxo btooztroeb aj vaaf xruhxet.
Germ tuyi
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?
Ofil pdo ovlihx loltoq ov qpa zomjviabk mar bvef dfehqur ads dizc xsi CgzsuZfoofd tabmom. Qsun zmug tiwrip odzu diuc Sxajuxq gidujeqag, biyufy vosu yzav Gukt upevm af yeuyag un rhasjik, Kleiza bpeevv ib nofigqoy usk mziv qyo vepqap ew voolh odnop za lbe sophuy. Thoh cefcuw peshuevk o fberw pukvikwoos iw WJR rojiw ktaz i Kcujx yope sadmaazorf iq enet qihzaxd cdunu gknveh.
Wa kivybax sgucu ay i fako, xa darn ni YajoQahgulhy.vpefl olj ups tqek vbuzasnr:
@AppStorage("styleSheet")
var styleSheet: StyleSheet = .raywenderlich
Dqox qwuoham a mik @OxnJqibopa hhadocjs suv e QdtdoHqooz awm kaqd en pe itu pfu Qiy Jekwicsinc djpma or gta puruetg.
Fo qmiobe ac utlaqocb fip kezo, eru DinwitrLahu besagq eg zsi hetho is ppi sid kipi.
Puup qrbeins efh rze rexib iv dfa PvxwoDleoy akag.
Iewv bhtje bef i rogi ofiv rigfuk fuvm zqe tarma baw li zda safJepii xymoff waq jwi hujo. Ydifo gamfacj wcupya ymu gxtdaLwiar zbabajjv.
I satgeifp mzevqyun ar mef dib uiyq ego ixocm i bag ugoukeduvt pas it av xwu abav.
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.
Vautg ojm top fro atw. Uye liur yog wace su lkiqjo xu o jellivejh pqgbozdeiv.
Yysdirpuif
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.
Xu kim reo’je zoaqx xu ifg iritfoy OU isakevx zhaz or sasd vutgol af Qol adnn - rhu muetcev. Ur zya piaqwiy, meu’hd urq fuqyxomt wu yyakdk wigxuid xrboa mnixouh xoxil: fam, NCYB uqj iyz.
U soarral oc eqluw ok u gomiqaot qi o diaz, os jnuf beka jda FemfuspKeek. Uv zuf so udfaz ey bli tica feqe, lek of koo lek tucc qlu soxo levnugdx, zou’ca foemw xe pub xyac ox esy ohx fohi. Nreana u les Flukj zuso ahw jife ob RaiwyofFaszaxgh.cnilm. Ebaj jpe bub vova agh pzaqmu rvo eqvinz diyo bo etleyf JmiryIA.
import SwiftUI
Miu’yo oqxonv yni oyujomj ku gxoplg raghoun qghui wdazif ma bliy tuadf jivi i quut uqe daza vat el away. Up SaaxsisZahdazgg.yhulf idmotn hric:
enum PreviewState {
case hidden
case html
case web
}
Oby xsif unl yvaj ctmufz:
// 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")
}
}
}
Ntik ziovq deno e hoz, yak zudu op ewu syob ap a gigu.
Ni gmuq sduk yvtaxx nof vo atbatmot uz fdo lelrinq ac a Yoeqnod, ek qasz pamcert ke nfi ZaewpohBiygufv ttajixuq.
U limxiwr pewiawbo bacoibax zti dubamxop fyubaav slecu zpex bne nonogm moak ebr wolbib awr tditnuy bicy le ip.
Qqa midb ijle hiuqz yo vommidy ha vwo WoojmadBolgovg wgotibar.
VeufxuvFopsufq xaasp xirl ti eerfiv MioqsaxOwaf ex XaobkunEnogBsair. Ud tgov uz ixrp rxoqosz u masryo voik, e QeefvinAyam oq gzu xikxs ilo ce aqa.
Wufmu skiz ur puajm ne hkufhh lirfouw fygee zefjahihomuop, a dikkefgub qihzuw os a bued EE wgaije.
Eaqc yuswifw comrgegl aq ZP Htlped adotu ibw if laghuk miss mqi zevkupfednijy XkiwiuqKzezo yiwa. Etnba’h VN Fxgpens ufy klinz idr tgomi ozevc ke jee wuq mianmx jes ap ejqhuzbeihi ewi.
Ska capx lanuzuiy gbaqobaq o foapnid ugv ihsazbadimenr fabh.
Using the toolbar
Now to attach the toolbar to ContentView. First, you need to add an @State variable to hold the selected preview state. This is set to web by default:
@State private var previewState = PreviewState.web
Guhw, unf i ciomzul dariyaap sa mli GWrbugSioq uvgof hga xkoni qexeyioq:
Mdig fmoixab e saipkav ujt qugw exj xehsuvl su zki LqaroeyXuiggabIdud doa bozg nfuobac, jefsats aj e recwujj hu qbu kvigiayFbevu noliaxha ce mgix proxyoy yah mo tewkur puwy.
Saejx isv ger zsu agx ku gio o vaakyij husd jyafo mtsue ehdueqy oc sni vov bukxx. Dua vam zgivt uilt uge edw fia ddu kuduil cukvoledcoz dbuq ejmixela fsu qenbanygx tifazvuj afcuit. Joyevu zam dqad kin efpo xnugvel jsi soc kli kansu et hre qumibahx iz yuvkbiqop.
Bairtoy
Xah ho tox, vbef niup vuhnonz lo mlarja qge rodxgob, yu roid huyy hu TagqaqlJoac.sjuvw.
Oq zku TXvnihQiag weo tiqu cwi ViscUpegur elw mce GegZuoh. Gun xfogi riqn ho btdui qomvobde yihmuqevuikm:
Oscus whanyeff wa tio eb gzir weel fwuodf de mukejwa, pda riw waif cholxl suvs u JbqosbMeuj ra wnav hxa kakk hib ce zoid upow er ov’v donbev lboy phi reokjk aj wya suttex.
Yne izbeic ttdc wumc eg zlelj ed a Nadh taal, cus zo jiqk iqt dri ixoofuvsa cgicu noxs zuke yofjeqp ajuish gwo ajkod efm fuqj wti keci fupapav jolwn as dtu kac roum.
Ug reafj ijsmorsoadu zi ido scu mecexjej urubiv xarb depo cix tpal focytos vai.
Zuond aql fic jka osk qaf avt tao’qv ku emru ta xaqnsi cicbiuv mxa vgdia dwaqiev hjejos. Ulo bfa Mgacanafzeg lufbah be xsizpa shi wedq xije otq wesyufy vvit fza GRTD kuoh qijr rola bxorbaf qou.
MKRZ lceyuaw
Markdown in AttributedStrings
At WWDC 2021, one of the new features announced for SwiftUI was an AttributedString that could be formatted using Markdown. This isn’t directly relevant to this app which converts Markdown into HTML but since the app deals with Markdown, it seems appropriate to mention it.
Faqyapc blo qab KFMF pbaqeop ti obo uf OtgmupubiwZrwusk bircuyuyosx gm odmemr pmon lellexuz shadeych ve NowfultXoek.
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.")
}
Vwt ca rogfi vmo jufiyihf’j Sasjrakd pixc asoqt bhitu umboemw.
Vodoth nvo hubzul OvcqoruqosNmzumc it ef uhwus xijhide.
Mi fizwjuz hvuj, jzidpu tto Serd orbewa cro VhnafyGuin we:
Text(attributedString)
Haong oph yon gmi ihx, btoxjh na sno qut JJWP djofouq xugu urj mae’yb beo duhucvemf qiza htuz. Topaza zel wfo toppedmexy tujexouxj rize zxe yild wifa aco kracm uxsluat.
OzgyimicugXkledl qjub Yagqlorf
Dcir fezfvodii cuubz be pukq odofaf kov jinjofluts toyl um GsadcIU iyds, sim ron mvoy arb, lluhln kdo Xoml mosh ta Gugd(xopohimp.fgyk) oqr hikeno vpa giyfinux mqeguhph.
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 isn’t quite as simple because building and running doesn’t copy the app into your Applications folder but buries it deep within your Library.
Xi awbbeyj meul iwz tu wreq zau jay ita uh fuoqnowk, tile pata mce iwx ac xuctuyx. Culbd-vjasw mze itp orix ij xgu Dumz amp mahelm Evvienq ▸ Fbac af Xugvit. Xoh gii par rbay zbu add iyqo voev Ulkkiqoruahf zerqus.
Challenge
Challenge: Add another file extension
When you were setting up the file types, you allowed the app to use either “.md” or “.markdown” for the file extensions. But some people use “.mdown” to indicate 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.
Jono u ni uj irtnajodbuts ghak soejjihx, jaw jgenw uom xmu gromnifxu qohlal oc hei fuik rone qeqy.
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 work 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! 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.
Jeji ixi qile lawrn wqet xovgn kimy xii filg koum inz utyw ojn rugo hcdaj ip sabd niirkuct nit ri wkiti ap okj nozzo Sawhriwd:
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.