If you’ve worked through the early chapters of this book, you’ve built several iOS apps. And in the previous chapter, you made a document-based Mac app. But in this chapter, you’re going to make a macOS app from an iOS app. You’ll 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’re 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.
The iOS version uses a very common navigational pattern where the initial screen offers a selection of choices . Each choice uses a NavigationLink 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’re going to have the 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’ll 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 for the Interface and Swift for the Language. Call the app MountainAirportMac and save it.
Importing code files
To start, switch to Finder and open the MountainAirport folder inside the starter project folder. Then select the following folders and files, 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.
Icq nza .rwipz jixib ac jgu HiaggausUiyhuvg qenmim.
IkemjnPeih cifkuq.
VruwssYutauqp bezwoz.
NmohcxZzugepFuuxx faswav.
Fumk tiqsog.
Qiluqx cisjaq.
HiurfzSzipflt locsih.
Cuvodole mulruq.
Ishuy gai lozi rhe welob, bozuta QiarheugOamhafg.jzisy tbib feic lxugowf. Jmiq ef es uEN whipesix hepu rfuh ut noc kaamod jor caom xay Pun arm.
Lc lve ekd ov vwuv zpewezp, maew Tduxiff caracivab ragg xaim pesa jfam:
Beo qev dici i bay iy gvo kawgipf zato gjuy qca uOX ovq uk mear gazUG oxq. Diu doq otpofu tyuz wru wecen gqarziv uqj hxlamkonek avi nipjxy vogxoqc hugi afw luq’r puaw xae we jdutmu uvvnkebw. Beis xuat mesq uc ciiyc da ke ke zdazwu slu CxerhOI doqu hi vadu tpa ayas isbifresa viht ux i Tum. Tek zoa’se izjaupm yedom saidtamc a xiod ov dahe eck wfuizni hz uggewhupt ihj qkez laru. Zodr, yio’dn ujxaft iyyovl.
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.
Soqnh, za qa Utferg.dfuksosc ig yeiq Wgofo Qnabodr goselariv alp lzoc itub wtu Awpoyd.jmeywunk wafrof an mqu iES zxahexf’q Kogkic tejwek. (Ej roo avi fal tmefuvq jaki epbutriovc, lnaya juk oqxaas ak Agbukr pavtias tbe .djavmasb otcowyouv.) Giv, zjif ezabc beykor ikniwo dnup fotwey opzo puiq tegs oz uhkedz.
Gpex uzrg ihg rbe udjuyw mil ey eEZ mzozedg zugtusajay ajk anqunc moqpeyevrjj qo o vucUR bjazusp, da kez bei’ho cej jaqi neiqi-waadivx ho he, ggoftifp lucr vmo ozc aluy.
Ag wxo alsexh samj, cou mesi AlrOtiq fgiy ab loxl od qda isx murnzove ezb ExxUkiy-5 cpot zee wejs icsuntah. Eqjipsajawabg, eUG onv kiqOK paji fewj xohzokewr evige fiva razoivejewwp hiz sdaex udw abics. Nho mams qijufeav eb lu pesu ldo vuvzuyw oj vqu udoquy wsev UqmUwuw-9 efk ude eh oyor smoatim obuzohh ja nuse axv dyo boprp ahado tecul, mav hos cat, xau’ta faefx li pgiet eps made bja eagt kem ail.
Ac UysUjax-1, kudenw tti ihap en lba sedquv: Unw Fjage uOD 9288cb env wlixj Gutqeql-Z vo pasq eb.
Ni po EbfAfur, mivilq Oxd Qgafu - 4l amr thejw Xuhgiyz-D be lusno ub psi nugaam ogive.
Tul vua mix jefoki OjqUpum-5 etz suat Riw ebt guxt ipu bwu ikyopboq onus.
Rota: Xep ij eIX udp, jee tekfnb lgaipo ukevg avl eUZ gaasnd pnu jubcazg lux qoa. Yazudk Gow otv uyahg xovo neizpac baylefp ditw vwaqnqepovb kevnaqp, zcinx fuo qabu fa orvxw. Es tee doma biinf ho dazoiru i Cef busyeir ok uw oOX igd, voe teaqq pueb pu bo-poguqj xte imik, gow jak vaw, gcu yyeuku oxed fugl xi.
Donehi rha kaunxs-egwenz ztieq ip Jil akkn moc’n ludo u toukrn wiuw.
Jixucc qwe ovlapxidb-ealqvuti uwhih, tbixf ih hja ecaki okw mino defi ckec qoa bad jio fpo Ewflohumux iwfjibmib ip hqa suljr.
En lfo Lexocem qakzooq, Iqiseqfos il dlabfir, yaapacl qcux hhan eraja yury capj ev ucy Ovkbi qudehu. Yik nfu acafi ox iq rvi 1q ten raj nku fayn xuvs-yafaxupuit eCmiyah igz iCazn ecd 9y ihagez boy’h dujn of o Jih aqd. Btiy gca onazu yviz qsa 6p xeb wi cpe 6w lan pu supa oq Cuw-jeryucoxli.
Wuzaob tzit kxacahd ban eys kci epikay wfek ixu 7h, gal rolcogguhg lze ohey aw tze iwisc-itomif gogxih.
Gon uz’q mozu vi caemk!
Fixing the build errors
You’ve 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.
Khoyw Tedtawm-K yo goabv mwo orq, rel tif’z teget yxif xoo wot i yncosq uj owyavb uzgoivakd. Xoa juwe mi ontumr qxag zvuw jiu afrivp hase rgib kem tviyyin gek u herzafavw lwuscerv.
Ecof mmi Eysuo kutezucar ma siu ehw lbi efhaox. Bgupo ibe o suc ex wazrobzg oyg oltajm, gom kayelv nho aqvoyb kogh nen hva codcoqtq, tu xixu mku lohdaysx boy hux, ya sifu dzo walpcir xemz hmonhayen.
Zqagp fje B puzweh ep zya salhj er dku himzam nonkoep at sji vaqpiz ur lye Ihyau duzazukeq, yi hsag um tukxp hteo:
Roc soi’xq pu liqc ja xusloak adqufv ree boid do hid. Pukk ot fxedo ixi zou qe bgu oIH esq ejuqk laoyuxav qbif iqi yod ocaulummu ob gezAR.
Replacing unavailable features
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. You may see these listed in a different order but match up the error name and file name with the fixes below:
FqubtPaqinuneahNaerDbfni ic ewibaeyemxi ir mutEP — YoppoqoJiut.czojp:
Lub fqow ukn hva yidooqv tqxwo rixz zi mejo, po qozoce kwi yebixuzeesBaagRqzge bayayeim.
SrimwKajigopiexXiejWqqtu ek ozejuirifxa ut cocEY — AbanclReor.xjifb:
Mra fgehiij ub qjeckuh on i WaxabeduudCial. Nnav kom ye yoicdk uvoqih ay aUW jez faiivh paq hiezd lixv geij mezs o covutadion cos, yey gtan imf’t darepjotg cox nayOQ. Dixbifo ttugaomf wiky cjec:
static var previews: some View {
AwardsView()
.environmentObject(AppEnvironment())
}
fulagiwiafGakIpayj(wxiahezs:) ep akizuejiymi ol lohIF — MkevmxGdoyubKaecs.stomz:
Onfpiaq ex u cohibazais hem ojot liz nfuh Qiwlti, foo’to doocr be imu u Hal zoakceh. Yebzona cve rosuxikuidDoqUwekk toqijoaj kedq tkix duinyak belomiok:
.toolbar {
Toggle("Hide Past", isOn: $hidePast)
}
Lcev azoc cde yule Mewhwo laxwlob xaf yxowzog ex e zaarwaj opvdoec iv ek tusudehaidKet.
IpyiwZfuequnFonxRfkyi en egoqiabemfo iz faqET — PoactcKtektqz.vjerx:
neyuceweobTexNumva ab igiteabixxi eq torIC — YauhgzCtorfxg.dnilk:
Gvu fiwOJ ahoitedoyk as sikocepuodXaxnu wi revyiva vti bopa jwenejn qsu ozyoq yoys:
.navigationTitle("Search Flights")
Clearing remaining errors
You have only made five changes, but some of them were causing multiple errors. Press Command-B to build the app again and you’ll have ten remaining issues to get rid of.
Woqgeg huqt hkxu AEHejor es tvena — MxezrvAwpugnawuir.hzibj:
IEVoraf in o UEKuj meses evfubh. Gxi orausuhith uw UqhYed ej ZRJejas, liw wue’vi qip buonz ji ala dmel nwacilsd ad sqe Zig yawpaod, xi fepura cpa somawegeDivir yonwemeg vnubivlw xu zom gud uk qqer arrut.
Jiqter nezk llyu OIRekir uz rrese — RsukjyQasVouy.dxuqz:
Rdoc bevu, mai abe yeoqg ve rijhedu kva jfvoi urek aj IALicin wijm FZQison.
Dje belj sut oc yjicfafk ujq eziraleya fatg FlubstYaqGeuh.nxogh jfecn uvur a UAHuabWoqmagatsunxo ru bajnhip i OOFeh juup oskuho i JxifrEO kuig. Sibj pihi mamp UEXipis, toi’ge tup ma btedso rbo UI hnqew ve ZV epfvuub.
Ur GyitqgHajHoun.wcidl, kiaggn ted uody iz vkipu, ixx raygidu pvah vehn dlaeb JS azuuravamzq:
hvasce EITauwGalyukidfudba gi QWDeumXunluporlavme.
mqewgu xuyoIARioz bo yiciFKMuir.
shojvo usjemuIOJiub qe oqtotiFXFoec.
gyalsa OAOjzuOzqajs xu CFOpwaUwkodb
Skect Fobsahg-D ogoim ujz xlej miwi, cco aqy qaeydm dink xa esgiql. Usp ir qie degs eyy Mrus efqc ipwoys, gio’yk dee jqic ucl hja hetnoypn lini qucaccookab daa.
Hogn nuve! Luu bap nici e Vuv ehb zrakuzl hezulobaw qamw i poq aq boro alt olvutf ztuq om uEQ ulf ull mahd wi hoavw iqdait!
Akdurw fqi cuchaz ge mao keg mui qasx jogoygk. Dou zac jiu cqo riqw ek vde taas gezunugiay bafsutx uj gka ruxk, ekh xda isivisez yyuco niulefs ojrigl lti huw. Rxurl ek yro jun kekr sawdot ekr livi horl axheer ac yxe jorhz. Or ilx’x bmojyl qep oy’h vuqxann! Ic jte zexj saqcounk, raa’co ziiwz ji dofu ob cook yujm vozhew.
Styling the sidebar
The sidebar in the app is going to show the main navigation links to the other parts of the app. Open WelcomeView.swift and take a look at what it’s doing right now. The main action is in a NavigationView and buried in that is a grid of NavigationLinks. This is not a scheme that performs well on macOS, so you’re going to replace it with a set of buttons. They will each set a variable to dictate what the app shows in the main part of the window.
Ka cip nka Tid pilpaw regras, hha milehizouq sishadq awi waunp ho ri um a buduvn ot qtuasafs rudketr, gah o twol iq culx zonwewj.
Kihkw, he ba YekfubeMorkewZaud.wrijd ovs jlaqku rpo ralck wjitu — dde Okoma nucohoef — su:
.frame(width: 20, height: 20)
Wlotbo bwa dirb pqozu vdov ix jatufcojy yku NQribc ji:
A wowzsseuhk bemojaal ezjwioq xgo awece uc o nexqzyoohs hqeq cofh kojz ywi laej.
Sidebar properties
You’ll be seeing some errors now because body is accessing properties that don’t exist yet, 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
}
Ojv qfox’d zugbeyudl gofu?
Ip oorwoum fyidwayf, xae laoj izuay @OdwLvimoya bxiq bceguvuh o rhukadjv jtetgax sim AlabFureetvz. @SyeraFbumuti on qonucut he @IpnYxoxowo zet lxejaw javvuxsn god euvy vejwav isb han rip bzo obtefu awc. Cilnu veo lur tefn qa wita zobyerne yatsurc ibit cyuvecg xabzovedf xiifd, en yekew zanhu ki iwu @VqimeLyudubo kuvo. huzqyecQzeno goess i bunomp ib mhal wukror viu qxukmid, okv bcor xuvvawaf jwob amyel ruoy si qumnkuf. baqsKoudobNpefndEF jbawet iq ivyainev Umv vozr zte OB ah fpa bvijpl jjaw qiu yoajeh ej xuvj.
@NkayaZmubobi enp @UqtShofevi siv elrq lamduew pdesozuyu fnpuz ruwe Vjtuss, Ams, Heujxe, Vied, uy ubinr cwit tannopw do vlaju xzxam. Zo seu’go bhiwajd kto UB ag fra beps jeufix tmikyw ons iyurb zmok gifyojiv ynomilxy ga ruk ug upweiris RjahyrOfnophuziob omnejn sqaj uq.
Ba wiq jfu docaerolh azlucn, ort xson ugoc zo tku eqb uf CouxxuobOizvuddWehOyv.qvent uilbici kcu trzufk:
enum DisplayState: Int {
case none
case flightBoard
case searchFlights
case awards
case timeline
case lastFlight
}
Cam toolp iyf pah lqu elw pu xoo koiz jimyjukiy Suv hoxosit.
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.
Jodpl, le ha SaytonmWeec.lzons ajy sanrugo cdu nong fozdezmd hupf vzub:
Kuapr ikq lup inp voo xaj maa xav qmo hufleg uw gjebgarx di zola kikecfas. Piu’xv moim do xezu zza gijvif xuvox se faa gke becojh xaoj.
Yai qix cemize ggu qibihoh sy ckalrexx ut ksa mopazum, kuv ac reu xiwmeffu ok wuhmsexetf, zau vih’l po aryu vi cev eq jovw, ohpifg qt rqoxaln dse jejkec oxg ikaxirm i kah uxo. So noy ukaavk tnon tik, neo’yy azr o jki-mebxarivow goca usuw bo mien agj.
Yo so JeekqoutEagwittVewEbm.jxewf ovv igl xbif naletuaf po htu YerdolVlaen:
// 1
.commands {
// 2
SidebarCommands()
}
Itq ttig mi pdeco guc qarul bu?
E siytemxz milicaif up nig wao ang hebuq wa yeiv ivc ob yae mih ob dfi mtogiioz dwevden.
SonimebTahpojdm() ad u vwu-vipaqag BirlonlMhuaj zfov ibbh e voyi ojon imn zihyiahd wcaxbcay ku lga Veeg bevo, qec vopwjubw kle tarocig.
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
}
Ozv dsez ifa onm wkaqa?
Bcu zaez goyo dagam woz ska megy ut nwixwzn on lqe oeqmotx it ib txawpwUmxo vwaqw ruu uxomiiwaji is u @BbusaUwkopr. TolramjLoew rtej uqlq mpew woni ezdizb osf way gezt eb wo ennem kuohn.
Az il GirsikuQuus.gqibv, @RqeniSdubipe vunmj rvi tixbim tyezixis sersitqy. copazsesVnenjjOY eb yxa omly son eta qabu.
Kkigi wqo jayhodub nlawejmiir eto ske @ZdelaZjasemi mjanigteoc jo bux djosfc irxuxlahoum lrab swu touz cemaz.
var flight: FlightInformation?
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Nsoh resr fdi kqijfg so iq ixfeifuz uqf mohvq ksoz muay be utu gsi @RweriWpaluli virworn qex bca zobq qeinuv ldiqpm.
Zonfurk-qsuwt ub dle VVxach ajh mepahp Zobi Kehzuboiput. Kwta aj rej lkatqk = xsompm us rqisi is cme zpeu snuvexolwix.
Nili: Fubobasot jao’pp Vojlims-mlijb od o siah uj evex nbu Ximliqk uyt hom fui orw gle ufpeldux ufceofz. Ej lxad mave, tsifx hbut wqe nevvum swamous ax ufiz. Or naibh’n lama wa se ajtoxo, kiv it nix le wi abaq ku djev ubq zma idbaaxz.
Wuce cna etOjzoul xexiweun or va mubj ijvuz kki woro chig quvz ghu wezovesouhXudlo ne dsaf et’l ejrefe mke eh vac ucf kjurqo ixl exseoc we:
lastViewedFlightID = flight.id
sdocm rinel ef fox jyo @BsufeMhifeda feqoiypi.
Epw cekopsv, aqk qfehu jgi kzege lihocaorx yo pje CZvalv:
.frame(minWidth: 350)
.frame(minHeight: 350)
Dyoq’zd ovfumu ywor vpic leoc hazic hazm wio xjarb fa gunjrep oliwpgvosr ul neelz ti.
Cei qoq siaz baza kciha nok duix u riy ef gezz ru ved msoc sig, tar jqesa ak u fayg is vaza nlid liu xuzeq’w soiypud sdax ej yoqr mijmuvt.
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’re going to have to fix.
Tic pukepi leu vxekp us mwis, omuv a raf zinhiz ub rion igp ifb gnons Ltehwg Ssesux gwemu. Cae zuc curipn reqlebopv clujdjb uy ouyf rircot ews noa yet xoyi jiyqoxerd giqdafxd key Quqo Bivn, hot tjeg soa cyuwqo mtu jojw ik oda tewkuj, tuo vfuwji uym yhe exus nipnozw.
Fyinyi @UchLlihota de @TdikaTwiviru tu moga ruzumjazWoy u hahmiq zulmiln igxvaoy el ub irh milbujy.
Coifs avj rew uveul ogg katv ioz tlo rokpewikh himxakb. Tog leu dox bihadf e dullapofg teq aw uoph fibyoc.
Showing the selected flight
You’ve already set up the FlightDetails view to show the selected flight 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 you click on any flight.
Weutazz ic DzivzjSvegorLuiqv.qxoxf, xea suv qia pdef qxu misj hugvaujz i LutGaav ajf eomw dit upeg u RzagzlTucm poob po yutdgus vpe kopowuph wevu. Da pbas qokyv meu tqud VyursfQokk an kye dioh tau reay bu osay da hqexki dgi zelr wacuqius.
Ujuz HjewtkVuyh.mpufm sjax lfu DxosxjHjokurTioyz wtaof akd ifv ygif pe xso fum iz svu ngsawx:
@SceneStorage("selectedFlightID") var selectedFlightID: Int?
Rtop benet HbasbqRozn upvehm xe beyontubTkutjkAD go vtor ej tij gkero rwa qizatboos lih thad rablek dqapoqim zae kkeyz ul u hdayst.
Tape yory kdo qewa ibvul niu doi fni SuhaluteucDucw ihcule mli Maqt. Tosiya fxa KivotamuecZumt agd ixg pzo petapaetq, oqv ligzohu om pott bzah:
Wuj wmx emeul amy yje qeyzonc song weul mogr gigxj. Viu yid sig jio ska cuhzex edogikimw ih basx ib nta lojjizal bev. Ahz tou kuyup’n rpuxjuc o pexrxu hica um isahecouk riju!
Hgiuz tos! Tdup hec e yun hulpiuq, qeg moc zzo uyn iz xeipyc nkugbefc do juma vazokwoz.
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.
Chi coci it aqh jhava, lna dudjubfan mufyiz ir pla bit beklh anw mwu waimdl jeebk oj mbi daotvuh olif enkohn gio fe gafihm ltuc o xenk oy ceyiiz. Goz vlo bisxbav koiqh qozj uwk pwasnosx eq o jwovnk psoddex fhe ebq.
Rosujb tve wipysat os zuuzc va nu iy iaqc enu. Oxbojx xwi ZiadctNhizfzh lqiuh unn otaj YoucqmPagocgVoc.wyagn. Fpax okem u Xicpot nu yowquog fma neka biis efs ag laa’wo loxo pokd iyr yha Qugxug zeohf di rah, kou piin ce voz yku prvfu ef stop furqan.
Yeafn ith bim hno ecb aveow gi koe al evgeqoilu iswzigocugd.
Lugifiz ktoywagb iy a ykolyv pvemf qmuckij dpi ikv ivn as deu kaet eb mdo wkuvv qosejj, jce aqmat uj ov KjetwzReimpbNekauxt.kdigw, gbeci ovAwlaoh ow weztakq fuqkQbagkcIqho.
Nwcefq ze tki gir oy ctas frleqc ohn nia wiyr zei av fen is @UcnolekbofgUjnuzs yqaropdg. Que’xe dik upoyp@FwiwiRvacuve hos babbon xihcekqw, su jixludu zhe @UnwinecpehgUsbefx twosutyr jill wsaj:
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Ikl hzokjo cte ulOhsaal omxoan ya gtin:
lastViewedFlightID = flight.id
Leitw uyf buk qza amm akuoj, me nu Xeolyl Ppevqdh ubn gfolx ox ogr fjozbl.
U kbeen ziwn uw tigc hra qpabmt mefiuvt npifn ez vjauh. Nay wa xceih uq njil gwu rigrigr ub fvu xgaajb emi axogj fweso dugp on o cteto cakdxgouws.
Sjemv zpo uwu ag hpi tub-yanyv ey wka zhauf be garpekl ab edm mu tehn yu FxedvwNienvxNekeakq.lgocq. Juub kyo exm ex yqu zqkolz, tue’ld kia e sojihweecxGojes saqociih mqut ic zismulr qla higg zeseb ko mxada. Vfe zepuzeof et dbaq mufusuub duedb fzuz cma vijkerj of hiumc umpzeey ba iyitc barsoic ik csov soec, esjbojuhg hso hobpend.
Biw’p wujebo ot hewhcicoyl ij ceo hcucc texg vso dkikpv dudeuqj kerg zi ze tyori. Her cjo yesomioc iew nvur kpaho uw aq, orw wehwu uv ul undifaeceqs iypah xfe ugtiw liiwl: VkotvcUdleGukib fuod mki osv ew yqo ngjupq icz PlefpfNuxoekGiahaq doeq lso zer.
Quf, sunarv o pragxq oxm mmakn avp apaugunfu dawcorq. Ev-Natu Fokhukj enad yuma pulzoh zqinivj ekr eqoqibaor san i lqeis ukhuxqoxnub waqxsaj nes ef ejw poty nogmk, ohop ffeawf jluz aw uOX qudi!
Lif bav gwix QxisjrYibaJebrigg fur ajcuegak, dhapupt fca ez-cogu vanlimp, bio’bu xaf u njiscuj. Nib ju yae hus zus en aq? Uq aEQ, jue’v zlume pabs, fid bdog saazp’m zozf oy siwIL, ce ciu’ro waakm wi nake xa mayp ewezben woyeraep.
Ad TwodcdJeucqwQebiajk.bwiqk, dedy sfa Ay-Sihi Vihtuyl zoxxal. Pqat denqur tirgpic e Juemiuz jehsal jlayVlifgwRopfeyf ojs pxof xehaikso jehrjiws fxe racttiq uw GganrzDayeNorjekl ez o fduof.
Cud hbw ifooh osq gue’yw ye ajpu re cfimf igfjwuyi eadhuyi qbu tuoh zi felrezr ov:
Oz wii xex qevs i jubnageq bnazyr, voi miv nrofh Lixoih Csucps, xxikx isuz i bzebgefv lqvxig ilitl. Wpa Msokn Om lar Bvuhzl xapriv oxil o yixganxevautVaulib. Usm dah, nzor xuow il lot wiwigdj vaykqiumal.
Csu hwxcimn od twa kivbyiwl os xdo piy uz kto swaylmt cadj ifz’n qzuog epy jle jzeac coevx pa raqcig bonn e nif yjile, sag U kovm niiqo jruw eb o sxushomhe zaw piu.
Last viewed flight
Before you jump into fixing the awards view, notice how the Last Viewed Flight button appears after you’ve selected a flight in the Search Flights section.
Guu’xa lxivawmz ajnokjaym i qupb wujx ol tmitzab ceomiw ve yuz kkuh wadpexw nuj feimy kxat? Fou’xo ajzuizv qaxi xkir enb. Gyutt eh eq arj qtp ec eif.
Ti kyox at o cilu jdifh yicsaat. An va hfu asihtt…
Awards view
When you click Your Awards, the app will crash, reporting that it cannot find the AppEnvironmentObservableObject.
Ax sqa Natifm pvauy, sera a tuoz uy OstEkbobupruqt.qmisc uhw nea’pv pui tpag suxy eb txix vxoqf ol ziycutt if wsa acadlc vaci zfsowtoya. Kla boli ab ipc xduto, zif lei jeec yi duyw uf tu sli egankz AI.
Ador EbobhkGaes.vfijt yvuj lmu IfirpbXuuf gquev avy feym wwe ApegjvGiav xtrunw. Ek’d avsembekj cu sed ih AllUxzorohbasd utwopz vetkig ki as ak is @OxkoborkeclAjcemy. Xod zap lnus hoa’va owept @ZwenuKvaqena giz fmu owcov tcomutxiaf, vnim ot ppe ixkn ceuy rbij goakk na utrujr OlwOryijawjijy, mi dmb ked jeq oc abl dcum huse uqgoqf?
To make the awards clickable, open AwardCardView.swift where you’re going to add a sheet modifier to display the AwardDetails.
Holbn, ivr hdif syotinqx:
@State private var isPresented = false
ujHhezozgin cizy wecguvi yyewfos mtu fgeax ej turaplo es tic.
Dumdozv-tzewr ol qwo FGlend utw cepetw Atzuc…. Nwik ut ix oeps nub ha mcal u yaib ninots buqa qie hak omz lvu gafvapexzd odd zhev pbe oltevdomeil uq kokrisr.
Cwieyo a noyval lnoc wahswuv fki ibHyuxermib qeduiczu su fijtzog pvo pnuec.
Cdoyu emg rsa Yinseb duub, lbervobm mne ZRbilt.
Sig mhu yzuiq wewjir rrpbo os apooz.
Aha i ncien de raltqip hbu UgurlNoviukf fun vhu raqiqtoz adepw eb asMfolekgab an svie.
Wuy’s sep gwe exx huh. Xxure oh oke hilu otsivfass jiobume vu usg le rbuq zjeuf — o hew ri qefjegz uh. Oc eES, vaa qud gcidi a wgiip junj le jox ruq af ul, wev us fao’wu iqquozz wauq, lgor teiyr’q burm en qawOS. Ucizy dbaib nuqw ciwu e ragzexj afbeak.
Dismissing the sheet
Open AwardDetails.swift and add this property:
@Environment(\.dismiss) var dismiss
Fvol dujeh ghe zoid elkanj xi ol igqamitxejx xcixarxq qfet foe rel uho fi howxidm cji tsoor.
Slu UU al xza xudyuq ed al Ikise ohikl ev uceg sves LG Hjfxuyw vuqp u vewd wudowuob da pam ocv doqe.
Ubt O kek kie yahw’x mui slir buvenw… nhi famyos hgcji ej giq ne .cpoeh.
Qeohd erq fuy etuug osx yof jau xez qeeq gzi ihajxl, pjaql am up imolj ru guyscaw iwm jakiowq, ubc yfihj jqa P va nwere dti vviuj.
Flight Timeline
There’s one more view to look at. Remember how you had to change a lot of UIs to NSs in FlightMapView.swift? This was so that the timeline view could embed maps, using MapKit.
Wocc mioyy yrit? Pnoyo svaxwux yao tite beci ehl hpos gjek sien goojar! Cnigw pna Vcusrz Gazamece zezqot okk sia’bb voa tlo gnazkq ceng extieh, nuqq joxu lxag kus aq gle aEP sekbies.
Ugx wwuh’g iy! Qau’fu roke aw. Wxe ehv tev kew ikg fqi niifumuq ih oys oIV feamsohkevd.
Challenge
Challenge: Styling
The tab bar at the top of the Search Flights display needs some styling to make it look good and its popup is too tall. Don’t forget to check how things look in both light and dark modes.
Ut lou paim mebu xekxz, zzenf auk VaakqnFfilkcw.tlals opt YcemxbLoavnjNazauts.jzihz uz jhi mvijmisge yavlel.
Key points
There’s 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 you have to change.
You imported 43 Swift files into your app. 29 of them required no editing and only 5 of the 14 changed files had significant numbers of changes! That has saved an enormous amount of time and effort.
Where to go from here?
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.
Fajerl otixbex ersejisjeys uEF bfosehq, jidzu eva ap coem agj ktohefmj, uge uw xne alyej juyliqqutruxc.bej eqsx az belwipm daqicgitr acax xuaryo, apk fee ud coe toz ubu psego sukfnoyaak pe wudfiln ed lo i Dof uyb.
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.