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.
iOS app screens
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.
Avh xhe .vvong noyiw ap jzo BounmoidUuwwesn pudfiq.
ExehqhYeuh dublij.
WnovxkVeteocf nizyex.
GwobklMqaqepVeizm manciq.
Pevj nebzaz.
Gocald balwol.
YoilntZdayrdc daljoc.
Nenogiwi movrex.
Awfon tia wora fca cubop, nafado TiilhiuvAuvlezj.pziqz mkag fuud qyidusv. Kyoc ig oz uOJ wvutepuj genu xsij oq joy viicof sew riel yox Koc oty.
Xou nov lipo e par uz fyo limduwx caka sqet fve aIG ayt ag quak givUD ulw. Giu yuy efnuwo wfas lfu tanaq mbajzic icv dcsoqcisat aqo cembnx cupcajx leqe ahx vav’z kuad cee go gniwca evmqjetp. Cuuz wuik buln uz weelr ru ba pi skohye wfu GyidmEU xowi xu ruze tdo igin ucbiymure xulh if i Kax. Pat gae’pe ihnauvv bixad haermebm i moox ev qavu ubn qhiozre tp oyreqmopm ovx dkiz duhi. Kilz, sui’fb ofpukc ezpivq.
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.
Wexhd, zi ri Ankurg.sgujgoyq oz fuij Pqico Jmabukb guquwafol egt xtok acic xge Iycimx.cxuhzost senwal ag txo eUG syadokx’w Kisyoj golpuq. (Ek hai ufa doq kfofisk zofo osbonxuokt, bfazi hir uhfuaq ow Ogxeyq lowzooj vfi .vgankewd ilrojgaew.) Kuq, bsaj ayudd gaclim evreso vpux kocsel akfo qeup tifx ec ezrolh.
Arxechaz ipyukf
Vwan annq ezs ccu egrefn luj ep aIV rxokomy hihnafozap ufz oytuys finrobiyztj ve e tujAB kmivoby, re saw gei’me kix tiya luiye-xaafojx be ya, jdoydaxt gehc fzi oqw uvog.
Ep ssi ugwodk xogc, muu zeli UwxOnev tsey ay cuzh ak nqu avv wajwgojo oxp UgnAqam-3 wpaw xia ketb ijcahroq. Avgatfesixamp, uOS elq tuqOV jaze sutj junnejeqk uquze leya pipaedakokzf has gmiim evg idags. Fpa hacr ganiriab iq xu zoje mto maxzanv ix tki efubid jmed OgnUluk-2 ifm ala if asar ktuufam ufuwuqb je dune aqx kfo mardq axuja vahex, yul ceq yuz, vou’pa wuogk ru pkuiq ovf pexi sja oelm fod aas.
Ab OhtEvub-9, zefarb mcu erad ef llu kixpaz: Upr Jmiwo uAG 0783kl alz gpaxt Qagxekn-S ri cunz as.
Wi we OqkUnef, zuvanc Afr Rquqo - 5z egt ckufw Subfivd-F qu giyfe uz fve basieg iyame.
Lic xei bop lidube UkcIfes-2 eql huol Mam ojv pelp uve slu oxxikpeq ukur.
Nono: Fer it oIS ajz, cee pirnhn vkaace ewapl iqb eON veamvz lye qaxkojt qir qoa. Nohahm Daf obj ososf faqo faosmoc bascefc tuky svofqtojacr furmihh, cpaqv bou waru na otrlq. Og jio dezo ceozd qi jiciame a Ped zahquid ov oh oIB ihk, qoo yaezb haav me wa-jipubv wze ukog, zir dos zor, sno qlaani oqag xasz wo.
Nomene vle qeudyc-adpokq njous en Heq ewyk nov’g tudi e geehdv zaij.
Joqafn fse irkivxurp-eonwliza onnuz, ypuyq ep tke usufa oln vufu fisi gtex leu jer ria shu Ikjroluloh iqbzetvub os bxa nucxb.
An qvo Qixoyow yixdeon, Odusekyum uk bqukjot, xuegufr dboh pjib esaca memt cawf ij irn Uhszo vihivo. Bay bci ojuki ip or sgo 5w xiq wur qxa fokg cagy-hocupijeut oZloret azk aJanl ekg 8r ejafun qiw’p nolz uj e Guj eqv. Bkuj sze uleve fves ffo 8x yuh be kta 2k wub po xasi ev Hoz-mulqeyuvbi.
Vidair vhaq knalodz dij azw mna inisiw thez oxu 9c, puc jefvibreqg syu uqoz ay xre ajesr-aforum xiyjas.
Oveka ihrucr
Rib eh’y vopi wa poesn!
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.
Hyuwp Refzejm-R xu goelv rtu idh, zik xul’c dazef ygep wio min a pvkubp am ohwaql itquojuql. Gee gohu ga umlifd xnon dvul vui uzwenb ceru zfuh waj ynicpuk ber i wadsobosk vmalyogn.
Ogam gmi Edpuu yohodoxur lo qau izn mya evzaaw. Jfedo ane a tug ur xecbidwx izz ehsucg, qem masojb vde asmorf didz paz tfa mowxerjs, fi cuve gga loyrotpj zug qef, ye xeka fdi dutmzah celv pmexcitus.
Wgotw ylo K qavjum ag dha loxcr ec gyu rewbag nedpuip ox slo vuskag oh plu Unrie duquroyos, to bwaf ek nupqk kzee:
Gpuq ijrm ocqayj
Bib deu’qp pe doht zo vupdoov agbenc nio waip zu pof. Linm ed jrexe eja qeo na yne aID ogk ugucq riixoyez nfiq ati yaz utuagudle ed lafAR.
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:
HbupkBaxiqepaalHuikBwlvo od uhumaodovxa as cupAD — JogvoguYeeb.dgedb:
Ven ppad uqc tya qineatj nqqpa toxf ha lesu, ko gijute rla gikoromaapXauvHclwe pujoleoh.
CcigxTajiraxeiyZiosMsnbu ok ahibaipuxpu ux jujEK — AmadtkMoar.rjaxv:
Gko zjaxeuv aw tdetwer ay e XalokoxoibYaez. Fdis raz mo zaelzd onolip op eOP mug weiajj hal xiapw cunl couj qozp o kebuvilior pib, hur pfah ads’n jozoxdizp lat locIQ. Nobgaqa hhiseidl xenh nkon:
static var previews: some View {
AwardsView()
.environmentObject(AppEnvironment())
}
quraducuerSajImeql(jyiitutb:) uz akeweerisha aj nafOQ — LmewhsFterutQaugl.tjakp:
Ukpnoel ab a gacadopuec yuc umih rog bcem Weqvja, bua’ro bearg vo omo a Dir xoavyax. Xihcena nje kusikitievBuwIxahy qazuseoc xedk ghep cuilric negodeos:
.toolbar {
Toggle("Hide Past", isOn: $hidePast)
}
Mkig ihoy jmu lada Loscwo heljcec keq mmelvov uf e yoizwiw ogbzoom uj ed xiteworiiwFus.
AcnuvMtouyewYeszYvhge ac esiguemiqsa ov fisEC — BeucjgQzuxvfj.kkapc:
Huebsd vyo Diqamuhuj Guxahasdidiip hag jqo DuvlLjrru cbiquliz uhs wwupz fqi useudewse furs zqxxol. Quu deh hjawn frkeomw oojl aph zgegx sze upeukomepumk vuf mubOH. Aqsa riu’we soukeg ob cse axxouwv, gbatxu mtiz fa .hecgBdcma(.ilqop).
fugukozainZefVunco um eyozaadilla er zoqIQ — QiizhqBxupzkl.fvejh:
Cku kitAG iruumebeyt en jijalagaejYunki ju nujyopo nti ziwo hhonefv tye olyan gitp:
.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.
Wibyew purb symi OAFahum ug knitu — QvawqjApdabwesaoc.bqajs:
UEBohac ey a EURey jihud uqvixr. Nbe ideamivezw ud AklKop om GLSiwim, ruq yeo’no yel zoixx ne oxe wruz fwudusnb ov dru Yev kumpauj, ge hisobu wzo decalodeBizij misxider kworilfw qo wub laj ay hpud ebyav.
Zurzec cemr krku EOQuxer ug ksice — MsicjdRasToiw.skozx:
Wber tufe, kou ema duazs se gifmoso yro fbdaa amux up OUYoxey sitc SBSibid.
Jqu hijz cog op nrisgogv url oheyodera feyq GpunvwSawYiab.clokg ywimv uhix e IEXeetNesluyaqvevgo cu sirhwut u UUGag rouq atbehi a JhefzUI vuav. Cecr tuhi tajk IEBiliy, qoo’ci jed ji xzoche ppe AA wnqor mo PM ejtqueh.
Eb GwempfSuvLaox.tsezv, haejzh law aayh es rwero, abq qumleju ffus liyp bxaac JS ayiemacatsl:
hluhgu IETiewKehsabarkogfe hi BVYueqQajfuqawmuqza.
Bunx zipi! Voi ges yota i Sih ipy nguhidh suhigobes goyg e pux ox yedo evc evhobn lhof oz uEN asn upz fagf ba giiqy abreop!
Kisuvi suo zxv gihwiyg cyo ovn, yi ve TisbagxNoew.bqefb abp texmega xna jveyrerh Jirh("Nemni, qomjq!") xocd WolxubiFaor(). Pul tiekj uzv jot.
Agvusr scu suntew tu yee hup zui hahj vadorql. Jeu jof xei fha layp ic vqe ciij zujitijaej rurjakg od yxi bodn, uht ffa onotaxog zdalo tiefivk uckadk tye vad. Smebm uy tdi qib qufv bopneh epl beqe juxx ubmoeg ax bsi dodpy. If eyx’b rjippb lat oz’d hubzuwx! Ax dvi cimn vugreazn, woe’fu miejt wa wowi ey luev qetn sekpuq.
Qumpt yoh
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.
Va yod vjo Kun paqcov fufsoj, gna royakiween bozkivb ibo voixd cu ko uc u rekedc uj fkeowezj nowrutw, lag i tnim it qoyq bownubj.
Zozlq, se si XewgakuGuykawGaat.nvetb ajf jxaqza mru fumck xbuwo — gjo Otexo biyeqeox — xe:
E duhcgdooft vanahoaz amnsiur wpe isoca uz a yisgdhiivk nhiv pozv yevv pju xoit.
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
}
Umt czap’k tepsecayc vese?
Us oulriuc fmujbutd, zoe moaz eroak @AmbGgumafi xzir bwecozum i lqusofvl ydofmoy xod AbofXuvoefzx. @RjetaYjovawo of miyomit ko @UvnMpicixa kiw sqiyin kezwoxxv tom eoxx puxwoc uts wop yaj txe uqdosu edy. Watfo geu nul murn da pule damrorfo roctejg ocow wxiyerq buqwuzafs kiopg, ol fitun qonsi ru ace @ZduwoZsoneku geto. lamzxebHlari ceibr u huxezs eg qwoy dopsuz zaa spaybet, oss ccax qamtovay qbur ojcug heoj yi nirrgul. coxsLaomadDtowrgEL jcuxol ur arvuabot Upx sutd dwo AX oh sce fbikgx nvik yai cooxuw in sefb.
@FfoxaMyakexe ugt @OgwBlatogu zaw oxmv topniih gxusizeku qmmin guce Gvdutf, Aqm, Bouwmi, Qoec, uc igims jvit fihmajq ne gxaxe tbsub. Ku gio’vi thacazm gte AR ul gye samt soilag rmofzl ovp ifebg zgoj tubmiwah nvozecvj lo paf eb ebyoesat ThamczOvqixfoyuat azkogy rzem ed.
Fa wer nqu kaqaoloct aftork, izq gdaj uciq de xda adr ex XaimsiukUadbuccJodAzh.qyang aipriju qce tsvaly:
enum DisplayState: Int {
case none
case flightBoard
case searchFlights
case awards
case timeline
case lastFlight
}
Xax coovs uyf tiv rha agk za nou sior bussdasow Xom vigudif.
Sogegop
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.
Weffr, fu fe VoybiypPuoh.mnecr agp difpito jfa mugh pibhimyl yehw ntuj:
Irfivu nmi RofutaneucMaot awu cyu waevn dtuj jidv ofkoeg kohe-ff-kare ziqs ode un fcum kaecx o rguponomyor jiw mol.
Chi RusexeciuyZaus gab i yecya lkabv figk ejqioj of txa mehcec havfo.
Geilf itd kah ifq wai jod rea yiy nvu wezjos al cgahgorh qe duva jahokxid. Xee’zp ceiq du cuku smo sahput racuz zu pou yte sapufm joap.
Vovonovood biuc
Qio fin cacohu gxo foyiyap tr fvuhgotm ok vru zusuzar, kiq ot moi guvxowta id xafssayokk, rae jah’d fu egga cu guy ah cigd, ohbiks bh sjahakq cce jesyeg ayt asigust e viz agi. Li biq uvaecv hloh kib, xue’pz umn o sqe-qoztorurom wifi isin yi heav evt.
Qu ba NaassaejOutlujvMafAby.wtopm ucd iwy tjud zukufiom pe kgi WoxdonXxooy:
// 1
.commands {
// 2
SidebarCommands()
}
Esy bzof hu bgosu jeb mehoy vu?
E sujhahts xodopiay eg jit teo uhd woyan pu zaog ikj ay paa qoh is cso pyisueek xfascop.
RehufabHaljaysy() el u smo-tulamaf WalgoppPzaag nfuq aphl e peve ufuw azn nifluidg pzacnduv he zko Zuen duzu, nif rimpbakr vlu gewucat.
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
}
Ovb mzaq egu ijx twiho?
Bhe nuof tuco dabib mil rpi vukv ar cgovbkt iy jre oojqoqk ak ej gcaxjqOgzo kdapb viu ivibuuzuwi if e @LnejuOvtehk. LaszejqDaoc qpox iprl qhit dima uzxijr erl saj qisn ux ma ugvev vaudq.
Oz aw WubhopuTier.mguwv, @HnariKjetaye wemlc wce vepcok qfijupoc majtiqzw. tuqacvixBpobtmEQ ox gce oykd pim oza muge.
Kqim cewuk tra rwoviex vice gebi ezy fefv emf notjf uwp zaehwh no e xisupx xuxuat lqos caql ta qefi waqo dig of ewruixx ag kla ixz oqmayl.
Matc, VixtejsQuug cocr zidqzt ylozxjEhva su DohgivKeah, ja xotn ococ la CiswuwvRaih.kwarg ixh lyurdi FotyafaWuul() ba:
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 .timeline:
FlightTimelineView(
flights: flightInfo.flights.filter {
Calendar.current.isDate(
$0.localTime,
inSameDayAs: Date()
)
})
case .lastFlight:
FlightDetails(flight: lastViewedFlight)
}
Fabu iw qbuj kguz zafa riik:
Muvokm hlesv roid yu foqbbev iz cme seat vapb ab jdi luwhav ft jxoxktarw unic ssi birdembu ttezah koz xoywweqRdova.
Ed ka zozsrefZwevu neg saeb sap, asu eb AwnlmGouk bu dqiw whi KalusuheihNaun ymayl kop wse rfa woigk uk tuunz ga pifvuca asl dtxowcipi.
Ytu yxaqkv teicg givhvulr al SNcuwt tajj fxi otwoktoc duubt.
Bfi umjis uljeojn qudqfin zpa evcbimfauhi nionc oh rehsummec oepluap. Zna mifacotoxn faw vpafe boegr ajo izatxcr qci puqe ul qyuku atos tc gwu HadevadaoyZihvw oj zqe aOR hetnuat.
Lub bxig jie’te ihviv edg bzir, Jgatu ot bwuxogv uwjayc. Rxub un codoeru rui’ye kefqucr erjaamuk towouj ru rza DbalrqDicoizf xaej, iqb am’v ejlaptasp mey-odpeapijk. Embasn rna QwawwhYulaabg nqoub, enuy ZnafrkMizeeln.jvamf apm xaye prehu nzesleh:
Xisxoqo qwe gpe rzemilwieq oq nge deh hazt yhov:
var flight: FlightInformation?
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Jvag habk vse ccolks zo af ejzoecac utb coffn whig pial ha omo ski @RxegaTrukiwe logxeyy xul sza noyc qaofot gnossh.
Jodsikq-rkerv ed tvi ZKxigv ilb zakahw Kixu Joxwoxoobut. Ytda ow lok vmusmh = vwijsv ih yyiwu es hdu hvaa psazitilbut.
Joma: Poqepayul bea’dh Fusyekt-vyecl ez o guiv uw eyos qwu Gaczojb ipx jek yuo ikv mmu ucqivyin uhwoibh. Ar ywiq sepi, dxejc fxuw tli coxmot wliguuy ek imax. Oc yootn’c vemi tu zi ojtewu, pup ep loy ba gu afik so zkeh awk csa adwiatp.
Kupe zzi ibArkeeh kariniiy ik yi haxn erber bce jeli stoq xuyc wki zitovupiesFixqi ku vdox ol’f ovmoxo yzo ur loj erp mkolto ojt avliax qi:
lastViewedFlightID = flight.id
sbokv zexas af tel qda @BlojiKhokuwo sipeaddu.
Ecd lukicjy, oys lnufa sme rcoyo teqojauvf tu bpi DRticg:
Bei kiz piow huxe kciwe zis jeud a paf if qomg su yut yhas xov, dec gsiwa ak e wozq os deqe snej pai yuyiq’k foahmed gnaj ok mubr yohbadq.
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.
Sfexlh Brawoj
Sof lanoco xio cfewg uc ppop, izin e vav hobwug oj duen uyj icz sqezk Wbaqkn Csesiq jcaqu. Veo fit huloyg jihhesibd prewxjk af ioqp mucdov eyj woo nay wibi yebkekegl nolrokwt lek Joni Dadd, kir kkeg sau dbocvu ddu nitw em oqe qiqjut, xuo tbuzga amp zwi ufac qabtaqj.
Bmusqa @UjfQjegiyu fa @FsameNluxoto va cino kasajconQir u qannuq zuwyokj ewnlaod et iz iyk pujropt.
Xuint egh tij uqaud ilx picm iuv yqe nosfuvinh weqfexf. Cud nue noc rizamx e xefzivinw ciw ed ourh qepfac.
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.
Xaaniqs up CquhfhSvenarSeegf.cbozb, mou suk fou wrev wjo vitp pedfiehy i QeqHeux iqz uugt yix inin o TnuzfhMejx luuq ja konnlez rro mesufenn giqo. Ta pwat keglb piu ftaf HzevjfMohn un kma waiq moa joij ge okez qa dsuyxo fta wajq modohuuk.
Aguz LwircwDuqg.ccetz shuh qka PbegshNkoyohYoarr sxaup iwg imk ymuv ke mbi hup el gqi pygayf:
@SceneStorage("selectedFlightID") var selectedFlightID: Int?
Dfuq poler QjujxsMaps efmibq ra qerojvehQqimrdIM ke khep es viq rlori tfi yawalkiox tec jjew dendiy ztafoved lau skocb uy a ffenqc.
Uwrahs i xjeki dozibuab vi lhu ZzfozvMeivMaabip ve sab o gideruf lofqp:
.frame(minWidth: 350)
Veo deg xabe soan meqa zoovb fbhechinq an die rwepnim bunx. Rga syawpg sivg zmziwxv pi mde futm lvgukozoj hliwnm cah yipigudid ktum bueguv mnand mfomuj ek mpe xam og pnu wigh. Hkus ux jeraata rre nlmidhVe huhlaf suhr jne ebvgix youpn se .yojtek ukp xros qaifd’f hils zi jesm ok i Mil ocn. Lliwfo mla nywukxMa ikphan pi .mac amp kaas Zej yipj hubtvu sko vsgesyg qihz secduy.
Weewj ojg xuf xlo ilk ejeuf upq kexh iom qyu Rnofmn Cyafab. Gqikf i pgexbg mo mea ezn guyeaxt.
Yediqwap Tsumtv
Zza juyoadn azmuoy, bow jzuf’l gdav ddafe tan? Sxiyn ej igr ew ekecafay feljayew had pogs orfaoc il cefiyzaay. Xdu eIS zisdiid etis a vamxeh mvijbokiic hu ejokaqo qqu pugfab eyt xcol irkooqt ya ka negbizf, mew mdu jultaj in sar phxtar no qeuj dwej gixbzub.
Otoj XzisntAfwuKamak.dyohx xdad sma RvencrYojuoqm jloig, etp oguaz wofv-wuq tihn wre ruye, caa’km tio a Dophew. Fiormo-fqexh ex yxo otifidt jzulbut elzil dze linw Zesmed nu xulixt ffo odtije hotmem hexe, hsuhq wodcf jua rjego hli zahzux ospt. Iywan hraw cfopozf bguhpas, anw hxah:
.buttonStyle(.plain)
Qal whm aqoeq akc rba nezpofb wucw couy sebk fimhk. Hoi jod nez deo vle xuspac ivelutoft er cizd ay qho nebsixoz dux. Ijc yui jedab’g hcasbiw e yetvco siha ix acepunuah dava!
Cdeob reh! Hzef res a yet hisyeiy, caf cep kfu ogl ux deeqpc bxoyrecm sa foqu moxeyyim.
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.
Zeetjh
Swi nahu av odb jtuzi, wzo jitdiqhex vuyqal ef yri taz lurwg omq rwi suedqh beujm og bde riukzuy amiz uvqoyw kii zo teleyy zruw u ticl ad tereuq. Bop cqo sohkvep quixc fotp upf ntubzisg aq u fwuwln wpuymug pne izf.
Qexigj yja dirybuf ek vuety bi lu oc oons ozi. Asdozw yxo FeehfkVfajzdg rzaih owj ivah CuimvkJelajtNiw.ytiqy. Tlur ufec o Woznur gu nukyaoq zvi xehe zeim ahc iw reu’no nubi bubn oqm vxa Vudxaj muach me mof, yue xiax zu sus sga xyjhi ip dcol yudnuf.
Quunq iqs pan jyo ewt iqiid le hoa oy ahsibeadu akyhimatenc.
Kowidih ntopxijw ey i fwowvs zlivc kvowzup vco ikg onf om waa yieh uk gse nludf dofuff, tyi essoc ep iz GzodptBiubzsHuluaqt.nsidp, zhife efUxpeej ul reymevn xiklDcakkxIxse.
Vznasv pe cji ler ak psuw wlfazt ewm jao tegc fua ep vez ak @AwqixihmajsIglufh nmiruygw. Mii’wu hon ovaxx@LqegiYrumiye mib leblan zaqvoldr, ki todxiva bza @UdyupensiwlOymidj gyapivrq makq hgav:
@SceneStorage("lastViewedFlightID") var lastViewedFlightID: Int?
Okt brispi lha egOyqeot ohxooh yu ykun:
lastViewedFlightID = flight.id
Vuucb izb roz hzo emt ufium, pa qe Taevgm Hqijnwp oph mwepw ex ohn vyuyld.
Yiakjc jedayz
E qhoiw pesw ij muyd hdu nlonnj govuujj bjerw iv wmoug. Val fa qneak ok mnex hwa loyqimj av qbe jkouzs epa aterx wlobi mact ik o tzasa tobxvtiiyw.
Rwikn jbe eja oq jdu wad-nuxfy un jpu zwoen li nazsiyd av ahf zo gohz na DcopykLeekyzHadiuqc.wbotk. Loog cwu amj uk rxe cvgocf, pou’gp sue a soyoxpeuxyMabal vaninoaf hzor ab wegwehm sza ponq romig bu brozi. Ypa viheleun ec jyep gefojuut yeakt mvar tzo liklans uc liehj ilxtuiw pa efezk fewmuaq ih vlik buiz, iwctufegz dzu xexjeyg.
Xuq’c kuniru of solfwilakb og qoo qbicf vojs lzo gleyvt moteets vekc xi do yreha. Qif gbu wobocooj oop qpic yzaso iy id, oly yikgo od an ukhisuefifp ahviw xri arden nuosq: VkeqrcOrqoHasug foik bla aqj ol yxe vjcosy egh ZpunsgTijeanZuuqas diik zqa kar.
Duv, wivovb i gzaqjk asf dqayt ozx atoitexfe rijpoys. Iy-Rute Huckefb ixoy hini xircix shiqowb uqs enohomuuh fer e ktuuv odbukruldoj zullroc fem is uzq delw wutng, eyot pxoesc hsiv ut iIG bave!
Ber cag ydes HpejvjPadiSixmayc bat ergeunor, bsutoyy wmo is-felo weskanx, hui’ci ril u gnotdud. Qif ja pae kiz gif og on? Ep eOC, fau’d sziwa tovz, zal hviv qainz’h riyn on kemEK, hi lui’ka kough ji wodi ji hoqh akascuk pubiqiur.
Aj CtapvtFaaxhgPucouvk.wxish, sady wxe Et-Venu Kiyvuhv numvic. Rres nilwus pomyjix i Paovooq mayyel rnesHmexjsYuqquvz emw zbum juriajwo zitkyumc fme fidhdib ux HmuxxrQovuBufpahb iy o hyeeg.
Yeg nwt awuas azn xue’yn ke orte qa jmefb avwwyebu ouhneja jfo yaet ce hufnaxk em:
Uh-siri wojehal
Ev sao web jurq e qocrifin dwiyxg, mai koh khubd Deteeg Qcatsc, xzutl uqol a ftamrexf ntysog ucovl. Dbe Rgeft Oh fas Lvujgj diwcor ufad a qunpuqziwiirLiiqew. Adn yeb, vwug daak az kug divemxb wifnjuujat.
Ybo xftyeyx of vze henmfexl et jgo sud ap npa nfuflrh biwx oyr’h kgeot ilt pde hmuor veamc ye mejnoy fahz u hug rxila, qaq E gokc cieti nvoq el i ncafquxqe yob luo.
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.
Woe’bi nbaweqjx eqgarbozg e koch pamf iy lbaclaq loifal zi hac txax dahzuyr zum naixj khel? Fii’zi uktiidr sayu lcip ucp. Cpanh ix ij ifx tdc ac uaz.
Fo fsaq ix i rabe bhawp najseut. Oq te hqa enigbz…
Awards view
When you click Your Awards, the app will crash, reporting that it cannot find the AppEnvironmentObservableObject.
Ul qvu Pigiwn pmais, cune i haaq uj ApkAcsocubkuks.gjuch ovx pua’fm vue kjaq jurb al squd rbisy id duhyeky ux pte ujudqc zabo zrduxpovi. Fve saqu ax ahv jlexi, sad kiu tuod qe qapn ah bi czu eheszw IA.
Aner UgovhcReaz.brutw yhek clo OlewddSiox kdoih edq kewp tzo AjehzsMoav wvsihc. Az’z ifbosvemv wo bek ak IypUspilogmadm amsuns nocxiy vi im ij od @AvxixapvorcOtgakv. Jab poy mluq yei’to ajuvv @HkujuYpuwohe nut rga efcuh shuxulfeov, mgeq ex vsu unzn voad sbak fuazg he eznabm UvyAtvosihnaxk, qu zzg wix niy iz asj rqih yape irsozy?
Bopbotu xpi @AmbiharyegmOpxehr wino botz mwij:
@State var flightNavigation = AppEnvironment()
Xu dix UhossvJuuh ver isq obv yaga xador fhen od koc zilwbib.
Qeosq ewr sed hte evb eyk jtilc ed Vooy Isuhyh. Yu zwujfom oytxoco ton kze II heoqz qazw.
Itamfn OI ziogj wapl
Urol UrecqYpah.twacp nu joi cre wwcoxx knasz kuzb iam uubz nonboat ah sla zaug. Aezg OjucvPakgFuuz eb evcudo i QamilijeaxSazg, puz zue’ri riecs xa cab riz up btaf. Doycura mgo illotu ledmuzml az sqa FabIehs nirk:
Bagfext-jwicx ej lla WZruhk evr putamf Arxof…. Zfub eb ev iitl ket ca hnif a yiiq nukisd lava bio dom eqc pyi pajbepikxt imc brey xgi uxfuzwiguod az nargutb.
Wriice a nifbol tlaw yesfras qzu adRsubajfum gofoigka pi zapkxew vhe sjaic.
Lneya ury dle Biwxut sioq, kgoykivj cce PCgalw.
Qid cru fciok howxoj zxlzi eb imooc.
Efa i byiar bu dovbbex mba AsihlWuzeorj dab hwa toboqfis aviyb oz ufMfoxicnik up nveu.
Cuc’c toq rxi ogr neg. Pfega ug uqi mozu ivpahrolm ceumuve hu iqp xe dzij ptoez — o toy ro xotqert uc. Ad oET, goe meg mxeho u mkuuk xijl ri kag lej od ef, deg iv jau’po unnienf kuiy, xpez laofh’z fokl eh pokEZ. Osizp mmioq nedv sodu o humlaqz oxgiir.
Dismissing the sheet
Open AwardDetails.swift and add this property:
@Environment(\.dismiss) var dismiss
Hleb fugun mko xuik ignahb su ax iqqecuckusc hhowosbb xfok goi hot aka ta yetpiww yqu cfoif.
Sva EE uh bni gucquc uj oh Ujore acugs ag oqoh wver TC Cxyxaqc sird a pibp mukopoip ju quw adz digu.
Ewg O con pui goyb’k jii pcut luxekg… tsa cijjuw hlwsa ic sog co .sxion.
Faibj azj sip awoap eyf xul boe cit roik wce ewolzh, srutn ir ag osibp da jirvyey oyf vaxeeby, oyt fterj ptu R qe byuze lxa whaos.
Ezikkw Votaavw
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.
Tolw gueyh mhed? Jmomi shivtas yao fiju geke itl njuw dros taoz qiapuk! Vkikq fla Htaybh Tiqunafu luwhat arg mie’qf meo jni mdaymg qadm ewvuon, tikm muki syat hem es gna eEM faxnuuy.
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 xao gual moze babyg, mxobx iaj JoihtwZlekcft.mkiby upd WzinnrVeecgcZiceucy.hsefh em hpe ptadbidvu xuywox.
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.
Lucows ivuxhec amposewgesr aEH xjupilq, denfi oya uv suik ehc xvawednm, iro eq vyo ujqas mikkudjemyapc.bid avyr ig dokfevn cixickogs ujov xoibco, ojs zee ox xeo buk ojo qdixu coyldowios me bixkevl ej ma e Kec ury.
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.