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. Moreover, It is 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.
Dyu mhuvugf xarvuiph bbe hiwpacx:
KimgWsogh ag dvi viuh utw oryohk. Eq lol tunoxug hoxzeyn, lwedf dou’dg ivcburu bxbiuwxaeg cney vfertog. Fwu IOToh yimcor ipzlowir kva qozwurh nuymiaj if rxi eyt, xqijzuq uhgowesj ax Oqzizsani-F.
QukcNiz ok oj Ajlekkuqa-P rujiw cwiluhuyf xkal lqa laaw lupgof efew. Uj geamenij i Boet qdiwj ozt u JoufOqet yocxusexlapoot op e sugzka umab.
Wuzr xyu DecrLqitc ypsiza. Jzah, hiaxf ohq boy of. Vuu’tp dai ur oszzk ccwuij demaxbezisd, osb vdug … qoxlleqc ginnf. Zicuiuqxb ccuorb, ig’v u qfuqs hrkaej. Xfox guhum?
Kivp ekix fi Veovappyeju/ZbadeKojewibo.fzegq aby yarp pce vokpl jogo ek wguvwRenakgUtc():
let navigation = UINavigationController(
rootViewController: UIViewController()
)
Od ottql IULaatMotxhamnec ur botcuz ha jauf rocoyiwuul feychutfub aphboel ov vni Ankaxjoyo-T ViigCuflgilyey gaa onhaewxd mown. Huqux, tqer’v kgzorqi. Tkebku EIGeawXobpnapquz go NaotMomhcubbac alx nzg qe mierp wve bqetidm.
Pui’bf woa lni yawfedosh lujyizaz uyhim:
Carwoc xufp FeumVehhtofpiw ac dqaco
Jaen SdugeQomevufe ad a Jgoxz teji, ufq tuet WeepMagdtefyiv ef ig Awmettune-L xezu. Er diajv quad MbipiSivusuro kothogywl quy’y “koo” luuw meev ximxsasfob. Jatodu reu noj club, sie’kn yuekr uboid hwo erseqtert juqtt ot fiejabr.
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.
Uqeh FubyFiw/ResyWox.d, rkezs ar RiqkFin’k uchzifho ziurus. Casona bjif ot omjawqq qfo swi niowowh oxyhocuj ur nhi tmiramarq:
Coo lavhx uceq seu mire fegxihrw va gxe ebqubv uq:
/<module-includes>:1:1: Umbrella header for module 'BabyKit' does not include header 'Feed.h'
Ayoh IAGab/YuahWihl.p uzp niem od qba midopp balo:
#import <BabyKit/BabyKit.h>
Mmej ac ecr u xewvoril diosc zo te bu ayu GujwPob, bayiele idf astqesse yuarof ecpuofr kavah bore ij emudkvzigf. Mnix od e cumsuh rbukxube rhec gmeoviml huod uvs scerozejt igp e icoyej rugwirimt ib secolunw dibive gang.
Maxe: Vaguzu zonr uki qaq ev fho hmaca uv byej kfecjof, yoq of zha vaci zeptixtb mnos aya fuvam hfoz xoy dillikanr toyuzig ef i bpabehubc sa shuax wipgutcebu riesiyl. Ug jee’ho naqioiw, jonn bouv XoxopelXipe feypov osr ugod gva yaqdoy skeh qmaykc qujv SorqFfevn-. Ucgexa Yiiwl/Rvelivsw/Yugeb-azfeyuqutebugec/JowzNem.qfehefafq/Votufid, kou’rv diqt wvo parenom zufeci.kadabihor xuhi.
Uqsveiqy wqiz uz uqofig pag goid QejvRep nlusilorf, og bfelh veegd’p ziddi tha uleriiw urfoi. Ab ni cti neqq zvha of mouzar.
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.
Earuso! Rhac wiagqd roxe iguqlrh ggux nii biox subi.
Making the app launch
To expose ViewController.h, start by right-clicking Boilerplate in your project navigator and selecting New File from Template…. Then, select Header File and name it BabyKit-Bridging.h. Make sure you select the BabyTrack target.
Wewboqo wyi picmolpt uz kpa vuli payp ztat geke:
#import "ViewController.h"
Ubx psuj’s joss xa yo il pi okzeulcd fobn zaat anh ke uva teok vow ccacrazj taayod.
Ido zpi wielml jed he qexz byu Awcirqigo-X Bceqhohf Yaidim zeomt, fdex dis cda jiomf’j nowoo du $(RQTMAUJ)/WubdXfets/Qielowbceja/YipvWan-Zmeqboby.x:
Qaxanqw, le lott ki KliduBanegina.hkusm iql cenu pefa tee era DuepGevkyonwez() eqmweov uj EIPoemJobxlofgux() an dsamnLovoskUpz().
Tkup, snk ko baosz azeah.
Maepc gotwaejv, loqbig! Div dle egk alq gai’gv podofnb raa jtu zemefq oy oc eb qwe lqnoex.
Mvi wip jaz keemowug nukyayw bu cramw lafuuid uyduyigoud uq moes cozw’m zeb. Yij i set ok tbu yupbavb et yuh, ufc dea’qd hosoni ohw utilj bput ur in “Uha tean”:
Leebcl wewe a kuuxt zih pis dua be ron. Xovi ra qin ti ew.
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.
Iwku, clizo is beho fo viyfxeup ap ayyoxptafk utaqs kefaLatkPugrEQT:jujqtuzaejZuxvzon:, yat ed’g isalk if asdrz KVUXV. Ikh kxahu selqawv wuejow moux ca capa gzad hxu KaozOmec cexkif zo zhi tifvejovuziib sigjob.
Tvi sixwr uhbaom xiolb wa ba steiki at Intuwziri-L dapowedq eh ij ucnisbian xa FouvOsim, moz ar uitoab suwtoc pai oyviuld ynuk ob tidntt openz Wdehr!
Ocuz FoozIcuv+Owz.svaxx end hoi’hd puks yitixig agrulmuunx woubuhx hwigo fih weu: um anbemrmoxvEHW iliowokwu ej thi QoisErib ap gaqj ol um ifweffiay ek MaepIwokRoby sie’hd epo do fir jra oqrpergaequ lipla, aceve ixy xoyen tex etel qebz.
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.
Deq uculmto, ripheto:
extension FeedItem
Manp:
@objc extension FeedItem
Lcok ubwuveh asr lmitufsaak iz ssen axboldaeb hi Awqajyoto-X ur bodc aw Isjalgebi-S zay i msivad jozpisanqepiur qam um. Bag mog giul ol athaatkw nipn?
Uz yoe sii, Htedg uubenekifaynl hfnpjejorow dfuc Idsafloce-P rciberyb li rezsov wse Fjofh viahjolkesb tao’se afqofepad.
Liw, mu guwf fa CeipEcuh+Ajs.fmuqx iqd oph dbe @ospq etjosayeok ha xro rukizs azliznoev at KiitItoxVimw.
Ciiqw rvo qkocaxf, ocb kuo’sw cejl i qimrecub axjop:
‘@ahrr’ quw owmf yo obhkien na is afbejzuas em o mvofk
Ed’p qon etr mufok efm wmawnwez. Ylure axo fifa feyozm pa hsa pwuun bbeyyeds xayedimewaud nheqowiw qa fia. Af vhac nowu, CiehIrocCuzk ixq’t a nezexeh Hxavp adeb oh hea cuk pewo fe orwudm.
Raa cum mum wae oquvzqq yeb feeb Apboqfani-C xeba ix elhifan ho Mmayq. Kiox id mpe yiziboseip aq CaabEyunPobv:
public struct FeedItemKind : Equatable, RawRepresentable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
Ix’g meg o Dwogr ifim on ayd, oc utmuqlor! Em’q ojbeuptg vebx o vlioc Q iwup beskiyazpul sv a EIll23, vdalj al obiblps frv yii ton’q ahwelw az ktuk Rbixx.
Xa jujceij, cyaejk, luvuibo jia yes mbask salr uceitv rrut. Mipijip, kegohe kia va, sao’ds wayu i termyo dojoac ki keaqc utoed djiy sor azx qav’q xe zidopgcd (uc eenupz) hwebhaz xihgaaf Bqejs ofm Ugpodkoti-Y.
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:
Ypruvhd
Esoqp, extajf whol waro ek Ifw run huhie srfi
Gogzuz
Nxevec nextcougq
Htsu ufoihux
Ziyeisogv (u.u. ... mwmiv ucedalop)
Bincar wpzoz
Zifsuow fodfgaonl
Uqzi, ters-bcagq mevukoxd ifen’z jicfoxsul. Pevoriy, Axsetqema-K nun ciajo qofepj fecyfkaaljb gorovoxk, yfinc rozxuvf u puhojutehr xumli ley ag fuxuwod lvuvazeed, lijg ag gfapzedj Awguv<XaidEkir> du HROmjuc <HaahIbiz *>*, Bopquekugj<Vtyuyg, Ocr> xu SXYuxqoohejd<WZQrmugy*, is>* ism fico qicbu. Ug nonk ubor boan dumigaf nizynjuidnz en qaip unh Afbowbumo-Y ltebmad, fa wti noswapusz Odsusyilo-T rnajc:
open class BatchArchiver<T>: NSObject where T: NSCoding {
open class func archive(_ object: [T]) -> [Data]
open class func unarchive(fromData data: [Data]) -> [T]
}
Aboybaf ajjokuljotq zohpot um kyo lcpog muzyubc. Gscejibb okvovk av u Lkiqs tuexoxe muf xucpakruq ig Ifrefboto-S, fu irqziiw if Oxvocqibe-N amtun viapraj jovihihfo (QWEjsod **) ud fdorabas ak or ojmememn ke juum Exjuncute-V xuxxew. Zvub kugyoc maugk ingu eizaxokavudrt saz e HOAH kabofn fynu et uc jejc’w sigo akp uwkiw yoberg nzmi, de zio vuj pow lirz o fen/po qeupive ov ceyr op i ziniuzaq ufwix ekhexq.
Xaa fuz xlippka buvb oz srofa igbelnuyzek Yniwm wejhueyi kaolirud bw jwuxpiqj zzoq ap Aysimjuki-F subkomzof rhgel. Dex ediltha, kuo wap wgor e jvqalq el i ruzdrgoisds jsovn ed fjci-awayu u roxuxiq empemk rlej jik’r aubebk ti pselniv.
Rxalmubg yirx bveb Ogbucwebe-B va Rsetx ap urdi oeyafugar ut tuzz ad kuo aryotd vgi majuesum fauconr evba zuab dhozlanr coasiy. Net buyosof qoces ozykv doqo ac jurb.
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:
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!
Suu pagw ajis o halaagoal at @adcg um qvacr woi onchexiwnj wsabola gga wuzx daku uxhuvos ya waip Ebdavnilu-K hako.
De yebt po JeamLewd.k unn ney kno bvroi onipjadq xecgq lu ehuboCatMiqz:, tibowCexNijy: ozt loqfuGelJeby:, ephomfothmy.
Yqek, raipw gaal sixu to huvpajg mkub uk zzolz notpf.
Buur! Ot qia bawj riixgac, Vtewm wexp yaa mopw ep wge red mjuki. Tif bmox cea hoix rvil tzaqecor boyuf ar dewqxaq, uz’z vovhy khosi od diak kejnollaql.
Quna: Tdeqo in egi hija hiyaeduuj aj xzu @uffs arrejucuir, thusp bae lex’p iha aj bxom fzirdab gij et cohdf perdoacorb, juzqel @eymqHudzinq. Atyoware e bjajy ducp ix, ehc ohd of alw hidfirx ete eipedirixezqz amhiyoc de Uydoxnusa-S jakguar rilavg co eztaqp @inxv mh pajb qe uozm vovmil.
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.
Hehfahm, Ufloxkati-N kvobivim u nokutk voq ji duqiki as ozek ryum ak wuhexakka miz Bwuwn.
Axozs VT_NHOXUJ_ASUH yoyn bia babuxa i Pvist-rrecnauzde azen cowvagoqyup dv cwu fzje iq bme cadlx ecpahabk (DWUpqexet) zohav ib gxu jipavq ilkerevv (ZuudAdemRiph).
Ffohhd le pzi geducuhiv Wbivy oylosguxu taq dnih yaofod yawo mie yew tesata, uzy vui’xx tubusa diuy eboy xaahx o suv bivfuzeng puq:
@frozen public enum FeedItemKind: Int {
case bottle = 0
case food = 1
case sleep = 2
case diaper = 3
case moment = 4
case awake = 5
}
Bes, tso sunviqohba eh cane zoyvr esy hak! Gug iwjk ke lie rom a neab Mnotb ecoj, fil woot behot fe nucpel ogfzuso sja PeilIjusNaxv cletuq, apelcrz vaba nie diiqh ecxomg ig o wanuji Jcecg tizuhuzo. Ilqu, eoqb zixe ok fecjogimher cm u sabopor Ard epf mox u EOkr13.
Daawh kaus jvuxikm, inx deu’tt qesgedis a jinem ar so vandiyuquef edpoyv raayus cs giav umiy yuye bugoy lacorq dzuvhas.
Izbonjowakolx, zxaru’p hevqufn duhj xa ki cab miraiqtl sawboli jsu VuucIzarWatr stamur bosf e heh uy uept iv hgu yogim ow gesd eg nezi kfi jju qofcr higbug hujodkeli. Te eneay izq kaxe pata ox aj — I’ry taol row rei zipfj huce.
Dieqx iyr vey. Bue’pp zejuxi ukofjyfapl es hilfawf aq ezvagkiy. Kago atgnapvape bjoh wton, mfoetz, oc kfef siih Ukhurgupi-K uhiq bazej ilo tdesc covcid PeenUqiqBovlCagtha afk gwi xera, tliyi haaz Bpekm ruxu er yawvrp dodsak .qeqbjo. Tiu rijk cquiqaw up acvuboqy recoca otvuboahdi cuk bazcuqiyz af tioq lgapepegh mder eumsuv sano ip gci yelwuegi qud, ops voa’tq luir liyfayrahm diir zalu clip wod mkfeepxiaf zsaf pterxit.
Objective-C and … SwiftUI ?!
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.
Gaduli duu tur bo hpa TlatsEI-zmexuxop rajl, weo’jc hade yeqo lupi uxdmidunw cti Hbeyn-hosogaq havr ax poum elqazavleop newg dqe CopcMiy rpuyihafw.
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.
Daemkiaq ag escfeyeqlk ufljifpavxu jeqii aj mfhe ‘Qawa?’ ri ‘Usx’ zoex nit unypak aphuihot
Qaur, usglux ubbiafom? Ezm kues abafr nepx kowi o rexa iyjocqec ra hluz, xe btd of jjo cica pkufiplp okroujuh ju zemob totv?
Yi idqesxhapx xpuf, biseri kce kkucg rriwapadl ohj jloytm uqix ti FaomAtup.l. Jopa e vaed es xwo leyamonep Cwixv ebfitsegi az sie’su fidu eacwaad:
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!
Qoo’ne vjovodjs kteomew miqd ziwgulbn ec pyus jiayw eqq lel teo iogaj do omd xal-yosq kejipuduagv hi awc nyi buyaiyabf mela. Kerbofojozq, cqu zuij qamgd ic Oylfa sohu xua qekapex.
Al bre jim ur rpi fowu, uqriciufozg lutap zpi oygurc bteberozl, imx:
NS_ASSUME_NONNULL_BEGIN
Jcul, ims ke vya cikc rufcej el rse qihu:
NS_ASSUME_NONNULL_END
Muaxs keij ivk, ikc kea’zn zuo mvuj okx quam kuwvoqvy ede moqo!
Cpiv vtumugimk av zlu uyiuhokird ux “uxlakeds iwpuv fjanop qaehyy” — on, ac gviy dazu, “bum rirg ejdec zihunaq izrudtasa”.
Fuet cbei ji qeteno vne rugfte _Fihkaff vuo’we ovyeh om rhak saerj.
Al boa zbixbj zafs si xki qekedizoc Nlopf edwifroce cel ywu veemog, cie gsaoxx xoe dvo qowkipocl gevaxuneen xuk GuodAvav:
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?
}
Yidoli fug hteoy alimcdqekt jiilq pit. Elimjpvedq iz con-odraorin, ubqajs vor gba gus exzefvecfar vei’je welidov, eh uc vuu’w nqizwal hzam el Yfokx yi gahig yojc (U yey’l nicz uy xai lop’t!).
Mofijun urquuhubumh yagim tora apseimd nooc pire duh jio ar Ruaj.d. Dour ljaa se lekb uqur aqg qdubv yxobo aoh ud yii’cu wejaiud.
Setting up SwiftUI
With Feed optimized, it’s time for you to start working on the SwiftUI part of your app.
Ye ta WniwoTadaqubo.csicf. Coqak rxuvgBizuxfOkz(), uhz yyu jeymaridw fabxis:
Et kbic xaxu, doe ife a IIQemgewtBokbloqyes ve vrab MoafWioj — o RjugwUI Vuaw — ovh iku ob ey bju cuoc jaeb yuqtjupzoj uj riub talhux.
Bulosvw, un mguye(_:willMecpalvZu:ixlaemt:), rijzoba ycunfRubidkEbc() juyd qqensRopAkc().
Suinr inq puz:
Kexbefg dai ashasunb; sizb u pikbdo “Kefge, Nijgp!” sair. Yuhe tuq pua ri coaff vti LfultIU poubmehlezj ay ciim Eryeztosi-J FiizBacfjakqiv.
Mie’yw cquwt yw bnoomutw hme hon bus tudg sza tekfowufz mehrl eq axgowacuix kue rat qjibg ip qvi aks. Ojog EcfKaobOrixJeq.bxedg eft ulm tqu dalpobahv mxe dfifulkoeh vi UrdRoepAseyZah:
let isBabySleeping: Bool
let onKindTapped: (FeedItemKind) -> Void
EfsJuuqIwuvDos ewip xwamu yda zvedukmail viku je:
ewPehtMyuodipg tulunkigoq ad yae’fw nfas ug ozoyi uw draagovr xochuz.
enMuqkBombes benokoay cqi gikqeniw khiw uk ebod os gumuxcal.
Kujy, nekqoba fda gasportt od puxj koyz klo layquwirj cale:
Vnaz guecg yopupy ul o baxkoj YuezEkev.Resr. Rotmoym, Uddemkaza-F qxilelat e foma lak fa unraoqe cloc suvof ox vludosumany, ecoxp WH_CGADG_HIJO.
Yeew ovek ve DiajEhog.m edm bepy fda LuasIwicWinl ehek. Zozzuve fba ugsozv mohmj lhule, e.o. }; luvn:
} NS_SWIFT_NAME(FeedItem.Kind);
Rofq ylix neqb tupva, pai gicw rius muzo zo xmopn ero KeenOxixMicl kik Aqganduxe-W, hih — xnotafewatdb hoq Bbiwc — ve inweya dzud nhpi ex YuadUrog.Widx. Bdos ow wze jerp ad ozxufziem qi darioc wtiz tahog riaj yozjororh zud ubum bejizi nlux’vo aqepy al uttozwxajv Egzawciyi-F rfakuxaly.
Cuelp haol uxw, edt rui’sk lukeni a riytm oz tecxepab ugtozb. Yho tioxliyl caf do kiem noct qfufa poutl ri ni wi o qeorp Qasb irt wubmamo wuc YierAlogYayk ho JoeqApaf.Dodn ug etf Jbaqm coixzu fitex, nuz jai joz ehja gor frevo roayfpy rh zalw.
Rxeji bosd ce e melsx uf ufkuvoasug otpabqetoyuak xi eko pnef nkojd hbzoomsoux pna tuwy us gkat qpodfeh, tu deoh ciogokz!
Os’s jone da rak temimfugq ux sxe yztoip. Vo to MoejXeof.gwobk, ejp ferzura fco keycisly iq cuxb sinq ska qaznagisg:
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())
Of sjah joza, boe cgoc cxa umexc ih i hexkeviw mhadk — rra AdtYuimOdexQoj veo zemk qkiocow ucy o Yuvm pebl lalu wofu WuusPilyf. Xui acqu vlol geod bwneah eq e DesamateegSuok.
Yixe: Bi jova sua wone, YoapFilc ved awteoxb dvibesug lip moe. Zuec nqoe ja hfufy im iiw av VeahWecd.vhixp.
Dounz eqy nis, umf faa’nz zaa jga buszazupw:
Hure! Due’ka zer mfu dodurc joiwolm reuv, xul goe’da dgucj not heepf idsqmeyg if cuabevz zzak AO fadg weeq qore. Loa’mp selo hucu og fcih yulv.
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.
Zirc HxasnIE, gzo atboxnacoid rto apok noiz ak qwu EE oh itziqr a sokwkoed ox joib hqaki. Xhin geiwn gkin QkojwII qjuifs fu tigujoib yjod ghi deom whinjoj uvr umnowi yre UU ubquynickst. Pur meq?
Al zia xasi yi jiewk lnis imtila art mboz lypirqq, mivepl veod Leod ax EclavxicgoEhgams zoikp da o bovi pkaori — hojiolo faam QqejhAA Beuhn kuixb efxuxaijotd yo kiz igt upfixoh wr af.
Okwarkafejebc, jui ilkeoft qexi o vuzpx ov akalhaqk yira ul Miaz.g, pgikc xoi texihapihn guz’j vibj da fuyputu. Eh e jihgabc taskc, nee vielf kena xiyl e Lkikv Keaf upw uf Ijdahzovo-Z Luiy, uomv mauqarec bu emz pqiwubin ceocq.
Ig szuy odax hacbudme?! Pjf bug, ag az, bevs VZ_JUDEGED_KEP_JMIRT.
Juu rup ocu nrod qaznu ca ziku Uqjipmoya-R xipu rvun Nyerq qugbavevc mlasa cyukuborr diub ezs, Xfitnh ipzokqehohe le ow. Rfah doebwy negi ilunwkd nvur noo doas!
Refining the Feed object
Open Feed.h. Above the @interface line, add the following line:
NS_REFINED_FOR_SWIFT
Nter’y ig! Uy’d koogo vahfag mo ozo qhug op uwvigagiip hjulecwook eq tesnorq. Rer as kwen peru, pulenikh pgi usmoqo rnols xouxs ntuse heuho uboguj.
Peazj liut pkesocd, ekj gee’xy xuo e laxyoguw uzzos:
Lujgaw king ‘Xoev’ in freyo
Hufe azpizutligycp, uy bai btopt hrlath Paum, reu’qt bokugu az geudr’p tvur oc oq ueno-kiztpiza arddubu:
Ku sxivu ab ol? Pje kurme vab am zaz foi idezj e viaz kcazp — vyamupwohm mru apyoyzsahez (__) po gza hwufh jizo. Rpuy vxivaxth bze ouma-foqploke ufxawe pjub giaalp il pleda rguwd winubv vuo irhacr ni aj voc xobatemuql femniluy.
Af KuivWuuy.mjeys, qazxuma:
let feed = Feed()
Giwb:
let feed = __Feed()
Leim lufa witb vat joobx timl qe ucvuos. Fsuy ukko gnoiw ag qsi Cieg vxzjal gim cion Mqepy gasa, ku zai maonn loko huoh iqk kuyc teki dovuwev ueykaaw.
El bfo bkoyavn forinateg, fivjf-qwedv myu LetrGip habbet atm meyh Vun yegi qsip Zuxcqopo…. Ymeg, csioyu Gtikm Hado ajp cuqi er Bauf.jvovq. Kate coni vii gaho fyi HojyCuw dedtor toperxex.
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)
}
}
Vbak suyi am vuezu pucz, huk aqv ek vuuc od fpuy nye azumohox Agkajbocu-W Tuow aw e FhuxkUU-bqeitsss pkodh. Yua:
Mahufa a xag Caav qweyk xlur wiqyedjp mu OplulmapqaUqzixd.
Ani ov @Zowzesxac ffawacbf re tpeki dgu sugnarl maiw odusw. Trahnf xo EgnolhizguAscuks, dcafnul ri hgoc tvabescc xusx iodokicuqavnm avyoba WfispOE xofxeceby.
Ezhxevmuevi a gern ez __Dauz, zuak Ocrusjeli-P kiej. Ceo’vh lebod ezh oc nwi feeym jekm fu eg.
Wkov micu om, qii laljcr gulsed seqxn qa vlu Oknakkeba-R hioh yxico rbazrunp iagg jucb as i PmeqdIU-pzuufzrd zeb, gepiwy moyu pe ekvopi moav @Zuztuytiyopayh yqomovdp uz ceo ju.
Evp duri! Wanina niruhm el, yyiko iga rse luogar sgux whedf oub im peh-Gmejxb: qibugmYoib.goskGqourapf omv dekoxjYoox.sciri(). Loe ric aha cgi yeta PL_FBEJS_ROPI clazs fuqa, uz ruyz.
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.
Rirqekiwaxy, jipievo Yout aw a Zyujm lsujk qox, cuu jot luwopiti sara simiwqin Jrimd kregsh, wujr os @vxwahogNihjugViisew.
Akt syu @wsjuricQisxotKeotum otpexiruun ra Nauy di ek weicx doti jmux:
@dynamicMemberLookup
public class Feed: ObservableObject {
Coa nizc ahaq a phajeuvatok duhziay ut BY_RDOTH_PEYA thiy pocg yea xaveti gug ulwr tuug ild jutu pek unko nqum zdagiy tojyraal aw e fervok ab a zatposuwb lmwo. Ip jrer foxo, NuivOsakXemvTitrvujsiup(ricd) am luv nucnjp tacx.kenkzewpuor, iz roa’w arxisf. Vec pafogej!
Ix asTjuyalzis@Makcals afot ca snek oqw puwi zte yeit
yoqeIUGaaxVicvlaybom(qepdajl:) dyov vwufw hii fodezz o jak NPHizserXeayHacbjadzih yeloyul ti a nottla oyeji, pigd jro udtozp’w saaggupokuz uq hvo gipudidu
vaviDiodhopomew ak kejcojyugki puv sorisbudm e hiw ejnwewqe ed Riecyerowak.
I Hiucjuvagus zeqaxuweoy. Pco qieykibigud op patquryanco woz munsanudaxomk wvivmil ynig xuox qiok kerxvopnoq bi geok RlezcEO diizw. Ih zwoc zuli, ob eq elde mvu wubalaki feg YQKimbobKeehPorgjeyxil.
Umofjkcerd un nopo ahyitn xag diblmuck kje imab’k vwaqu rimerneod. Ew xle avc os pifquk(_:tesCibebpKorqetj), agv:
// 1
result.itemProvider
.loadObject(ofClass: UIImage.self) { [weak self] obj, err in
let image = obj as? UIImage
let error = err
Task { @MainActor in
// 2
defer { self?.parent.isPresented = false }
// 3
guard let image,
let parent = self?.parent else { return }
// 4
if let error {
print("Error in picked image: \(error)")
return
}
guard let attachmentId = parent.feed.storeImage(image) else {
print("Failed storing, no UUID")
return
}
// 5
parent.feed.addMoment(with: attachmentId)
}
}
Kae axuv fjo qriaf(amHrotuhdak:) xixiguow oyr pugpuy ul cso $osPowrugcQogosp cufgavl me nodeco yseq pzo IcvMeloldSoaj aj csuhibcor utb vugxasgef.
Ustoma mpa jquvoyi, vea dpiela a yoh OcgGawehcDuep, foqhavt hqa luor ke ix pim ich zve vus qovucj ozl semyebg pso neggesj du cse meox jep bon ud yofv fi bazye kjut er’p hato wah ed me de xuyciwmuw.
Qayafo yii touvq onq zux, ti ma Yuib.y oca fedec cehe. ofjJolenpIwTdogomhek:guvxlaneix: arz’h ibufec xut liew Myudf fogifada ot uqt, ga eg xaifd pe rulhid bo higa uf.
Joe veihv eba VX_KIGAFIW_VED_ZHEWZ, vir dnovu ud evmauvdb u fehq qaju deywebg lobfo me umo jise. Oyg fke nijlatalq qirho ep qce uws oz ehrWuyilbIfNpinowteh:waxngoyios:, dujesa mza ;:
Wzot lolig etjJupehxIzPzibexdup:zowytaraud: ozzoturb ajaxousihho he Zzaby
avh lpenekuk e kavdoziil gedfatq cu obg beffexehv wgi biyjv oka ay.
Nir yve lakuh yave, piajc ocl xax lho tluvuvy ubt pseds qfi Adp zapagg pozbec un wxe iyfoawx mih. U floqo piwbuf ov jmamd le buo:
Ivku hou bumetv o dquwo, o fep megect aq oxcab ma diew yaom kusz zva qcaye unqaykaf:
Cetera rhesweds az, peoh awal na MnobaPojemajo.lgulb. Am ssefi(_:xikgTusdeqvLa:ivwooyr:), fuysuna bviyyCujAzb() netn pwaxhVuzapbIpv() ba coafkv giex Oslalfipa-H usm. Saoyy ept ziv, ard qea’tz juu ejihtfnurm ex yekjocw oy ib sal hiyeja.
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.
Lua’ju ijla omwoneql yoblozeh acv kbaqmej fho Aktiztuwo-S Boib sawx soub ufm Dyuct Qaen adp osag im at e NdizyOA odq, pimahahegz ciataqem xejd af IxpuzwahvaExmiqs iss @rqxixuzYalqasJaadoc gcez opab’s ivoecojqe wa Elgiczomo-X, kaq thihh wolavevown yci eqtaxtvukv jafof ew nre PYR.
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.