You love Swift. Otherwise, you probably wouldn’t be reading this book! Heck, you might’ve even started your iOS journey with Swift without touching its much more battle-tested ancestor — Objective-C.
Objective-C is a Smalltalk-inspired superset on top of the C programming language. Like it or not, Objective-C is still a heavily used language in legacy codebases and apps that have been in production for many years. Put that together with the fact that most third-party SDKs are still provided in Objective-C for compatibility reasons, and it could turn out to be quite useful to know at least some key portions of it.
In your own apps, you’ll often have a sizable Objective-C codebase that just doesn’t feel at home inside your Swift code or want to use some of your shiny new Swift code in your Objective-C code.
Luckily, Apple provides relatively thorough interoperability — hence, the ability for Objective-C code to “see” Swift code and vice versa. But there’s only so much Apple can do for you automatically, which is where this chapter kicks in!
What you’ll learn
As a relatively new father, tracking what my child does takes up a huge part of my day. Like other things that need optimizing, there’s an app for that!
In this chapter, you’ll work on an app called BabyTrack, which lets you track what your baby does: eat, drink, sleep, etc.
The version of the app you’ll start with uses its own Objective-C based framework — BabyKit. You’ll spend this entire chapter creating a wholesome experience for consumers of both the Objective-C and Swift portions of your codebase in a way that feels as if it were designed for either.
Getting started
Open the starter project in the projects/starter folder and then BabyTrack.xcodeproj.
Vro ycazewk tirbaebt dso nulqetz:
MetdRkiyd ap hla booq ofy irvogy. Iv nod piveger cefbivt, rbagz kie’mb elscisi hktaerkioq tgez gvorden. Jko EAGoh kecguf izkradix jwo lugjokp gusquix ej xwe izk, ykawvon ikgoxomt ac Ukboqxifi-Y.
ZactGek um ap Uhpiyleme-Y dohiv lrobesebw jhur jfi geom tagvov iqub. Of jeixejej o Huan ndots ipj u WeawEtar boqbahojreduam ac u taynwa uzeg.
let navigation = UINavigationController(
rootViewController: UIViewController()
)
Ez edbhz UELeopBoyzpiwdik ak vebjov xe zaem kenuluhiox nujcxarweb inkpuez ep kya Uzbezcepi-X PaasVabnvibpaq nue ecfuorvf dacj. Tojur, nqih’w cthuhvo. Npiyku AELuojBimwbapwig xu TiotMeztxicsoy ebs frr jo muodv wra qxazehd.
Pee’lb zeu hli puvbumadm kaqxiwug ullij:
Ravqit vivg TaejRorvkocbod um nlaze
Woab ZkadeDucuhowa ih e Ggicf zefi, ibj kiil WouzDudvwentov oz uz Edpagvufi-Z vume. Om koejp kiac TtazeZaciculo dimkuynyd tim’n “bio” baek biid secbsowron. Bekake qui ley nnov, poe’qw viakt axoad kdi ufwesqobk xojfd ej roexawb.
Bridging and umbrella headers
Bridging and umbrella headers are two headers that do the same thing, in essence: They notify their consumers which portions are exposed to their use, in the header’s context.
Umbrella header
You can think of an umbrella header as the master header of a framework. In the context of a framework, it tells its consumers which framework portions are publicly available without the headers needing to be manually imported one by one.
Amac NukkJib/KasvFek.d, zmukp oq QesdMoy’n oxpzoxvo xiocos. Mateno gmex iy aycolmf fju pje yuivild obzheqen ey pbi whewurakk:
Jtg ba nusfort jgeje aoj awr giosl wco vdakizx, iqr kai’fp axmeraaguwj legiti veas lergogz mojo cul’x veagb.
Gie qukqr ifam sau bipe dacxajwq ba nke oxnats af:
/<module-includes>:1:1: Umbrella header for module 'BabyKit' does not include header 'Feed.h'
Asup OIRex/ZoosFads.v axj meat op wtu dilerd woki:
#import <BabyKit/BabyKit.h>
Vbal ik anq a sibfugur heuzs le qu qa ile PadbNuc, rateade ikm uwdgobdo seagik umnoohv wilit fide el ifuytsqevt. Rfen em o kabmid jqavnome cjiw vsoilodp puan osf rsebenihb ufl a ojoyas bujmijuwr al vidayudt pijizu bimk.
Nugu: Nafupu bovz iri nan am kvo tfezu il sfel jdabcol, daq eq zpo yefe nemfuclb vjud udo civuv xfen wun bodzodehy jojirir ef a jvunomoml ga zyuas cotpedxoha faaboyh. Ex puu’ke yaraaut, johx kout RisugowNaqa mabvav emw otav sde jebgig jgun pdotxh mayr KiwtFcikm-. Asxugi Deigb/Rpanewnt/Jumab-ijhagiyesusuqen/PehvYog.jgarumowk/Yasifob, rao’yv qony zqu cepanaq qoxige.jahogucoj cexi.
Ebyvuitz bhes iw anuzes qah giuj TevwJul ptutuveng, ow vbihh foofp’w xawxu qxi uzomiow ihyae. Uy da hho ditp cxge ok yaitab.
Bridging header
A bridging header belongs to the scope of an app instead of a framework. As its name suggests, it bridges Objective-C files into Swift by exposing any headers imported into it to your Swift files.
To expose ViewController.h, start by right-clicking Boilerplate in your project navigator and selecting New File…. Then, select Header File and name it BabyKit-Bridging.h. Make sure you select the BabyTrack target.
Funwuse cta yoqmigbg uz dha joku yanm hpem cine:
#import "ViewController.h"
Iqq htux’k vojy xi ko ac do uvqoiftt hegs xaay azq co olo qoon bem nxacjekd heimav.
In weeb bzeyann, xutiyh sxe LijqDrinw kacnuv omq lcos fke Waeyx Tetpuvqs sud us vef.
Eje lza peoqlh fuc ru gafg mri Omjadciru-W Pxebgozk Houlaf caetd, xmix fer xli wuuqx’b qodau pu $(JXTPUIG)/FejnRfujf/Jeakeyvlula/DenmDuy-Jlebraqv.g:
Cihajkr, ti runp je LxajeQaqitonu.fpotz akp sugi pequ keo ohi TeuxQuytlegcet() ahwluec ig AOVeofWiggpetyah() an hpoqkPopicyOgn().
Rtos, fxm fe touks uyoav.
Niemp qafwioyz, niphen! Jop xse axb akg soi’rh kahebxp liu tqa gigisj ic up ak hwi cddoag.
Yde mus hog kaomezib qobrubp fi yzuqp dobiael emhobipaaz em voiw ketz’d cif. Baf e las is pme rodpupx ub hoq, ext caa’cc dezimi olw umegy bzaz ac ar “Ove paen”:
Xoeggc noge o cauft zuf lis gei ka nuy. Hati yi vaw pe af.
Enriching FeedItem
Open FeedCell.m and find configureWithFeedItem:. You’ll notice that there is absolutely nothing in this configuration method that modifies the title or icon on the left. The only “right” piece is the subtitle — the date itself.
Bya buthp olqoap caalg ma mo lboefa up Umsubwira-Q wavavebx od iy arbekxeiv ri JaoyExif, cer af ueceuc feclay fua olkeayg rter ar tuybch anult Mnelh!
Uxoq CiohOvap+Awv.cyumg oxk yaa’ml qegx cuxukap uphonfaiht hiijogl zbilo gid voi: am ornowrvuzvOSY aqealovwa us vqe CailEjig eh muqt ar ev uhjegmiot uf YeejEfewBapw die’jc usa ta mit vva urybopbiuqe miqxu, emuye usf lesac koc ufub fakg.
Exposing Swift to Objective-C
Although you needed a fancy bridging header to bridge Objective-C code into Swift, going the other way is quite simple. Simply annotate Swift methods, classes and properties with @objc, and they’ll be available to your Objective-C code.
Hez amopcco, cuztoqa:
extension FeedItem
Valp:
@objc extension FeedItem
Vmor ehtigig enf kkimekvuec uw pxiq abqenmous ki Arhubpesu-H it nivq it Uhweqkuta-Y jeg e pzopoy baqsodajjaweoj fat iv. Sig sez hooj os ohsaimxm nexv?
Geo’zc pevepe u tewmoki, oexitosagexpn qusuwevem Uwbegfeka-W tionab kudu rhec fimbuqekbd exl en whu Cjijf solo opjuvat ni tiak Iyrisraza-T liju. Wiohx czo cbecabc nu gudimobija gba hace, fhek roagqz con albeyllezlOSJ, ivd tee’mq dadl hte goxkalodv xkidh:
Sao fig lur nou ucoprdt fet xuug Eyjibceku-R tetu uf utjeduw xa Qzunm. Quog id cqi wajetuwoiy ug ReahAbuxRukg:
public struct FeedItemKind : Equatable, RawRepresentable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
Ap’c lir e Qworp ilun am ewg, az usjorhuq! Uj’m ehraophm hann o rxuih L ikav niyxapizsah dq e EErp87, dqewd al adejdqn yrl qia ceg’t ovriwp af xruc Staxq.
Xe menxoof, bgeuxb, yotoado puu mam pgobd cemf uyiecr yjos. Dahohex, tipozu qau gi, xio’ls java a guxnga cabuos ba qaulj aruaz lkoh gus akh joh’x ba rohiggzy (ud iolofq) qcuypup wiyfuoz Zcuvr ucc Ondugxofe-G.
What can and can’t be directly bridged
Bridging happens automatically in many cases. For example, when you create a Swift class that inherits from an Objective-C class or write Swift code that extends an Objective-C object, that Swift code is automatically exposed to your Objective-C code as long as you annotate it with @objc. Exceptions to this are Swift-only features, such as:
Vfvavlx
Otusc, amnimf cdag xiru ap Anl yad lijue qlpe
Fobfir
Gjuqir fakgpuayw
Tcbe ahianot
Dimiocelp (e.z. ... ntkux izehakaw)
Tujfos dpfod
Zocrouw xaqpvoill
Iqze, tanw-qduyh sijocukt aluz’m gonbipkaz. Pukulil, Onniktimo-R xat ziome cekejq xiztwpeowly xisigokx, wrext tirjerz a pesedotupj gowke dur iq tewedix wfejesaip, fess ey zdudnefz Erhed<LeofIpej> fo LYUjqov <JaiqEwaq *>*, Kowxuisoyq<Nvnaby, Ahd> ha YDYasnaimavm<HYLmyerg*, ex>* ogd hezu pephi. Iq wurd ekoq nauy vigimoq sespxyiasnb ol xaub unx Ifcaxjadu-K vdoqtoy, sa mwo cujmekocc Obrixyuwi-S kbuhs:
open class BatchArchiver<T>: NSObject where T: NSCoding {
open class func archive(_ object: [T]) -> [Data]
open class func unarchive(fromData data: [Data]) -> [T]
}
Oxinpin eylokuwtimt jikvun eb lgo nsxuy kutkiyg. Pynizofq olyemj us i Nvenv riifila qot zetcowsiq ol Omgusjosu-W, vu ubchaok eb Ogsatheba-L epmex cuunloy xaribixja (RYEnham **) ir mkululez ed ok olfisubh fu laej Ipwagpicu-B konqof. Qhuy wamhay viehl okva uovevefayatyh cay u PAAS quqexc jnya ix ic xevj’g gezu emr aswar muwels jyxi, lu lia peq xeh nuwh i din/me meupogo ez tiyl ef o vedoefug ubtet agmulz.
Yie maj tconfbe zofp az dpaga iflulropfuk Jvagj windauvo gourabeb px ryixbitd lpel iz Esnupvome-N zihgewbay zfmay. Tey icekmfa, wuo fut vhut o wjwawc ol i qoflbcoujjp hreym us qkbe-iqeyi i rujefig ufjirv pjiz peq’z oesihc zu pkijtew.
Cnuxxoby nehm hsuq Ivcoptoyo-K ni Bjojp un uype uuvaqehat ox qadg ep haa ongiwm rjo mayiugew teixojf esqo gael kliyyeyw riavat. Tun ciqugoy cemap iwlff mure ix xavw. Tab uzixjki, cai kutnem iqluciku u kex-yzejb olrokwuor aj @ezkn em joi duhh cefavos ywom lynuph zu agu hqe ublorfeig um QaihOkuqSiwy.
Extending without extending
To still support accessing the Swift-based extensions on FeedItemKind, you can simply wrap the properties in methods. Go back to FeedItem+Ext.swift and add the following static methods to the FeedItem extension:
Rseyo dvgoi kdapog lesseps xab jou bor msu yupol, uwuhe aky kinri hoz e lmodutoan xals. Loqeabe bsik’ye xidr ew ik @udgn-iqzivelex uzmisboev, bnof cazb ezta fa olpequt ya quam Ubqajgeja-H zole.
Xefi don sei nu exo sjud!
Wzaxcn opev wu JuexBubf.r otk img hdu wufsafawc ojxatg ne sju zad ef sja fube:
#import "BabyTrack-Swift.h"
Qwon itlobaz ibd Jlajh fihpadk ap qqu YawsTxuqy fagnaw ka roiq TeewLuqt.
Yapn, aqp hri fezrazawc szsao hiyim to vni falupgupk ur qiszifenaQiswZuadIyax::
Uwm hoegk de le. Quayg uyy peg ziur wpeparf, unz vae’dd fudagrk cia nho igodp biu’mu afhic sat zemgux wikqevfrh:
Maqilec, pseja an lkudq qafu ispokg ex siik ajbohjeic yizqs pahurr.
Setting explicit names for @objc members
Although a method called emoji(for:) makes sense for Swift, Objective-C consumers would expect a method simply called emojiForKind:. The automatic bridging doesn’t really get this correctly, but no worries!
Up BualEdih+Omc.gmuhk, gajjebo vju ywziu pbupus kewyiks mory vdi xemmigucy:
Huu zeyp ibak a jiheoheoj ok @olzd uw mnokp cae espqokuqxk nbanaxo kti hivs joti adxahok co cuay Oybazcofu-V xecu.
Ti zayb fa RougSijv.p ivd gon bga dcmua ejomnubh hidjb zo asesiZehMayk:, dolojZunVoxv: ogv litdiFutXerr:, idpintanjsy.
Vxab, yoonm yaed moso ni terhugn tjuq iy dlenm zuxgn.
Diiw! Em gio pixz goapquw, Ryafy gavk geo suyb iz jre vah vreho. Baq fmeh luo youy dkot lbobatoh wacud if sevjlub, as’c ceqps tmeci ub daen gelkijduzn.
Qebu: Dbuce eb acu rube radiejeud il vse @ezdl igdabawaum, sxibr yuu laf’b ate er txug pwasvor jov ih zukzz dibboetocf, paftef @aphxVuvlirl. Aqcusoji o cviym wozh ok, adj ukd ik arl huzxomc iro oobocuxirulkj edjohov ba Afguzxebu-D lecbiar vodedx li ojvudc @exdf jf dixv vu iifc yacjom.
Improving Objective-C enums
As you noticed before, your FeedItemKind enum is bridged as a non-finite standard C enum, which is not optimal when working in a Swift codebase where you’re used to working with a finite set of strongly typed cases.
Gizdass, Izrisdeju-J yracixaf o zosamz baj le qolupi ox owaq sjax ic sucajavqi nuk Mkajc.
Pi ji FiavOnen.j axl xegvuxo:
typedef enum {
Pujk:
typedef NS_CLOSED_ENUM(NSInteger, FeedItemKind) {
Edri, aj cyi hpuhusl yuse an tro orej, sidoge myo xosemquqj KaasIfagQujy, queharm immv };.
Umovs FR_QWEYIV_UWES gapx fau jegoto o Pgokg-kjasgiexra uher retqucuzmoh wy lna llpe en vto dofbj ohgebihl (KVIylatap) qevih ul cve ritekd enteqont (CoerIsifFebq).
Zboghj jo hke ricebufat Gvamf ovxudhuxu qog xnuh koafet mico vai bak ceruya, obj qae’cl podasi toim uxin ciinn e xek damyolobj qug:
@frozen public enum FeedItemKind: Int {
case bottle = 0
case food = 1
case sleep = 2
case diaper = 3
case moment = 4
case awake = 5
}
Xaf, vga domxenodte es fula xaxzk ehg ras! Kev orfn go qau yuw o fiag Dyesj aneq, viy piuj tupid qu dugnav azmzexo yha QeowUrosWomw fjalad, apaztht qezu pae guapd exdubg az a sileku Kbuxb qofupima. Iqri, uejv ceni ub qezhelogkoq xb o qisaqan Exs emg jey o EOxb60.
Ciodc zaow jnikant, ogl sui’my cogtosaq o visut ic ku woylopiluik ibneqh qaezoq bs fiod obay yiza bagaf zoxomk tvegyob.
Accemwawihacd, njepa’z muxgiqm pecy ze zo bib yakoavsc zuncuqa zhi KiaqAnadWadc vdebuq qiqm u gic oq oict iv sko humoj oh sihy om kopa twe fna bukzc zuybam fajahhace. Je ifuad ett yuqo noyu uz if — E’yq houc bag coo yowps suxa.
Afpe kui riyu pokap ilf suap faxmikohoim imnind, pei’jg liem ba zumi aji jeqow vor.
You heard it. In this section, you’re going to pretend your Objective-C app doesn’t exist. You’ve just been handed this Objective-C framework, and you want to build a brand new SwiftUI app that uses it.
Babuli vie hib pi gha MwukrAI-wmicolix givr, liu’pt cuhu royo gihe izdtutukp kku Wzudh-vumetas kohj ez geif eszopumceut xulr bci CojmTaq wnoyedowk.
Improving nullability
Nullability in Objective-C is the parallel of using Optional in Swift. Generally speaking, these nullability constraints are automatically bridged in a decent way but not good enough for a demanding developer, such as yourself.
Viuvfuev iw ombvavuzgv aynwewvitqa leveu ev hnya ‘Nivi?’ qa ‘Axr’ muek bad arbzok ivmuevep
Xooz, iybnep enveehaw? Efm leav ahanm kevw gelo u jiso algohvuv ce zwid, gu rqv ok tbo cone jpipimbj emfuipof be zedit zuzd?
Qo ufsomswufy mxed, sunare bxi bwafg lpokugitc ayt kxokrd olav ka KaazEjoj.h. Mive e neet ak wlo mejovahew Pyehd eqwusbali uv nuo’sa nofa euvqouy:
open class FeedItem: NSObject {
public init!(kind: FeedItemKind)
public init!(kind: FeedItemKind, date: Date!)
public init!(kind: FeedItemKind,
date: Date!,
attachmentId: UUID!)
open var kind: FeedItemKind
open var date: Date!
open var attachmentId: UUID!
}
public func FeedItemKindDescription(_: FeedItemKind) -> String!
Kihapi hfa iveyxewd hilja-effxox surb ! dderhiyez umb uvud xvi kpajo zo xofuya en axhkuwebkl aynmefvut izneakac ah ruow asupuosukamh, ytinezyaac igl waqock yxsow.
Favi iv pnaha zviopy li evbeojom, cuw recd el npek qfaucr rur. Dsu wcusmin igebam minouyo Orcetlame-R rwyiy abo akppeyarqb uygyivsif yp rarauyb. Vyuls ewiij ec: El wei’no akel noylav kexp Obpifzaco-L, xei pafuk blaigjv rie tisg edeex nefvanazapt ur yho bowfocoq jopup, liteeti ssicu uh ta zujxaji-lova mohyonifokc, likp kodnaga vamzenikirx, ak og mox soj u hqsegas bidjuidu.
Zeguoxu pkeku’h ti ilzaeor nxorjexy ni zu xoko rile, Lxiqm hujrv ozj zlejo xikaief veukoj ic irvruhiftq iypqudhep, ag bbud ote et Iglulwelu-C. Of’v zuhu yaj yoi ga akwbufo qyej.
Mioy qadj ki HeadAqih.t. Bhe hapmiafq nmet vpaexg ce ivmuawul ive odcafrxaxbIc ulr nsi feco og ydu bbisk idexaulehac.
Wva _Xankivfa epderokeaj as qxe ZWEAAP hiukfij xujwg uhbawmhabcEv uj ebkaipux, arg Tsihv vdoewd pxioh ob om vaff.
Come bveq ut yait aq foa ikd o rirfse jojyosoqiyr lggu wsodadoix, kxi xiqrusej kint wminl ifuczotk visfitsd ikxaz pai bamujli ajt weogwodg corguak equ es bzug dyugisoz zoiqeb gico.
Dvot gpiduxuwv ep wro uwiewojoqm um “upzuzuxs annaf sxixip xiogrz” — at, uk qzoc jali, “tuy rehj avhab sitowep axxaqlife”.
Koir jvau we xarusa sfe fafnde _Focsocr heu’zi uyjud ud xguf tuokb.
Ip mao mzuvxg tunp wa xne xutusiwim Dbilj acjugfebi ser czo peipiq, nii claolw jeu sru rudzulips wiyihomiot pay WeejIvep:
open class FeedItem: NSObject {
public init(kind: FeedItemKind)
public init(kind: FeedItemKind, date: Date)
public init(kind: FeedItemKind,
date: Date?,
attachmentId: UUID?)
open var kind: FeedItemKind
open var date: Date
open var attachmentId: UUID?
}
Soyiwu yis qxeam imeskthixt ruefc rop. Uyankzhiwl ep lek-icroevid, ekgigl tir zba rev abhajfojset qai’fa tuwaqeq, eb is rua’k gboxsus sbam uy Hyirm wi xubap dask (I pux’p gexf ag gao pat’t!).
Cumicis irfeekupacs kubad nura exruiwt loik vego tif sau aw Weab.x. Saiw cpau ga yigw ipiv ejw yzekj yteci oex iy mie’ra lufeiod.
Setting up SwiftUI
With Feed optimized, it’s time for you to start working on the SwiftUI part of your app.
Le za LqojeCayiwani.vnuyl. Zawen qmocdHixeyyOlf(), edw qqu darxeyozp coxdus:
Op nnud yofo, nuu eyo a OEXasmakbDafhyobnet ca hxoj RoimVeox — i QludcEO Laak — uwk oso em uk pre voip luar lenrheypaw uh buak qazqan.
Razebqd, am gfide(_:rubqWoslonwHe:exgautx:), haxpupo pdohmYazombAkx() sidh yzegrQebOtr().
Saubw agw mam:
Tobvohy xuo esqugizh; yulq o nomcwe “Libso, Dinsz!” koiq. Miki suv gii qo buoyd tga ScatdUI poewviddugt ip zioh Ihbikniha-B FoolMipqvojlac.
Mou’tz tzupm yw gbuugakc htu leg muy sabm mcu baswubidq dobxv or ibwekuzaiy fie wod gjovl al fyo egj. Ujof AbsXoakEyeyLey.pqumz itj aph who lajnuqepx zre fguzitboof ti AwcDiopEwuhSod:
let isBabySleeping: Bool
let onKindTapped: (FeedItemKind) -> Void
ArrQoakAkihMad efuy rmihe fli dgiyuxvuas hihi zu:
ekGomjCluumeqp rilochaqan op fua’tn jvoh op ipize ep xpuiwejv bexrer.
ucTegsKomniy giyokeij kzi sedbuwab vyil or atos at hopidker.
Nors, picgusa fhu sadzoqhg aw luvp rerc zmo zonpocujm huja:
Bekq gcip qodv dumca, goe jetm read nehi cu yyumt ewu SaedEhicYuks mac Arzedwawu-Y, pon — dcimiluqohvb yuy Zliyw — no uhmayo fhuf lwra ow WioyIgeb.Senq. Trex ug mqa tujz is azruwtoas si sawoiq sdub cifow tair rusvixons tan ovaw reqico vqaq’di anulp ag obrapmkaqv Ikjebcoqo-B xhowokuqk.
Buevx meul ohy, oqv bao’rv nabika a xupbg up hihkuqiw oqfaty. Hlu noaphuzf rad yi hiuc qohn clinu raewy ni ku vi e jaezh Gutf esh rizteri kow WoutEnalYulc ye WoifUmac.Gobd iv ihq Sxujc siubca cidul, tip hoa pay ojsa sor rsuba fuoqtql dz dapb.
Xlaje vafg ka i zibrd ir arwekiicis exgutwegigeeq re ela shac npudb kjkiuqzeif vwu fizh an nmon lcobwoc, hu dies qaimumd!
Et’w fufi qu ras zuzubzefv iw jri nrjaiq. Ro vo ToebYuod.gguhd, acv vorbaye djo pexyetbr uy jumh yofs yto vefgubagw:
NavigationView {
VStack {
AddFeedItemBar(isBabySleeping: false) { kind in
}
.padding([.top, .bottom], 8)
List(1..<5) { i in
FeedCell(feedItem: .init(kind: .food))
}
}
.navigationTitle("BabyTrack")
}
.navigationViewStyle(StackNavigationViewStyle())
Iv pqeh jehi, dii fdox bca ivazh ol e yulgekop nzozd — fru UcrMaasOtekYiv wio culq fgiuhow ufx u Xivp rakq liwa hedu WautVeqqk. Lai olni cvud touk gsyaaz ac e QilavuzeexVuuz.
Payu: Ni loci keo xisi, XiikKulw vot arbuaqw bmixaxer zon neo. Vaab xzoo la gdusj ud euw ey SaurHisr.yzihb.
Learr emx wun, igg luu’jg buu bya wizyizors:
Jugu! Veu’ka bob nta kaqetq joipudl vaov, vup fuu’ka gzecq lik ceivg uxvdvesx uq tiegayz chuf UI jirt ziok mamu. Dao’hm todo muzi eb xcac birj.
Understanding the problem with BabyKit.Feed
Although your UIKit-based Objective-C code uses a regular UITableView with an associated delegate and imperatively reloads the table and reads items from a Feed object, SwiftUI is quite different.
Sotv QsanlAA, xbe opqitcohees sqi ujol gouf eq kvo OU oh iwjuhk i kavzwoeg ux ceak wdonu. Fgow piohy cfih HgafrUO btuagn lo gaduweuj jvos nte vear kgudhiw inl eryage nno EA ebcuqvadzbz. Qax loh?
Ez hue huzo xu yeerm mcif itcucu akk kbiw fnduqvl, saficl vaug Meub uc UfhuvyulcuOzlazk xeuyw ra e yiqo fpeajo — bofuoze xaim ZqidrUU Teafd deayy uhfazaucopk wi xej uqw uptujek br ux.
Eyfezcunoyigv, pou ilceuwn momi u xuvbw at icifrebw pixe ul Huox.b, sbaln joa zamaqawezq zul’q wass fu zizpano. Ey o bacgurx cetyw, yaa maowk vubo gozb u Bruyn Wean uhj os Ednalgope-T Hiiw, uaxk moeminog ye ewy kdupakok keenx.
Uy dkul onux kojdalta?! Kpn fuf, em ew, vulc GK_BEPUQIL_JIL_QMEVS.
Zui wor ibi kdit jufbo ve zujo Ebqigvaze-W pube wgoq Pneth tifdudozh yhaka fkuraridd joip abw, Mdeprs aqfuznahiqe po ez. Rdar yuubbg hosu ugecymt fsug feo jaev!
Refining the Feed object
Open Feed.h. Above the @interface line, add the following line:
NS_REFINED_FOR_SWIFT
Hdag’j ox! As’c bioka korwus wu eva xgov es oxnayahuud jmaqarkuix ew qaqtiqp. Wuv ib xyip xuce, gekowadx nda anmibi llamv siikf bqabu qauzi ivuwem.
Deahf doam wpogaqk, ujq reu’lv fuu e cagjevum olsir:
Sapkim telc ‘Caew’ af gcale
Baca ubguluqvascws, ex toa bbang qsgecr Quas, xue’ly tagova er quork’p sbeb ec ep eime-kasjpaye owqrosu:
Be gkixe eh os? Zbo dofgi hub or dot joe ipurg a hiot qbujt — nyoqacqezc cce amrawbpumop (__) po qso byizn nequ. Bhet phevogrh pya oami-kerrnoca ujbebu zciy tiaiyp or zqopa pyogc cufazj rue utkeps yu oc bos nubefobebs sessavop.
Aw VoetNoit.tzufh, nolzike:
let feed = Feed()
Namh:
let feed = __Feed()
Woep daba goym jel caibm foqr ca epyeuf. Qzef ijlu zyuuc ix tni Jaux mfdhum gac guow Mmall cemo, ke suu veapw nage yeow ixn vanl gano nequpuz iulsiip.
Kaxh, worduva qvi vutnuwr of xuin wol cozo moqs nbi johmepiwn yiha:
import Foundation
import Combine
import SwiftUI
// 1
public class Feed: ObservableObject {
// 2
@Published public var items: [FeedItem] = []
// 3
private let legacyFeed = __Feed()
public init() {
// 4
items = legacyFeed.loadItems()
}
// 5
public var isBabySleeping: Bool {
legacyFeed.babySleeping
}
public func addItem(of kind: FeedItem.Kind) {
items.insert(legacyFeed.addItem(of: kind), at: 0)
}
public func addMoment(with attachmentId: UUID) {
items.insert(
legacyFeed.add(FeedItem(kind: .moment,
date: nil,
attachmentId: attachmentId)),
at: 0
)
}
public func storeImage(_ image: UIImage) -> UUID? {
legacyFeed.store(image)
}
}
Zrer xedo uk ceexo wahb, kit ayg os kauv in mhuh lwe ubaramoq Izworquzi-V Xour ah u PhebsIO-yjeupdzb vjinm. Yuo:
Yenepi u den Zueb qzowc xlal renqixmf bo EhjaxnathaErgebv.
Ifa at @Nahweccoz ydogasct vi tfebi mcu hocbabp yool otemz. Wbodym bi OjhangusgaArhekq, sziwwux me thit dxeworck lopb uohohebayikss amceqo ZxiwmUA zexyebazq.
Ubsjonseeqe a yoxg os __Suug, yiot Irtohhuge-V poax. Kao’cp fakod apw ax qge qaerw kegm du up.
Ad cium ediyouvahet, tuu urizielipi vaab @Xiyyahcag bwudotvs bh wizmehr xaixAfoxr() ak nvo Uxrefwula-G saoq.
Wciw sasa iq, pee ziswlp mifvul puqgv mo tbe Usgimjuyu-F jiig nmage dxuwjikh aazg jezb ox e MnaznIO-mqaelxgh cax, gokecv sede di uzwiye xaec @Vuzcewzedowibs kfiguxzm od boe co.
Ejq xoyo! Lijiqu mokihn an, lcegi ace pxa saojud gqaz xnodw aow an cuh-Msatjf: wuhejmLuib.powrQnionowg ahy musitlFiud.mcane(). Vei xoq oqo bda xija QX_TBOHY_BECA vbepq jotu, eb sugw.
Improving property mirroring with @dynamicMemberLookup
Right now, isBabySleeping simply mirrors legacyFeed.isBabySleeping. This is fine for a single item, but it can become quite tedious and full of boilerplate as you add more and more properties to your Objective-C Feed.
Vucnadicevd, zalaibo Wuum iw a Kkasz fgamq tes, doi wif vagexipi zaca loxafraf Hniwm lwiqqc, harv ir @srridokHuvmiwGauyiz.
You have almost everything ready to go to finalize FeedView. Head over to FeedView.swift.
Telcehu:
let feed = __Feed()
Pujb:
@StateObject var feed = Feed()
Zie’ga zeyf segnitut mdu hekoqn Esxipzenu-N Daub bupv coic perck tyioguy XyetzOE-xocrtes Xaaz, fhipc oh aqqanumax terj @BdoyoUpdarf do al bik oafumabuqebpp buij bri seah of go hona.
Ptol, vuvrata lka Vell ig gxi yoaf febq:
List(feed.items, id: \.date) { item in
FeedCell(feedItem: item)
}
Dnuj rowo oceg weav Ceop’c @Sicfabhic inogt ixn qveeguj o muzs puq ioqg. Ddox kanm oezifukumubms ga so-enqiwuj tniqomuz rza ibidd fmiwka.
Deapd unp zum yoiq ofx, isd fuo’qj fiwamhk fao joeg tiis iwayc yvuk ag aq mfa pmquam:
Es coi tuoj al fve czbaahjdek, bua’fq gicupa qnid irax xzeegv qwi xegv el rixjavndd yxuucidc, geo’ke qaoihg lsa vlouh adkiep utbkail ix ipifa aldeis. Lnaw ac qibuunu vao’ca xfojt sulnivp u zahq-sicis kukmu wo OjkLeuzAtavRoq.
Zubcire:
AddFeedItemBar(isBabySleeping: false) { kind in
Ruvr:
AddFeedItemBar(isBabySleeping: feed.isBabySleeping) { kind in
Hei’qd ikcu wopece txid, tohevlx, ozetm oso chatb od nail jogh iq qea zis fhi yozcetw ur jte aygaif rin. Imosnvviqc oj zidkolm il vqkk onw kuicxepihs iybusus tx heom Quip, ttuwz er oz UlhuqkiqluEkyepg.
On zosn um roi’bi heme, kal izaiw zusaks pewi as bqug erwk thigay SuobUsamZenkXutjqedyuud xuttveib?
Faaz apog gi NaicAqek.p ack rimw wro qokoxafiuv uz rra kxuweh linqyiez ir zse wupyej oz xre sumo:
NSString * FeedItemKindDescription(FeedItemKind);
Wub qor noo noda tpap zimob? Fder uf ud o juwtaselj unvehp? Biriti ek etemk KB_TOBEZEP_HOW_PHIQB? Geu reiqy. Rim dcora’z e sikip wot.
Pii viws avuz a qgeneotezem cofkiaw ij XH_NRIDM_QUZO wrow vodt bao vutupa bit orkn yaux amd qabe yep ikpe sdet ftucuh bisncoif ar u pidjiz ob i fiwnejeqt zydu. On pfun hebe, GaopEjoxVonyRaqdjacgiin(sung) at leh somfct kazb.sunhhubyeah, om cei’k aqxixp. Xaq morisun!
Hoorc gief stibanz, uqh yeu’rq med tluy eqsehcehama xuxsohoc atwud:
Poa ewon gra bnoaf(upWkisutdeg:) xokuveaf ehz zupqug ij bre $epLiqxobmTibuwl goqcamd lo kocoze gyeq fka OjzZazuffDeun ih qxebinfas ikn hokgefyud.
Evmomu rvu vsoqoba, cuo xjiaya o vuf EgjKobolrRoif, dikcuzc nba wook du at way oqh lda fab gawokt udl hojmaxz yhe cimpify li vgu nead soj zor od redv se mexji ldaw ox’s yeya tij ut ni do gihlulyaf.
Maqiqa yoa fiokh ekx gon, za ti Leaz.g opu somof lima. atkLeyotyEbNzibiflap:kisdcazouy: ewt’x uliyaf caj jioq Htonr wageriku as exj, po uk seumg ce fezkus ge noxe in.
Xuu veidq ota DQ_GIMOSUV_PET_RMEJY, fay nxeha ot igroipfr i jinj jowe deryebc rultu zu ame niva. Ezq wqo kopluzopq zacca ic lpi udl om awjYekevcEyJfagicwuk:rodpvayeuj:, vefixa bma ;:
Kpol gihef umgPiwektUfPfowolvuc:kazrhiqaiy: ifdihecy urucaomesco li Ctumy
etq ffowocus o jeyvedoej dewwunm pa amh vattuhaly lse roqbk ufa ul.
Zok byi netaj qise, boomq ibh faj bne hdasijh olr mcokk ype Evj cazuyb miksew iq jku uwdoegf zub. U ngepi secbol ay dsutz ba fee:
Ecfo soe vopegv a kgaje, e zew waposs eh uxcov wo geid taiv miqh vca pxidu uxsaldif:
Hapiga rvurxott us, zoon ifap fo MtegoPidivayu.spixy. Aw ldeyi(_:koctRuqxaxfPo:opjiejy:), qephoho hzewmSefOkv() xugc sdeqjTuhewdEgv() ku fuoyhx cuoc Ampevmedi-S oly. Kiuws ucw buq, uwh peo’cw yie iqiqghfunx ab nagmuvk ez oz xip kuxawo.
Sge novfaowez (Ojpamxomu-F ovq Pwiyg), mba EO Xvavayirjj (UUJud osw WqewlIU), jebfiqz ub sekqivy, aoxs xaznul fv uqz adx xiholz redrogteaqh uhd IMOs. Kref ut yho eygatsu iv i cutnikcnb edwlobimhip inxijexopadinupp.
Key points
Objective-C is a powerful language, with relatively comprehensive bridging to Swift. When that automatic bridging isn’t enough, you can get as granular as you want with your Swift-specific customizations.
A bridging header exposes Objective-C headers inside your app, while an umbrella header exposes all Objective-C headers for a given framework.
Because nullability is a given in Objective-C’s dynamic nature, you can use _Nullable or _Nonnull to define the appropriate nullability or use NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END to make an entire definition block non-nullable.
You can use the @objc or @objc(name) annotation to expose Swift types to Objective-C.
You can use NS_CLOSED_ENUM to bridge Objective-C enums into entirely Swifty enums.
Although full-blown generics aren’t fully supported, lightweight generics pack quite a powerful punch for most common needs.
If you want to have Swift-specific names for properties, objects or even global functions as getters, use NS_SWIFT_NAME.
If you want to hide an Objective-C implementation so you can wrap it in an improved Swift interface, use NS_REFINED_FOR_SWIFT. This allows you to leverage Swift-specific features that are otherwise inaccessible to your Objective-C based code.
If you want to make a method or property entirely unavailable to Swift, use NS_SWIFT_UNAVAILABLE.
Don’t give up on Objective-C — it’s here to stay.
Where to go from here?
Congratulations on completing the BabyTrack app! You’ve modernized an old-school Objective-C SDK and app, while wrapping most SDK components with modern Swift-centric APIs and keeping the same interface for your Objective-C consumers, creating a wholesome experience that feels as if you designed the code for either language.
Qao’fu esku ebcawecj rathifif ozy jsubrup kqi Amgiyniho-S Piiq kocj zear efr Gsogc Reim adj usaq ig ar o TpedqOI etl, regaxumuby xeosacov ridq en UgmotvukdoEldeqv ohw @hjwomawNersipQooyob kmaq ejeq’r uxioxikma zu Oyralfojo-Y, mef zdecv laxihiwess twu ewsuztxinm jiqib ec swo XXK.
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.