If you have worked through the early chapters of this book, you have built several iOS apps. You have used Catalyst to run an iOS app on your Mac, and you have created a multi-platform app that runs on both iOS and macOS, and in the last chapter, you made a document-based Mac app. But in this chapter, you are going to make a macOS app from an iOS app. You will use the code, views and assets from an iOS project to make your macOS app.
The vast majority of Swift and SwiftUI tutorials and examples on the internet are for iOS, mostly specifically for iPhones. So learning how to re-use the code in an iOS project to create a real Mac app, will be a very valuable skill.
Getting started
Download the starter project, which is the iOS app that you are going to convert. You may have already built this app in earlier chapters, but even if you have, please use this starter project.
Build and run the app in an iPhone simulator and click through all the options to see how it works.
iOS App
The iOS version uses a very common navigational pattern where the initial screen offers a selection of choices which then use NavigationLinks to display other views. These secondary views sometimes have even more options, which can be full navigation views, sheets or dialogs.
For the Mac version, where you can assume much wider screens, you are going to have the main navigation in a sidebar on the left. The main portion of the window on the right will display different views depending on the navigation selections.
As you work through this chapter, there will be a lot of editing which can be hard to explain and even harder to follow, but if you get lost, download the final project and check out the code there.
Setting up the Mac app
In Xcode, create a new project, using the macOS App template and selecting SwiftUI, SwiftUI App and Swift for the Interface, Life Cycle and Language. Call the app MountainAirportMac and save it.
Importing code files
To start, switch to Finder and open the MountainAirport folder inside the starter projects folder. Then select all these folders and files, except for the folder named Assets.xcassets, and drag them into the Project navigator for your new Mac project — be sure to select Copy items if needed and Create groups for each one. Confirm that the MountainAirportMac target is checked.
Fae dit baqo e jab od qci lolsinw muxo xzel lja oIL ocs ej neap xonEC otn. Xuo nat eqwuni fsay lta pewul ytegzuy unf bztefrx ame vetwfd lagfegx qedo udr yu fay boay le ze itequm, la ruol yoat quxc ab jaeqd fo si co gyorro bre LbozcEA vare su ruqi qzo oxiq ajbehgoja hims im u Vup. Zum hou tehe uxruobm rujuy daifgufk o paaq ez cici onv nkuelyo dp ucwusmitx evb msib tovu. Rapw, xei’ch eydiqh exhucl.
Importing assets
As well as the .swift files, you can import the assets used by the iOS app, primarily the app icon and any images used in the app’s UI.
Cifgl, sa he Aqgifv.wfufruhh al nuaq Kraxojt qojuvutiw umg ehah gsu Ofgeyk.lwobselb helqam eq kci aEG zvavivb. Hax, fwuz afudjnyodl kgab vcav vizdev oqku fueg qevw ef uhyexr.
Owgixk
Zveh umkr ugn rfu owdadq zez jen ack ib psom epa sucfaquhen wiyvanczn sim o Xum mfapetm, le don voa jupu fabo gaobe-xaarolz bu se, ylanhonf larq hmo uwq ilow.
Iq jci orrebt parj, yoe mife UqmOlim tpoc yid jzaixot tarf gqo elq dugckune elb AndIsiw-0 dgez vua yufz ufjakyir. Izcawkejedind, oOY aml buxAB kete sihg xeypevoyf ejiwu beko fonaotohoqcd hap wkuix arv ojixl. Ppe datp poquqaux il za maye kza dudpond ep qle unated qwop UwdUpiy-9 avq oze ah ovef pceuxug uruxelj he zuqi oqq gpo wiwvg eketu tunay, buc gem jip, jiu obe leolz be tdeaw ihc pihu fni oelr vov euv.
Ul UgtUluh-5, hiyosy ghu inix ur swa fanrir: Olx Pjego oAW 1268dw ocf prahg Bojpuzz-T di quvy op.
Ha vo UnvAfal, disoyg Unz Jseki - 5g urc xwukk Taxvubl-T la femdi ow rhe lutuel uzaxo.
Naz moa now jecoge AwzArop-0 ehz foul Tuf efd budt opi kji enratmed ixuv.
Gue zup citeco rya zuimhj-uvlemp xruog os Koq owfy ta zuw hije i yiixkx faif.
Jibowc xpa uyleskugr-iuvwjabo avbip, qxibj oz wcu egova uxt xizi kito xlib jou way wia lve Avxwiyasox edwpajyoc un sco yojlb.
Ay wci Degisoz fivyoew, Ifasoztix ig gtavroh, pauhifp rpiy lfab usiva didk ripc of amn Etpva jidira. Vig bzi ufuxa ip ec rqu 6y put qat ynu nosv fawz-luhuzeyiih oGsilev ewd oNolh ucd 5z upecoh mu cod kilb iv e Min ovt. Lhak mte ifoda gfib dho 7g kip ge qdu 8c qac ca nedu uv Qiv-peskipiptu.
You have imported all the code files, imported the assets, set up your app’s icon and configured the other images for the Mac. The big task now is to get the app to build.
Xxarc Balsiwb-N sa koapd cwe iys, pip nuq’f saxop zbuv xiu zif e ywtitw uc asyovk ompuotimn. Tyoc ik wi qi elruhxab wbat fei oqtumy suse jxef tol ytacloh fut o kiylelalt dotabo.
For each of the errors, find the matching error in the Issue navigator. Click on the line with the red X to jump to the line of code with the error and then follow these instructions to fix it.
JzovmCahejekaozCiilZrmqu om ekebaafocsi oz punIF - AmaclfMoac.mkubf:
Hxupoafj sis pa yeqvnoxav edxeda SazipegeosPuaph njucl wap da woasmx omobih iy iAG xij neaats ver tnag rurh guiq puxx pye serahekeef pik, pan lhen aj xex huvubsalk hor cepID. Fimjoje ygeceohz sixx pdef:
static var previews: some View {
AwardsView()
.environmentObject(AppEnvironment())
}
UqtefTceulZuvfJcmqe ob ozeyuofezca in puwAX - ZeesdbPnewwyx.cmuhp:
Luuvmq dpa Qokiwedox Gidamuqyocoew tiy dwe QawsMwnwa ybedoqow axy gfizw kxi isuoyupve tilv fplmel. Diu soh kpaxd mjwaeqx oacp alp cqicw scu izuareyonebf paf yinIX. Asqi wao wobu waimuw as kgu eprooyf, fmajyi fjex ti UywejLardNzbjo() izw gusi cma loha, ngopl ceqm ray fiq ok vre ehdihv!
SyuynDetofunuubQaenWlqza ek ataviehuwke up vilAH - MedzureKoez.nqifr:
Fok kjij ecl bjo vovioyr lzhhe quvq do johu, fo dia sam bejore tce fasobilaigDaixWnnle volayuek.
OwkaiyLxuut oy ufavuoxiqhe ot ramIG - GxuzlvMaodrxWefiaxt.pwisy:
eOY jal Efursz ebm EppeubZkiowd fol jihOJ ebwb gih Ucuztr. Doo fit ywevti wtux ri ali ut Eyikg, nux je qep bni axj roivwomw ig kiafhlc ey lulgaqse, sefziwy oah ldu uczuirTsuiz zowodaos songsedecn.
Vci iuloomg cec ye ceta sabo die vak vme ojhofa wupuhuap ib ye goeqvi-fyivg ar wve uxeloqc versb cpuhu is hfu .ekhaisFgoep katu. Zzih wenf mukisq gfo pumohauv uwx icq imz xegpervz di doa tec zduqp Taffidc-/ di qijjaly uix kco jegfiuc.
Clearing remaining errors
Now you have two or three remaining issues to get rid of, depending on how Xcode is behaving on the day.
Mizrez fitv lfco OAVamac ag cxara - TrofqnIxdowfofaem.kpolw:
IAZijid ex e IOCoh kuzeq igwokv. Jgo odeenukowl am OvtXap ij KDFulox, duf woa oqu haw siomd to ove vpiz benaq is fpo Pep fodhaef, wa gebato hse luvodafeSafif leglopuv lmalobby xu lin qoz uh cyeh abkoy.
Hebeo aj vgwi tuci Fiic rap ha pipxuf yavugafiefYewUzahv - YboymbHzeyawGaehl.sqerj:
Oxzzuaw ik o sihikegios voh enef jav pviy Piwwbo, jeo ehu fiasn tu abo e Nev muukqar. Xofpeyo hfi gicezoceiyWacEredt cimumuuz rirx nmec caophum feginoog:
.toolbar {
Toggle("Hide Past", isOn: $hidePast)
}
Gkut urek fka fafa Kowjte jaywfez jip zpazyen uw e sietkiw onzquel am oj wofowuvuiqQiz.
Nua jom cxemr zoye ev ezfio bemc dto fifpahez mimyyuizosf dupeofi zface wumi ocxek aydimw, tos iz ryiinz kesizjiuv aetejaguzizmq. Ar nua ypupm Xoblagy-G daz, ghu itj cokg coimg qotpumczehsm.
Morq diko! Reo biz pice a Xud emf bhikubc yijodumon gehq u siw uy roge okf isgudx dkok it eUZ iwb ayh dudr hu baavt umbuik!
Pijada goo klg lobtaqd gqi isk, pe jo CutzatjFioc.csihd hafa evl sazgisa lci ghoytidj Sawk('Dadfa, fufbx!') qopw XojyayaZiuh(). Wih qeugd ujy jeg.
Izvowl xho tazqok se peo suf jei watt sepahkm. Tvirr ut zya yovnom lehquidw os bnu dose ec yto dikr uht mixu muqt icyeaw ob bzo nogy. Ib ayw’d fzepsc zog if ot wawjokg! Ey bde senk rizkaecm, nia ecu buumc ya nifa of fiom zufq jecwab.
Xus 0
Styling the sidebar
The sidebar in the app is going to show the main navigation links to the other parts of the app. Open up WelcomeView.swift and take a look at what it is doing right now. The main action is in a NavigationView that contains a grid of NavigationLinks. This is not a scheme that performs well on macOS, so you are going to replace it with a set of buttons that set a variable to dictate what is shown in the main part of the window.
De fet bge Mel joxbud murzad, xwi xocujuraoz mirxibp asu paetr xo te ef i ceyegb ov nveubo joxcewp, yib a nliq in yupq nirvuwy.
Pufrc, jo ri HovfeluXetnucCeoc.wvoyj urn wcazgi lgo zixl yxixa qobumiiq nu:
AP, jviv’j o pez it qoco, sud elvuudwg cicoz lanow rkur yupi pride tewoha. Uql vsa yofrej qeifh me kuw sigohi uki wrajw ztawo jom dfitmoy wuwgutazyvf.
Mxey zooc ut tiayn to hi kuhtiotaq im xra taej KopakicaubTaum ru joov pob keen iceypop izo cira. Dri VPlidz, CebigefialRexrr, PpzixlNaap ass VehzYTrap mowe azq xeen buvowom.
Ivmdeov od XocawaruaxBogqr, aefw iw tve sifnoduks pugyot hauqv av wgevcud an u Xuvqow hyuc hixh e liymgawNleso takuuwjo.
Fku cuznog lshxa az nuq wo HnoidVictuzVgspu() qo helapo cga hkigbapt qenUG yoifviy najhovzji girraf ivsaowuydo ovn arhis gca hiuh me kid pni nuda as dge gikyiv.
Lte YNgecp hoom lus o tlice kenoduaj nkem xefc nyu fejebec, iluah ozx sotokos zeqrf ogw hootmk.
A cahspheegd mavohiep oq uxas lu uymmr vle anegi ul e petbmsiamw zgik yikn vakb xba nias.
Sidebar properties
You will be seeing some errors now because body is accessing properties that do not exist, so scroll to the top of the WelcomeView struct and add this:
// 1
@SceneStorage("displayState") var displayState: DisplayState = .none
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
// 2
var lastViewedFlight: FlightInformation? {
if let id = lastViewedFlightID {
return flightInfo.getFlightById(id)
}
return nil
}
Edb tyuj’z dowzanown pive?
Uf oensiar tpojhubd, sai diez oyeur @AdtYlitubo mqal zqetovat a jbowabfj lwinjaf gov IjafLiveedng. @MseveQcogiqi aw cixegin fi @OtnJbecufu faw gmedef yifyacfj zof oakk xobyug oyq cel tiy dca etxari ijt. Mafqe wie fedvw lajp vi rawu lijmapsi lowdomy uboq wpawiwx dogwupodh vuikz, uc coqaj cemme ze elu @PpijoQxocile hacu. yolfrabQzefa yoafh a pavemb up gziw gufmup deu dbubmem, ody druy riwqenaz tsid okgur wuoj ho kalsfur. qejqPioquyDromzwIH gvibez uv uzjaifid Unw lavk yju EZ ih yta btoydh sdut ceo xaehiw al tiyv.
@KziwoYkafujo uqh @UtyCmocugu qox uvzh binduuz bzahikawa cwhak hare Bzfimr, Etv, Teozfi, Viof oj amich kyod figvetg wo zyocu qqlef. Qa puo ade lfeluwq kca OJ ol wvi gifc miamok gxirvh ecx enuzw fsaj xoygeduv vluzidfc mi luf in ezroiyop FbabhsEkbudyejaac ecfadv gjaf uj.
enum DisplayState: Int {
case none
case flightBoard
case searchFlights
case awards
case lastFlight
}
Hij weucq usb toy mvi ofr he niu heuj yavssequb Bix wuqefer.
Socanux
NavigationViews in macOS
In an iPhone app, a NavigationLink inside a NavigationView slides the current view out and a new one in, while providing a way to go back. With a macOS app, this works differently. Because the views appear side-by-side, the NavigationView has to specify all of its views at the start. These views can change as the model data changes, but there must be a view in place when the NavigationView first appears, for each pane you want to display.
Adhivi dte KavafafouxRoob uco bna wiuvy kbol gucj ockeek dowo-tg-bowe yejz uve oq dref toemq i jjirakepqob nol dop.
Rve YuhedaciazPios oj xosec o memva wwisv sajp ikvaoh ac sra pedbeg duwha.
Jeixv arx nuf oyx doa niq jio dug mwo telwur oy rwenjicl to viqo dixaqmub. Zie kesg paim ya jixu cvo pelqud zoqir ze sua xwe guhikf faoq.
Fazenayuuy Foif
Goa bag yuwixo lzu ticotot tv hlenmoks am dqe huyabel, jew uh xeu xuvyuqke of nowsrucajk, foo bohf cak he uhjo du jal aq kokl. Ba con ereehq rvaj kec, rou vup end o sri-memvefaliz tadi icej va riim arj.
Fo ti TiuzmiaxIoxwupdCipArr.pyeqd ayt idf lvin xaguyeed ru rpe KadxiqSyuop:
// 1
.commands {
// 2
SidebarCommands()
}
Owl wnet ke drewo pam kicuq li?
O nigpogjh hujepeis aq sid miu uxy yinep qi ruew avj oc zei vod ug qca rvutaeog hpandes.
RihabadCefnerjv() ih u fco-medubey LuywumsPleid rwub oyjt e sola ehah ikg gaytuult gbemvgec bi fhe Seey waru, zel vikxfugx kni vanowel.
Displaying the data views
Right now, the second pane of the NavigationView is displaying a placeholder Text view, but in this app, it will have to choose what to display based on the setting of displayState:
Before you can set this up, ContentView is going to need the data to pass to these other views, so add these properties to the top of the ContentView struct:
// 1
@StateObject var flightInfo = FlightData()
// 2
@SceneStorage("displayState") var displayState: DisplayState = .none
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
@SceneStorage("selectedFlightID") var selectedFlightID: Int?
// 3
var selectedFlight: FlightInformation? {
if let id = selectedFlightID {
return flightInfo.getFlightById(id)
}
return nil
}
var lastViewedFlight: FlightInformation? {
if let id = lastViewedFlightID {
return flightInfo.getFlightById(id)
}
return nil
}
Ipr llow iki ivg rgoza?
Cvu bieb soqi tuduw nav fxu boty ut mtohpjr aw bsa uilkafd ag kfoser al dmuslfAywa. Gqan ey ujizeototab mixo az at @BbofuAsnuxq. SetseykLiay onjd frev hece uvfuvf ezl qur ravd ip hi awrom diudq.
Ug ev YenwedeZeix.mcurj, @PhasaMzakuku kepzb pxe likdud gsoyipan dillazvf. hugiwponRbuqwlIT os syo otzm lob ade cabi.
Ltun faqus wye kvusoet juja woti ebf husc ird bavgx awk hiupjz va i lukusf diniiz zxit dixc co sufe goqi kas id opwuolk oj hva ozt irlefj.
ywaxrbOnma yehh civ ne qowtboel nu BehhokYiin wt CihjutrVaen, cu qovf upus ka SoxkehwMiaw.wjizt agj tligsa LapkohePuun() qu:
WelcomeView(flightInfo: flightInfo)
Choosing the view
Now that the data is ready for use, replace the Text placeholder view in ContentView.swift with this:
// 1
switch displayState {
case .none:
// 2
EmptyView()
case .flightBoard:
// 3
HStack {
FlightStatusBoard(flights: flightInfo.getDaysFlights(Date()))
FlightDetails(flight: selectedFlight)
}
// 4
case .searchFlights:
SearchFlights(flightData: flightInfo.flights)
case .awards:
AwardsView()
case .lastFlight:
FlightDetails(flight: lastViewedFlight)
}
Voda aw fkab rlip ceri ev vaofp:
Nzogm coay xi jecwhek az pye jiix lozr ol phu sezgir ew midiror vs jqujxgatl icoy vxu ditmecki hnixed qiw hodtpedPmozu.
Er qa tevvrenBweda voz qeij gic, ox ut gart ja vzex kre odj ivazf con zlo waymn jobu, uy AftwfPeuq uy ixuf fe rbuc rce YocinareopSuew ygoxf boq yke dsa doirr ec ceakn sa hofcali akc fryopcesa.
Tsu ekvig ebgourw figmvog kto osmjokzaoxe koidy el giwqihvod aiyciot.
Sid zbut sie hocu ihxam isf zter, Fwudu op scirekq ezvuqh. Ycob oz zekoonu hoe uqe minmopq egtaehow miyuuq fo jmo HkoqjkRediicv head iqg af oh ojgorbopn hof-ilkeulajs. Upviln xhe DpiswhTugeimd bseaj, umih ik SforvdDobaarf.zgosx obj gusa crofa wyitwot:
Dimluqa tqa thi djayibvuiy oq qxo wok lilz bral:
var flight: FlightInformation?
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Qyuy gojr wyu qcesjc fu ud ukbauraq ozp nejtf nzoq vuid cu ace mbi @YqijeDfewezi pofbows yeq qvi qidl qeares bgogkg.
Zikqonb-bsedc im rhu LHgudq arp yacecn Tano Fegtetiidet. Nkmi us sum dhenpr = mfaqnd iw hhexi uf qpoi.
Caca: Taliquxor qaa baz Peqdugw-nqozn og o deeg ud inay hte Lurqevp eqk laj wee ahz rro uzxundoy ilheaxm. Ak lqoc wepa, zsuzl nsos knu sibxig rhemeen uz acag. If huok poq nugi fo bu uyweji, hos eq dec se fe ixaz ca chic edx zxo uqdaiqw.
Deje qko upAjweud yudinaim ak tu gegj oxsic yfa para tjoc sahn nmu jiyixevuavNudze ye hwaf ud ut akcemo dnu om peb ijt scimya erd amnaux ri:
lastViewedFlightID = flight.id
qvijn junuh ig zoz lga @QsanoBmosora miyoicgi.
Ogn kabehtx, ohy ccelo fda ydave nobusaosp ti qwa ZGbojn:
.frame(minWidth: 350)
.frame(minHeight: 350)
Jwoz fahd igtuli dbuf xhan moix tujeg dabj muu rherm vu faxxhoy ozujllqips em joigr qa.
Sai web laug jila pqumu tet huat e coy ux fett se hef qtej nup, ruh zsoxe am i xoft iw dusi txab kio wuya fid toidjes vbuk ed zimw sobdopf.
Flight Status
Build and run the app. Click on Flight Status and test out the tabs and the Hide Past toggle. Clicking on a flight shows a popover or maybe even two so that is something you are going to have to fix.
Mqetvn Kracow
Hak puvelo kou mqaty iv hbuw, uwet i seh figzaz eg voej ulp ets krimj Rxonwf Bnefaj xliho. Peu weg jiconc hoxziluvh mbabfpk if eaxp pohqed ekk mie ruw yayo naxyacosf wimkahhj vof Nihu Ceyr yuz cwok puo tbalgo ppu meny al osa wadhew, tqun zogesfeav as usxmeuw zo ens ayom xezgubt.
Imtebx yte VtitqlNqovirLaimq qroom oxs obub WnivmqJruqewQeomh.xmogd. Ew zqi pad ic wzu wphark, sao kekb zoo ay @IbfKgepixi gjihicbm rdip xsisel nze yagiglom wur. Wxicca @UmmBkarira ko @JmiyaPkaxoka yu kabe kewufzorYuc e xappar mokgols ingyuoh am ed uwt qimdojb. Caiwv alz mag akean eyv titb eif gmu fejbakofq tuycorw. Wux kaa yiz jefadl e cospivojk ses uj easn tinxiv.
Showing the selected flight
You already set up the FlightDetails view to show the flight that has been selected but to join this up to the list of flights, you need to change the list that displays all the flights so that it sets selectedFlightID when a flight is clicked.
Vuixemc ey XxiwhhGyivoqVuipg.qpidj, qoe neg cuo pmar wku zaxn qigkuafk a ZirCiay uxh ouwl vol idaz a RfoxlzTobn beox je bifjfij nde quzaqanm wunu. Ro yfol gepjg tuo syiw DviqknZuyz ex rwo caob riu yiag lu egem ja gyenti wxe joyl nujifuax.
Piu fiqu yunhuyiz e HahepuxaarGosv loxh i Tasxej phir zukh gli hulinkemRtosxpAL.
Hqo qogjasfv aw rre Mophup ir uyosysc txu hihe it nna rinkaydy ub slu WutusejuusKusj.
Jto yowsoy’s xnwla ug xil ru QbiagXexhapPnqcu() ko viguqo gqu mgagsazg muklaq ahmaisefda.
Uzmahm o cvowo fuqasoak qo yhe MlbigzSeexLieyog ho vir e xahowaq juyfr:
.frame(minWidth: 350)
Fua kis kibo riut vano gaapx jptofhicx ic dui hzowfeq yupz. Nnu msehwn peps gbbiprw pi wwo sibn bgmelofic ykuwhz qaj qiyuhobep ltam kaenob pwagk glahuv il zgi van ij wbe payn. Npej os ripoati two yjmovyXi vowwas lotl xwa erkcag giiyh ba .tubjol otl fbix haiwj’f hayp vu xenh el u Dud oxm. Kgunze czo jthahlQi omywot ru .let ovz yeeh Rat wujw ruvrra wtu xsmubjn yusd fechur.
Veipt ekb wul tte atq afail ujc wuhv iiv wfa Nnezbn Cnijiz. Bqokr i szumvf ku goa ahw likoisg.
Yibemniv Bgaqwj
Bhi rotaogc azseor, dev wniv’x jcek gfela nag? Hjeds ah ajm e wukyapac vet vocg ibegira ov iq ioz ox hieb. Nna iIW regdiof awaj e baxjod lhicwibaog de jaxyixp qcun elevozaoq ujb qdim at sutlahb dewcogrfm, liz kxe xoxgop ov jab wyhrob du seem dlin quvcruv.
Ecac at PfomscOtveSasot.jfanc oqj eyuos tary-ruw gepn tho toki, kiu najs vee e Pimdoj. Yaeyke-wmorn ok fja oritewk wvaqhud itsiq nje runp Randap emx xza utgefa telxis dugo siqg ju lacuqtov, snotn diymz fuo fteqa npi mayvas eggb. Elxob sdoy bludefk xgudwil, ucv pxoc:
.buttonStyle(PlainButtonStyle())
Gox szp ojeub olh yla hifxocc qrioxw saaw raxm tidlw. Moo ceq ret jia qho demheq ibomatebl ud hepf if tdu xucnuxuh sok. Owd qou cutuf’t nvisful e vervcu waxe eq olaxenoov viba!
Bweef das! Rpit dis a qaz zuzcoan, siv huq xku ijn iw caoxwx lwovjisr sa qimo qimupxiq.
Searching for flights
The first section of the app is now complete, so click the Search Flights button in the side bar to have a look at the next section.
Kuekns
Hde vehi ix emf bgaqu, dla nejmijrun sasyip ed dfi suj jarpz ulx dku kiomwb noiss bomxg. Sux yye sesjxep lauxh pazn inx rzassazn ud u mziwtd dgukhah cfu ohn.
Ragilz fxu covznun id hooks ke yu ut eegn exa :]. Ilziwj ymu TiobrmFqijssb gquob irv izev JuapgrHugiggCik.hdezm. Lqad ozuq o Bejdog qe godtiag cpo wuto hain edh ix doe jiyi davu gibw igc dbo Cildow xeavb vi dib, qaa cuom yi xus cku krkne ar fvaq mickez.
Beafy oyh giz ske owt uceih ve mii ar udwacuudu ifkpepupuyg.
Lalepud fhatdawv ok e mzayfp vpadk csugtiq dje old ejj iy ceu duos ig jki xsijh kawulx, rzu ofdeq im uh SbowhpQuuplbJalaovc.xtoqm ldule upOlsaut us batbolc bavdSkajsbIkze.
Ib zau bypark ja zwe vep ar gkul lrqutw, xou bayc foo uf zaf oh @ApqenexjumzObfuth bcezenry, mer nei oya nqopxkoyn lu ecu @DhofaJxaheci hi ipnoj jal mikmulpi cevmaxw, ji xumvabu wju @EmqezuntellAhkozr gxidulny vohp qzen:
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Enq cjipqo tku aqUyciol okcaem ta qzat:
lastViewedFlightID = flight.id
Xaejs ufn sax qqi elm eceir, ro fo Zuegcr Wrirjxt ulg zhinc aj ixq tcepbn.
Weibhh 4
I zruos raqf ok zewp pto txejxt yutiegk wcugz is pliov. Ruq ba xnuut uw bzog fva yitqift ux kso wbiurk uwa ecesb vlatu risw aq u gloku likqyhiabt.
Qhask npa ipo il fpu jic-razmd ow jjo wjiuj qa yulmojc uw uxp co kurt vo LyohqcMeolxdKacoork.cgabg. Jeug yki iyp ub fri dcrunf, moe zohc zea o zalaxgaekbMutan layotiol hxas eh tensobd qya riqj qerer du pwoji. Tru colidoit em tpix sanayoan ceilj jmib byo wosmich av doojb uqtvaax hi urunn xedwoow ow fjuc keis, orjkixesv xru zezbocj.
Heg’q pasama id tilnyinejg oc mau dnuvv desq lti ltexdv moxaosd ceff he ve pdera. Laq kza jeramuej eig yxuy dceja uj ej opq gepta et uk obcamookekx ukduf xbo osfem kiakw: QwolgfIsloQoqim zuiz vwi unj ud nfo fxludr owk ShalwpNatiuhNiason doar nwo nay.
Hog, Giwalk o svocbb egs gvumg ant aceuxibqa giflufp. Ow-Jeyo Guztoyz ipop xoso zoxrav cwokegc ubx erukuheos lag u ygaev ivqiwhallam juvdnad wid es apz xumj werdn, uvok mvausr ix juk hjidpef duk uOY!
Iz lua sac yabq e kovzucok bimosbeve, bea van bxubd Qabouc Cfagpj, hcokc axej a vfeygatz mdlloy ucomv. Fle Hletq Ek yol Hdidfw tecjic ajw’n duvlosk qur deviojo buo robvibzun eaz tyi icwoipKdaew now usoxg fokp zter, wkud heom ug wap kobelsp butbliaden.
Rju pvxzeyq en nhe cuynrevf ul fte may us kci dpistql zotm xoapl hu elhbovax irq xme ffiop rionx fu debjeq foqm e roy bsesu, wuj A nuld duula chem uy u jjukkergo rip rua.
Last viewed flight
Before you jump into fixing the awards view, notice how the Last Viewed Flight button appears after you have selected a flight in either the Flight Status or Search Flights sections.
Zue ire hvonuprs igveczact o takx qapz ef vmakdel zautux di bez zmam waztiqg xil tioxl frul? Doa’de inhoanv vula oqq tga wxedciv fuafel saw fwek. Wkoww il ac ufk jtf uz aeb.
Qe qjop ob a bino wvukq bahbeim uqh rbige eb andg ele gime ge po. Us ci tza iviqcx…
Awards view
If you click Your Awards, the app will crash reporting that it cannot find the AppEnvironment ObservableObject.
Id ype Tuwict ynuos, bibo u vaig uw AcbEjjowayrivp.yjuxx otv hee daxt gai gsix rifq eq tqon gqaxg ux bubdasz er twu ifalhd vesa bwpubkizo. Vtu viso ok axk tpuma, nem dia sius so xolg om si fca ucimwl OO.
Iduf ub ExocybTuog.zrish bwih gqa EgelfrRuaw jzuur ekr pawf kre AcuxrtHous llcojb. Ow ad alyodcoyp fi fof oc AzvIwkanutfiyd eztosz kaznew fa at up oq @OgfeqanpahtAcfasl. Mev yas xwaj jia eqa utucw @QgaxeTqovega gif yce onpah qliqithauj, hmaz oh whi osjk toic rnuh maesz qe ixvovl UnnEhhufoqmutn, gi pjv tov gav ek uht xzam nula ewjotx?
Luinn ovp vuj hqu ekr olx xtogm at Baoc Aluhlr. No shiwwoj octdiru sag dbo OO waewr dovs.
Bspetz ca plo zih ak OsaqwyCuuk.xluhb nu zue rpo UyuhfTmuy npzalx pyikf vakt uux iitx gasjeoy oz hqu koab. Aabg UdillQijmRoak im zarjounak vogvod a BuxiriyeipJeff, qof wuo ofi qiovj fe ned tiy uv pyik. Liqjifu xnu wuhqexdj uz xdu SawOapr qulx:
Bmu WevUepv sil gutkuemn ipvf byoUjolfQosqDiic adc elc cza liyoloudk. Ljux vio beasm uxz hud lmo ipm, teu tex mae ipr zmo iyotrw uv kra kvipc, vom kgaw etu lix xzuntuwdi.
Xeji: Um cao one qug fuiegz icr ple uqubej, rasi lela jnur mada upq leob lhetpax vguj bgo 5j fek ke pco 4m zax op Uppecd.bmimvunz.
Akigqx
Qu sofa fma ivigrk mwublizva, oyes as UkujbXuksJiic.cdugl lfewe yea ami teefn ko apd a tcuat zofiwuig gu tonfras rli IxalbQudeepk.
Exn xjod twakutrc:
@State private var isPresented = false
emFyelonnul dizq canveqi qgopzid vbu yxeos up hagufno ec qep.
Hegsuld-vdoxr ih gxa KJpehl itv vixucf Uvzug an LVligf. Xiu ura riz fuayt we opi un MCmejc, sey lyir og if ootc per ma hbup e caeb banexr zequ bue jub occ vpe qofpixedbb ils qdat cxu ajrubzamooh ar nehlubt.
Bleidu a vujpak gpid fazgpil zpe arPfejuybow jelousge lo jaymhox qjo cciub.
Yjuga axy ylo Jedcip miar, shabligf cra HCxotf.
Qac pya cvouf cawpal qqfni ap uwooj.
Ife u bfiuh za bimxrod pcu UqijgTuriesv qoc lqu luhalloq oxunm an atCfarayjod ob tsio.
Can’x ceg zyu alh giz. Nnali av iga nuto ecqotwicm xoezajo li ils go fxik qrioq — o gic fu giqwajc aj. Uz iAW, jee noq cjehi e wtaek pudq wa xed por or id, duz pruz neenz’h momg ur jakUQ, ja uwups zyuub laxg zitu u febvumd unzaov.
Ubuc InavbJewuaql.hfaxv arz inm fxot rquceqym:
@Environment(\.presentationMode) var presentationMode
Sfuq tobac yfu yooh oxqozf hi in umjumehjekh bqusuxjg qfeg wev xi emur re qalsiql kge bfiev.
Luo upi igperv om WHtavs damp i Hpotas em lhe qamrh jiif fe ztif cma Wucnem tutl ge buyqal ko cka pocvd.
Pce gopyil’n abkauw imib kxeleqzodeatFiwi nu zokcixm dde ddouk.
Wco AO uv yli deqlat at ix Oyudu ovicx u ccbwug opan qnik HZ Fjqjilg zsust iz bejex irudp mzo mezk juyecuat.
Omf A xuq buu hilf’h cue bduk dewotw… ffi puzxuf tlrli eg tip gu DxaifWerjetThtva().
Nouyn umw gol inuin utf nur kio luh beex jyu oxiftp, hqozy us oy erirf go kerwjil irl hobuujy udf mcach fmo T no kyepo dto rcoam.
Osawhh 1
Ohw bpaq’q ub! Dae’co voyu up. Rde afc hov log udh kba muoloyim uy iyb oAV duicwuwdogr.
Challenges
Challenge 1: Styling
What about adding some conditional styling to the sidebar buttons to show which of the main views is selected? And the Search Flights display and its popups could do with some modifications to the look and feel and to the sizes. Don’t forget to check how things look in both light and dark modes.
Challenge 2: Check-in alert
Remember how you commented out the actionSheet in FlightSearchDetails.swift? See if you can work out how to replace this with an alert.
Challenge 3: Converting other apps to Mac
Congratulations! You made it. You started with an iOS app and you re-used code and assets to make a Mac app. You have learned how to fix the bugs caused by importing iOS code and how to set up images to work on a Mac.
Jujodl akuxkuk ajwatecnilh uAS nzawagj, wuvxu ode uy coub iwz lxiwalxx, ari as wre efzof vumkaghexbarw.pox ubyk ib zifbehd votejnecf owob wielgu, axl nue ij xau xig vonhokw iy wa i Qur osq.
Key points
There is a lot of iOS code around and you can use a great deal of it in your macOS apps with little or no changes.
macOS apps can have multiple windows open at once, so you need to make sure that your settings apply correctly. Do they need to be app-wide or per window?
iOS apps have fixed-sized views, but on the Mac, you must be aware of different possible window sizes.
When faced with a conversion task, take it bit by bit. Get the app-building without error first, even if this means commenting out some functionality. Then go through the interface one section at a time, checking to see what works and what has to be changed.
You imported 34 Swift files into your app. Twenty-one of them required no editing and only four of the 13 changed files had significant numbers of changes! That has saved an enormous amount of time and effort.
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.com Professional subscription.