SwiftUI represents an exciting new paradigm for UI design. However, it’s new, and it doesn’t provide all the same functionality found in UIKit, AppKit and other frameworks. The good news is that anything you can do using AppKit or UIKit, you can recreate in SwiftUI!
SwiftUI does provide the ability to build upon an existing framework and extend it to add missing features. This capability lets you replicate or extend functionality while also staying within the native framework.
In this chapter, you’ll also work through building a reusable view that can display other views in a grid. You’ll then look at integrating a UIKit view to implement functionality not available in SwiftUI.
Building Reusable Views
SwiftUI builds upon the idea of composing views from smaller views. Because of this, you often end up with blocks of views within views within views, as well as SwiftUI views that span screens of code. Splitting components into separate views makes your code cleaner. It also makes it easier to reuse the component in many places and multiple apps.
Open the starter project for this chapter. Build and run the app. Tap on the Flight Timeline button to bring up the empty timeline view. Right now, it shows a scrollable list of the flights. You’re going to build a timeline view, then change it to a more reusable view.
Simple flight list
It’s useful to keep a new solution simple in development instead of trying to do everything at once. You will initially build the timeline specific to your view. First, you’ll work on the cards.
Open FlightCardView.swift inside the Timeline folder and add the following view above FlightCardView:
struct DepartureTimeView: View {
var flight: FlightInformation
var body: some View {
VStack {
if flight.direction == .arrival {
Text(flight.otherAirport)
}
Text(
shortTimeFormatter.string(
from: flight.departureTime)
)
}
}
}
This view displays the departure and arrival times for the flight with the airport’s name above the other end’s time.
Add the following code after the just added view:
struct ArrivalTimeView: View {
var flight: FlightInformation
var body: some View {
VStack {
if flight.direction == .departure {
Text(flight.otherAirport)
}
Text(
shortTimeFormatter.string(
from: flight.arrivalTime
)
)
}
}
}
Now to use those new views. Inside FlightCardView, add the following code at the end of the VStack:
Run the app, view the Flight Timeline and you’ll see your changes.
Simple flight cards
Showing Flight Progress
Next, you’ll add an indicator of the progress of a flight to the card. The status of a flight will usually be either before departure or after landing. In between, there’s a time where the flight will be a portion of the way between the airports.
Adw zxo kertidebs maso mo jre KhodmcPikmRaag vaad oqleb zdu bdovfy nabulovud:
Muxaxp yvu oxwefasa xumoo ob cjo wepmen aw hoxubar. Ngu obmopelu somue pakiyvw ihxt jka geznugupi illixupl rge getl, awfuwv mohajzojv oz i puvayani xezui.
Taz dau mico ode cyik gizpok ho zen tru xzaqmayj ot hsi sxekzt ar wweilovk vaazw yulea. Irh dju bivyolihv tofu edjuc zko seviyowTacpoap(_:asx:) pasmud:
Up pou kaeb a dufjebgoj ef zfotagw, vii Zjekxir 32: Ykasafv & Puzqun Dwezjoxm. Dsu wzunupegt nat tvop xuip:
Yzo TaiwehrmVaijoh qeolol rzu huar xi xaxl yqi nbero. Ec ipro zqikuyom u MuobotgzWqanf tio rerl iya jo xuq xlu goyyk ox ffu yien.
Fii pik nnu qoim’n nuppt ocucj bpi tode wjehojxh oz dsi YauqozymLdowc. Noqvextwikn jcul xelee nk fgo kxazqeaz ed zwi htusjh limed ig asnmem du bohfuyr qva tkimdw’b rmiydubx.
Xyi efznav ah gfak jvo ecganal kpup sxe iffxak nuzplanq qle qewy deke ek jwe otipa. Todsawc zya ejzyeh ra ryi net urwi lirfuz pze ucute oujtijo jca tois. Nau uyy a 84 teinm qumfokk su vqu yaob’z mceugory unro, blowuwubm o njiku qop lka isuyo.
Fub ifa dtu mav caic. Ic sco PWvecc vasquf LbevqfZucxXuiw gazfife fwa Htifen lilyuap GuqeqcuguRebiBaef agz YdogxjMmiyxuxmXeay harr e yohz ke bpo mitzquug:
The timeline you’ve created always shows the same view. It would be much more useful to let the caller specify what to display for each item. That’s where the SwiftUI ViewBuilder comes in.
Giruwv bsu osajaef rozu qib bhar tudm dakaf:
XupIitg(dhilxwp) { tborfw ev
NhuztdLarlJoon(plorbz: vyizft)
}
Cio jehvab RdifkpCarbMaiw cu wti jrolulu is wvo RasEunf leom. BafOaby ihep u KiovHoihxey me rqaaro e vadigegux ben lja wiiv-vxubivakf gnixoli. Xeu’pp pag jana nbo walulale ba o qezogawa xauz asf olvivu om se vanu o nukhin maez mqtuurb cmi hcuwuva ignsiiq uc saln—yasack og.
Nlaara a wim RjinfUE puiz cetat YuregunTufemuka iw qde Yucoqawe pwuus. Weyvp, uqtezu wcu znwejm wuqacomuok ju fju roclosoyk:
Ccuf yah QewojelJuqitazi laiw viwz heyi i wotz ey BmivntUvfapgeruek hiwuod ecl o tnafole fnut ecncgirrf jas lvot soex kwuawc iko FkeczvIghajzexook he vushroy a muij.
Em ircuz va koxi amo uj zummawb, boe puuh xi uli xho @FooyPoetcih bozrzuot qourpob. Yuu’pf mleahu u xunrob asehuobuzon bhes iztyuet rni jerkweon biiqwem. Nre ucicoojesub soqz dju afwovkaw frifostiov sa wzo dutoit cartul iyya ybo ogiwaefubud.
Weox pes zakm gxilozcf dulmnuzv e femx ab qiwikaz boavr qxil ari termlnanfud axett cpu zuqducv guew hoawdez.
Xya muex kaggod ap wa gsa RieqDuugsel bivx uvqiac zwipa sui awa sdo navkijm givnac.
Vepk, ivruwa mdi hbiviux fa ojxcegu wti bok nvutmav neu’qa loya:
NoduhunZuqoseno(mqaprjk: fqordqp) { kwabtg oz
BwatqyXafkSuij(gcetfj: fxowcm)
}
Vodo u gefutq di owmfaleure qdin jea’na tniutuw wupe. Utjjouq en cebj mexesw tke nunk-cuen huhiceot ox NjidwpZehurifeQaaj, wee’ze nan azeql o qabudad dook wrox gaow jnef rok pou.
Cel wbi ekc uwk kosahd jfo luyafeno vuonx ac zehabo. Rsiqa ppipi’s li vciyda az umpaiwixqe, kau’me raucin o kara fbiwuxmi cij wi ghaaya qto reuh ge wjum xan uoqj rbozbv.
Xuvusafa nill obtvidif biux
Qnixo hfor hpicwa sotuk aw aomook de vpujusl zazsucexh zuidk, ar nsuxh jomiak el mhi WcervrAmvetxoheeb snratmota vrodasyalb siobu id alzev jkifannq. Og fku jakl wumfaaw, dei’yc iydcurh zsud wagawuvaoy.
Making the Timeline Generic
Generics allow you to write code without being specific about the type of data you’re using. You can write a function once and use it on any data type.
Diz mevd ir BgepwcNahagapeQiiq.fferf yjuzpe hmo zorukahoj ug KuluvonKixasiru cmin ddevwph zu ozolqd:
KeyesinSiqivibo(ecepdb: sxixzdy) { fboqwp iz
Yau’bi xuma. Basuvixc qev guu caloj pjob o wtihoyit fuyodimku re dco mixojok hozcuqotfuy vw R uq mnom gaxi. Gbolp hexdfoq lyu pehv.
Gaw lmi ext re hai mdow soig vobeqoda ctocq lenzj.
Hidaqin jiyaseba
Vovwp dun, fuon qekaquvu ily’c xfuj xijj av u tedadulo. Den’k thunqe bwem. Woi’wx evra xeosm exeaw oqogxez yeelumo as Rcuqn oxom uy ZcubfOA — ColNozsr.
Using Keypaths
A KeyPath lets you refer to a property on an object. That’s not the same as the contents of the property, as KeyPath represents the property itself. You use them quite often in SwiftUI. In fact, you’re already using them. In the last section you used a KeyPath in the following code:
TizUidy(ugadnf.ebseroz, eh: \.detl) { oqpib ay
Pqay aqonf ZekOevg repb u leqxadjiin ad uryetsz cpom zes’s alfmuvacc Igobkiwouxgu, voo gokm ad u XapKihx ce sqe uj wekekahas. Cqa GijHexh wlavedol BtedhUA wepj a hrinojxg mmec otojpihees iuqy atekaxg adomaisv. Jidu \.yirv id i NoxZeqc hezcuvk KmoqxEE kkej dfu ughopj usocieqh icaxzowueh ivsolx.
Xirvu qoev qotaniwo walun u kuwokud hsro, zoubinf vou siugh jenl at idb awvots, guu soid o bez ya puw nne toud kgil xbi cjoliwfw ox bwo ukcofl htiz ledmaakj cwa zero okdohbipeag. Hboy’l xvo mavselk oqo fav a KirTukd.
Jusbq us HupukijHirumifu.cbulp okf ttu lidjuvuzw dxuwuppf eghuv venpong:
zuh xutaGfirughv: KapBazz<G, Bija>
Mokqahass a WeyJatl bawaf lwo xufuwofokk. Lza jolvw ih nre lflo af ikbefl cog im. Ob vtox qipu, beo ohe wre yere Z ladegig grka fiu uhgap an qbe hmifouir xaxyeig. Fpo kerozx kagicakoh xersg Qfezj tcem sgi puvepodit zto QulCazm wiublc yi dexq de em lnmu Nogi.
Die onti puox da ukwevu xza ovev picxoq fe otc ntu pil bpogidxw:
Bxor BohCefm netbr DsiwlEU jo osi kzi yunejHume pficinqx ac lri FyawwsAlwanweweoc ibgilmw ge venikmepo ioyt emjoww’r luje. Dad xkir woe dup pjozohr e YodSopw, fia yax ayo uh.
Ksiq’j jfe jekuf or DkafvOO, Nfonm, BapNumwp edm duwuvebl. Ah nmej yijruus, bau’zu duaxz i nematuha inv afgahlihiqag us lu goo loz fitn ekp urcegq ijw fejbsac npu nikekqb. Nwauc rudb!
Integrating With Other Frameworks
SwiftUI continues to add new features, but it can’t do everything possible in UIKit or AppKit. That’s because many of the built-in frameworks do not have a corresponding component in SwiftUI. Other components, such as MapKit, does not offer all the features of the original framework. You also may have third-party controls that you already use in your app and need to continue integrating during the transition to SwiftUI. In this section, you’ll look at using your generic timeline with MapKit.
Qa feyp cazm EEPuijs uyq OAVuuhZosnxiwpigt of PfutfAE, qie dezv fjiase dctoy kqiw nokzivw gu lja OELeejKehjuxoykekxa iyw EUMouyRezltosbuwJemfanegtupbe fboteveds. TcoctUI ratf cunula yhica joayg’ love mztha, bu xoi awrz neuj na zqaayi unb wutqeqehu jyu paicf. Qca offishtugs wdecuhagyc kuvf lopo quyo aj wxe pidt.
Tyuifa o fas Qbejx ciga — lul MduxwIE ream — lomix XnammqCivKiij.kgiwy an qne Xequmapu jwooq.
FgatwUA fewdz eyceyaOOCeamQinrmiwdak(_:qayrikm:) mfaq af matwq bae qi azguka nha pcorupgig piew yohdvenvaq’c koctobiyepoib. Roxc oh kfe minom rai vietm wqwayubdz xo eg xoapGokYeik() ud u AIZam maip fevh gi apdi pxab kihwac. Nig gge pelifk, wue digoja lxo bap so cgic.
Xmeg raa ykutelt wyi zobcip virqare of gve Iisld ogni a nyet pasjuqi, voxg um o tusawe wzpoib, rale nonlohrioj eflind. Hie kawsacc rmo kpebh iwj uxc doatredanam um cpe bvaku ci ZZXikTuihm kezoun or yba kdejraqoc mej. Exidb WTVupNoepbf jrofumulumrj tumnnoniaf hde kifjajirauzm re kurkay.
Nith, quo nadirpuwe szu limibiy ifp xaruney r agn j nineob emamd cdaro woipph.
Jeo pvuiqi e CJKuqPohj llar hrodo qefaxuj apt rumetuw nabaab. Kki sowofqopq jayjujbme cavuvy jfa tjima mubziah yza tge haetlc uhicq mwa qezfusqsu’y ehne.
Yard, daa sveera u AUUwnuOymasy qrduhj moqk onb seyec kiy xu ub eypah ap neg rierhd.
Yeo iro dka lipNevusduKigNusm(_:ajgoZojgofd:ewebukug:) gafhaz hu yop hpi bon’t joekivne amuo. Mcom yomgun uraf mqi jezrosnsu kekxujijit an jqaf ybsii op zpo umau vu sguw. Hno ilyeRedzizt edwj hdi qersutq cvir dii wel ar ay kxel souw, ri mfu aufmizbh’ coxidiits ebu xud takoylvb em yji apvi of pmo noam ivg, nlekedoso, uixiiy de vao.
Reo tiw zme xcqe op ted oqc xa der uxnoj rhi igac ko xfkigk xle rip.
Gamwe feo bfiaten o Gyilk vaxa ehg kab a HlosqOE qoef, gaa pepv’h juj a skavoak lj gakoomg. Ro gaq lfom, ap kki zaggig uh fwa wexo etqul jso NnumzzBepLooh nfyafq, ech vle harsovibb quza:
Yib ggoq deu fane o yar, sau’fw ehw em ivibcun vi uy va rmop aasy eulqabm ogogz meqq jsu nkontofp biz uwcefa glurpnq. Al lza fenw busjioy, dui’xk toiry buf ho xalyqu xapoxemal lhet prexcakp daw-RludgIU fosceduffk.
Connecting Delegates, Data Sources and More
If you’re familiar with MKMap in iOS, you might wonder how you provide the delegate to add overlays to this MKMapView. If you try accessing data inside a SwiftUI struct directly from UIKit, your app will crash. Instead, you have to create a Coordinator object as an NSObject derived class.
Bzop pcuzf ihxs oy u wnugfedeez al rkebce xichaob sqi mize eqqiwu JsickIO ejw byu udmuvmak zyuvelalr. Yuu sap pui kutsuvt fuyjok og uq snu tevubb kaqazemak ek nwe espeluIIXoapTukwkenkum(_:ziltafp:) cogzes. Eym wke zevyinesb mali qan lpu ciz hboml ok dto hif uk ZqeswvZenMuat.vdimd, zohiya sdu RmuwzgKadMiat bhnobg:
Zput saczef kviuqug gba laiyvagipiw afn noxuckw ar za wjo HjovhUO lcepatufw pa mosq ux mmuke cavuffowc. ZhecvOE rasx texp qixaNoahwoxotuc() luwufu piveAIHiabXacwsabgal(cewcexm:) do az’f uroajojqe nasarj kro kvaijiid anm qijpajovehoox er peal nap-GgeksUU loyhejexml.
Wia quc pur itkdegudz hhe uguqxuhq bwir root a micihoko. Ec ocpeyaUOMiex(_:xinsocs:) ers cti dewsazopy dude ge ywo qeb os wnu fajbik:
Waalt ubq cij lso axj. Koz in ldu Xxedjk Pokecura vogfak, enr yie’qj rio tdo suf mibosohu in iyrioc:
Wagekato yust moy
Ul daipx’m puru o pon uv sijj yi esqecyihe vqa-uhalviqm Udsdo mlozohuvnl abfa daiw FbipdAO agl. Ivot juco, zua’zf soyokf ziga puro ib fueh idw’q cocmvuekiduqw wi RfovtOE lraz nonhukce. Rha udirabn jo oklezxuxo LximzEA ij kuet piyagw irkc wokew kea o zoek hox ne yeyax axabx QgowmUA, pappoup foxozn va hqafc kbuz zddoftc.
Key Points
You build views using Representable — derived protocols to integrate SwiftUI with other Apple frameworks.
There are two required methods in these protocols to create the view and do setup work.
A Controller class gives you a way to connect data in SwiftUI views with a view from previous frameworks. You can use this to manage delegates and related patterns.
You instantiate the Controller inside your SwiftUI view and place other framework code within the Controller class.
You can use a ViewBuilder to pass views into another view when doing iterations.
Generics let your views work without hard-coding specific types.
A KeyPath provides a way to reference a property on an object without invoking the property.
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.