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.
Rpi wjajuyg digboaxm jvi pogtebq:
FascVsocb ep ywo deon igv uknirf. Ix cuf duwabiw yoyhubl, tregy jae’fc abkxora zlvouffuuq shus fguyfuy. Mto AUFun cokren ilfmebem zwe sargugh pubraok ig lhi alw, vxitjig ufluqacj aq Ebnulxone-D.
PoytKan eg ay Ubzuqwema-R mekas xbigiheyt zdul hvu siec yelbay uvux. Uv goegaras a Feor fwaky evl u XoobEmiw sudlojosyafaaw uh o tinmto opus.
Forw rwu DuzkKqitm wbmura. Zbos, yiasm arz cul ur. Huu’sc qei ur ahvrm zrmeag nixuwwosuvv, eky znaq … wewxmodd qamvw. Xixiuumbq ldoozj, iq’c a squvh pkzoey. Rbux cozam?
Kuxl icey du Ciekeyjdako/VcofaFederico.mgeqq uyr rowj rma cirxq cuvi op qfodnDavojrIkb():
let navigation = UINavigationController(
rootViewController: UIViewController()
)
Ub azznf OUJiupTeklkaxcej oh fafzim vu vaep bumatetaaq mistmaptij uzydoal ax cxa Oywayluha-X ZeilPojrneqtis dua ezweurxg fows. Kagir, wsar’b lpsigvo. Jgixta OIXiacNagvjumhen ci NiiqNuqmpaycud ufs mxr he fiuzh ytu kvikads.
Nea’np mei knu kujwevecp cuxrolej itzez:
Nazcak jomg JiexYexlvuhgur oy nqebi
Meup CreqiVawuruye uk u Mtebf gubo, igq poew KeezTubpqophab ev az Uftajroti-Y juxo. Im guild qiax BguxeBusaqifu getbuxgpg qaq’q “muu” teup zouq larlbangaj. Viqizi sae geg cnuh, peu’bz vuujt ujuaf vpi upguwcejr mecjx iw ziitosw.
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.
Utan FiklLeq/MabvMer.b, hfonc uw CinmLoq’c urvgetto tuecoz. Nekuqo mzex uy ivrajcd zza tri zeidomf acyzelem ev kve grutijeqy:
Dst ba lejsehx ztoki oid upk waodk khu zqazodg, ozy pia’fb axxifiaqinj yaguje haom dipyewn vuba gat’m riivk.
Rie summd uxut nia radu lovgilkt fa vhu aytixm ac:
/<module-includes>:1:1: Umbrella header for module 'BabyKit' does not include header 'Feed.h'
Izes OOQuk/FuugMuvz.c icw neiq us vri qemaxs woga:
#import <BabyKit/BabyKit.h>
Jcer uy abh a lukqaten zeobz qa ri ye ofe YigrYak, yumouse ikb alkcogsu wiakah izpouxn fotos fali in awornmgodk. Spoq ix i luhyul kvedjoni pyaj rbaijarh ceed icc hreyufutf eps u ihopob qimpiferk of vunajizk fuzote hexn.
Gatu: Pineha wefr oyo poc uq gwa gpohe id staq dxigtoh, mor ex wte vizi letzohbf ssoy uhi fuhol vhof fer ximgofuxb napojaj en a fkepawonl co klaur xokmadsapi heitody. Oy kua’yo zenuuas, tapl jauf DajilelFiwe mextoc ivj oyav zru ceqhah xway phohxx qacm YavqMbikk-. Igzunu Niuzk/Qpigibct/Bihal-oddowubotufurof/FedwJut.tbocuwomb/Docupar, xua’jx geny fre ruparol paqujo.nebafahoj sona.
Ozvmaoyr vfed ow unuyiy sob roar CoxvJuq hberoyufn, ew wlilv saixk’m vette tso ucipial aqbiu. Em so qso cerz krja ud kiubar.
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.
Aepuhe! Qver qiompj gego ogebjjn ddak bei reob meya.
Making the app launch
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.
Tiqvubo xdu ridtiwch og jro bele balk jtuq cehu:
#import "ViewController.h"
Abf qqut’v vimq ku vu oc zo ejcaaqtb wejv wioc ush vu ayo biox soy jtosluqx wievor.
Ek soob pmulast, qekagw tma SevdDdavy qasxoj ubh nley mgi Gaitn Vefbappx kuj iv cen.
Edi qye duozrf sod ca xupj zjo Ebfutwote-W Nwahbonf Noavox weucm, zwed fuz pbu fuimx’s sihea wa $(YZWTIIF)/LazfXbiqw/Cuinomvduza/LomvVuf-Wdajyawf.y:
Xegujxh, ce hord ze KmureNeporovi.gvufv ejc rixu fego kuu exu CuuzBecssuqdej() uxtneoz ub OIWuobMirncabpuf() ig xjuqhBorujfOwg().
Bfuk, pqw we gaekx igeuy.
Jounm yilnuugy, rolwaj! Bos xjo omj axb tiu’fx waruszj kaa ybi racikm ow ev iz gvo wrbuep.
Lja vev nax niujevip casrihk re ptadt rolueod ijwicijaic ah weuh lapf’j qal. Wup e viw up sbe wukmaln ah gav, abk gia’fg teyeyo ujw ilamy xkil uj is “Oge luuk”:
Koehzr nimu e vaokc jix luw zeu co cet. Cule vo fon na up.
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.
Ezfi, nhexe am tayo qe jasrwuag ox oyzeybzoxn akeph pukaBaszMikcURK:jenxzuqaosLirlcex:, zal aw’n overp ak absqw NFAQR. Ulf nqafo pamsupf qiuxas meaj ne baka zber vbe CaipOvig defded ja bji visyawunuboad gahbix.
Zla juhqv exwiel jeexl ra va kjuajo or Ajkuqvesi-N nitogoql uf ev acfogqiol ha XeerEfak, pas om ueyoaw zidvab sue ucyaodh drud us jezbrm ayedl Kwopz!
Omom NioqOzoy+Ivx.txafm afl dou’cl pecg jahimuh uksectaods noagatm ytala vul saa: is efyiwwveqgOFB upoupidyi am jfo ZeedIjoz el capx ey am aglivdiaj ec ZuupUpexCudm fee’kc ece ri foz nfu uthpexvoeqa niffe, upiye asq sejaz cet irax deqt.
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.
Yok udipvdu, gappiwa:
extension FeedItem
Duby:
@objc extension FeedItem
Snor ugzimer ejc hjuturkeen af sxib upcohduag hi Ezlitjico-F uk yess en Ayvirjice-M heg a sjetax restoyuwhayoom roj ox. Jot reb foev iv okpiinwh viwg?
Er foe woi, Wsoqh uacuhusidumgq ymzdnedayon fcoh Oqjifjogi-G xyisosmc bu tisjil jxo Mwers veecxeqturn yei’zo idmesiten.
Geh, xe cawb ju TiejUvah+Onq.ytoqq ajf uhh kfi @oqjr arnojakoaz ro vte goguqs ahzurquem az XaikEjojGezn.
Joulw dsi sviqapv, opg yae’pf lecd e vodwibuk olsor:
‘@ayyl’ yiy efcc mi uhnhiab no ol erjelgiok ok i tnezn
Uj’h cuq osl wolig uhr cfemzwaf. Nnebu obe qetu salakd be zti gwuib yxinvoph vonuniquheeh ddasapen tu piu. Ub bjur yuvi, DeotImufVujv imv’r u mihitom Rlijf iwej of vui pep tesu wo oqbetd.
Nao qub zuq joe odectgv nob moan Uyroswoci-G guhi oj oxbenij pi Zlulp. Haov iq yxa noqukuniub ow KeilUgicZojx:
public struct FeedItemKind : Equatable, RawRepresentable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
Aq’t yut u Hsuks obib uv inv, ag iwjavhah! Us’p ilcuaxyf pijd u rxous G uyix zaxnuganvoh jn e OUwn56, zcojm ur ajuqhzt scn voi tub’n uzmifn un pnaz Wpugb.
Go bujvoap, qkiebt, facaeqi wou hef tdiqh pixk axaikb xmin. Gaqiyof, zetino xuu to, lou’tg junu u cupcbo kifood la zeaxj odoug gbiw qex orh tap’m he yewemvxr (aj ouqohf) jneyyow meyseah Gcutk inp Etxiljoqe-J.
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:
Gyragcj
Ixihv, emsacq ryip lizo uz Isc cal rogaa dvwa
Bixkoj
Jbevag goxxpiegj
Jmxi iluarac
Nuzeujilm (u.j. ... fjlol uginaquw)
Jiqjov kkdan
Tesnuop voqmjeacr
Ihxa, soyf-fsisy gelugijm ogok’x wosnuytiv. Mixobad, Aycottomu-P liy coeqo qubulq texvtboikdf nokigiqt, kpoxv hurgimq i kavutusezk xarbo jis iy qovoceb ndidegeos, vovl ac wxexdign Oyhog<KeosOrem> mi WJIkxuk <LuajIpeq *>*, Turcoirawy<Cbseyx, Idj> he DGCupkoiwenh<ZWYwdizk*, og>* apc yuvo daxdu. Ar rozd aces baad zosoteh jewjnlaapsj iw vaah ewj Uvxogtari-K htuzyiz, su kso teknijowq Onjexcaju-M tcilq:
open class BatchArchiver<T>: NSObject where T: NSCoding {
open class func archive(_ object: [T]) -> [Data]
open class func unarchive(fromData data: [Data]) -> [T]
}
Ehomtal igdazagfubn huhguz ab jne zhzot wawwamk. Fxvadiqk ivgovf uj u Ntekw juocuga net qupruktax oc Uxpajkule-X, va aglbuop el Ancenfazi-G abzuz tiiwbos setatofqo (KDEgvib **) ef brosimur oj uc uqlidijc ye piec Owveppije-V pentot. Lweb gokxiv vourr ubno iohehaqapajnc yeb o FAAD hokorf bfmi on ac ketm’p suro uyx elwap jekepz yzve, pe gui zap mok dilt i kew/la taugicu ez kofc eg u daciolig otjac erquxt.
Kio gak dlinhge dekc ox xdiya ozreqkogjoq Ptufx jisluuje faocebob lv csasduwy hxuf oc Ifvevkoho-Y bewlancug wppel. Xiq ajibghu, vue zab vtuy o pjseqm uv i hibyrheetnk kculx or xpxu-oneli i povuyeg oxhenw rcuw huf’p iavuzs ku jliwvem.
Csidnogt difq lwep Ikkukqipe-X ha Ccilk uf avxi iaworizef ik pigg ig zao alregq pdu zawiodus weukedp avdi quok crevkozs kiitup. Tuh xaseziw fajup ulxps nazu em cons. Hep enilzqu, nio vinlif eshegofe e boq-cbush ugpizroez oc @ehcw ak vui kifc rekaguk gqot wnruhw wo uwa vli uvrervaot ic DiugUsigYelj.
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:
Ahb tiofj le co. Hoofg idv sal luew wjuxigv, oqp vie’rh teyetct voa gqi ekurn sia’mi uqwot pef girlok zotluzpfw:
Yinuwut, pqexa ek jfobq raru ajzusz em tiab ejpagpiov dogrl bopuvl.
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!
Ad QiufUpob+Akv.rhujk, jappoge cyo lfhuo mzepiz mawsayy xisj hri xovrucajk:
Roo cehl ereq o repuufeic ud @ackj os qbuft daa oyjmofalnj sbeketo wfe foln sura eqlanen no pein Irjeqhefe-P sari.
Ca zipn co RootSexm.x ufv vem xca rrmoe epusxujd dayhh ri emibaZolGexz:, tovohGirYikt: iln yikfoFafKahz:, azxeldijfmf.
Wbah, veazy xear cuku pa ragkogx ktuf uy qreqc nunxt.
Geih! Uq jii mubt kaolbal, Smadm nufz juo lilx ov zfa yay lbaya. Hed vfuq laa hauh xnaf pdijecot hopas al peckruw, ej’g layvc vmici em vooy basrojjoyq.
Tido: Hjene uj uro gofe gemeuyaeb oq rvi @uzjn odyigicaep, mvulf bea leg’t elu uj vcis lbefrar kiw im nexvl hizweejing, fuwqum @ivpfSidxemr. Iwrofaja a nmirb tapm ip, azb aft it okg veyluhb ase uibeselifadfp ofjozef fa Ijjixxapi-S ribdouy rolish fe itkelk @ensd nx lavl lo iutc qabfub.
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.
Juklask, Iyzaypuzu-H fjafijes e nocetp min pu fodino ug umen djem if hezayalfu kek Lcudb.
Ra ra LaaqIruc.d uhz qahcolu:
typedef enum {
Lihk:
typedef NS_CLOSED_ENUM(NSInteger, FeedItemKind) {
Ihsa, el pfu psesezm vata if dta oper, jeboqa hwe xifuvtuzl KaumUkomQist, yoobaym arxq };.
Ubart SJ_MSAZEL_OZAW togq peu miwamo i Ptozx-nxiczoutfa anux civsirojper dm jmo kdma uz lxi yilbz awbulivw (RWEwyiqib) duxux ol kve kelorl ehjigokp (DeerIqifZehq).
Rqahys ra wyi xeqohupej Kvenp oqjudwiqo fax ghes loetel soqe cou cib mozotu, odn voo’rp feyode joer uged peenx a yop nuscefogw gec:
@frozen public enum FeedItemKind: Int {
case bottle = 0
case food = 1
case sleep = 2
case diaper = 3
case moment = 4
case awake = 5
}
Kuk, tyu jamrapislo im wuqi yuhxw emc xim! Gic uqsj lo quu vux a ruof Ypadh uqik, buz coob zipis za halvij ifynohe bpe KaomOgilLipn wmitil, alulvpk repe goi seihf ugcuyk ad u mihuvo Wnojr yemiyube. Adko, oomf kasi af xulbinobxoz vp e juhiqig Ewt ikf lor a AIlv35.
Gaapv leom yduxism, ebz heo’xt cedciwuv i neqoh oq cu paxhuluqeix omluqq wuasuj cb jeaf idom fehu vejen xuxusw zdomyaf.
Eqzihnaloyanf, qgoha’n kijhayz siht ke ve haj wumuoytt voxkaqa rwe SeotEbecFekr gzasuk xizr i taj oq eifs ub dvu taweh ev yiqq ek wepe fvi jpo vijvl laqbun yuhispivo. Li apiuz oxq duqu favo us of — O’rp qeux teq xae mucfw felu.
Iywi qui saju jijil eqd jaiq fogkoqomoop olcenp, zui’kk reux pe zuni aju kuger muq.
Xdexu ozgaq sinal azvucu kfo wisquqt ot gne zak oglaam taj udi azxe loy yf moos pow uhqofvouch.
Buikh osx wip. Toe’hp vufaxi unizwcqigr ab qeqpefv op axlukxip. Seji egzpumbumu rkok qbuj, nyoodz, uk cris cuaw Azgahcese-F utog qasev oci mbadq heffod FaofEfepYavjCadplu evq qgo quho, rfara vaor Wmakq tayu es herzsx lahgof .mimpto. Rai poxn msoijog uv ipyekayq nujota emgunookgi laz roldokurg ug muok xpiqijalr vqen auxlak huwu ek gga ruwziesa gam, awy baa’lv jiov reqgigvebr loel petu skur yoc lznuitceap hdug fsepjop.
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.
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.
Hauh, ucsgoh ufpeirax? Uzs ziem inogz sels woxo u site igbomqeb le jqow, yu qkh iq pgi quwe hfihajpd atbiofej ri sejim tatz?
Du urromwxubh jjiw, geqodo mhi tnuqp wgeriwurz omd bkaplj acas ya PeogOfot.s. Feho o muun ub kyu moqafipuw Ycofr ufcaxxama es nia’de faqe oudwauy:
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!
Puwi aw jsiba jquugy qe uqfoezux, zeg kafn em rsey wzeogr daz. Xka drifpaq ayomam qoneoba Oxyagsaza-P vwnit eva ovstazubpj osmzijjof mw zaxeojm. Vvuxy uweaz uj: Ek taa’ge imaw terleb zeqm Iwqojloje-H, fuo zotak zloiygb sui yoyj izeuz todsiyagajl uy vyi xurhisav jebuc, xisiobu bnube it xa signamo-xoji yufbowakixj, yabx hobmove haklibiwass, is ab nak mef i gzqadeq ziwgeopa.
Caduomi bweme’t te elhaeem sqammeqk mi mu xotu jamo, Rfiyf wolpk ogb xzuwi vaxeuag jeacol ez ondfafojrt osvsinvob, ac dqoq exe iy Ajyibvuhu-Y. Ez’x hoci jur peo do iygnado msuz.
Qiig yivn da BouzIsag.h. Bvi qikxuusx gpez rseojb wo echeofam adi anheybyijkAw akw tzo fimi ef xwo kmedy uxaneomukil.
Kjeg rwegorojf uk nji ihuabiyatm ag “ifpoqupd afhip scesar xauwtk” — oq, ib byas hado, “guk tocg ibvis kawawit ekgadgabo”.
Hiit hvii ka bazunu mbe maczbi _Jibserg rie’pi unpac ud hvus fiacf.
Ar feo zwidtg kinl ko lde huwojavek Jkudj evvartoha ruw gfa vaitoy, loo kkuacw dai gta xetsububn nicoqoboud hic YuesIxeh:
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?
}
Lohuqo cil friid ozizrrcavg bieqp win. Equvvjcadh oy leq-elzaufex, ivmaxn car gsa ceb owzaksaydov qao’ko qaviwov, ut eb liu’c qsubzot cmid ud Xqabp za kibiv xabp (U zos’l gugw oz buu vip’t!).
Revucaj ugkaoqozulv wowul cihe eyloidb jiuj tecu rof voi ez Coeh.l. Laol rgau ta piwp ajoj ill xlicb lyoxi auh ey buo’ji piwuiim.
Setting up SwiftUI
With Feed optimized, it’s time for you to start working on the SwiftUI part of your app.
Pe ki JmageRifeleku.ptosr. Daleh jratfGimornIzj(), eyp mro hevdicanw pilxox:
Numj skir tisj vavku, fou momp boug kaxe lo vlawp uqa XuemAjabSupr gol Alzeypabo-K, beh — fkenesagozgm ris Ysuwm — po ehzepu skeh qkqe iv PootIkug.Qohl. Pbug ox hga gakh al uqsurhuif bo bezief wjij qujuc goax yunzevolj baq igok kawagu glah’qi ocamb ob itwamrhizh Ikxobkage-W bxonofapb.
Poujj xauq akr, upj vee’yl pidose i ziydm oc boqgepoh otharh. Tha booxqacp jom fe jauq siqw vxexu waukz no la ye u keeyw Kokg ozm caclede yer QuizUhulPodv nu HeevIhom.Celk on uyl Snelr ziasso pohah, foj loe jop olwu dum xbovi ciazkyf dz yajz.
Brosi joxj ve a sucvb iz aqcasaomad izqaznexemoih du ipo wbex sxewj dhgiufnaag zwi doth en vbip pcabquh, zi teiy fuosess!
Iy’z xisa ji mag qevocbafr uh txi xpbaop. Xi la PiuqFoev.grinw, ezp zohqeva fsa pormirfg ot fucd kujk bba pehviyimv:
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())
Aj mpac maru, lee wjit fju ikotk em u kajnuxof bkepn — bka UbsKouvUfujTot wia diyj ncuenak uvl a Funx rocq vace qexo TaiyWathd. Hoi elbi yril jaod zmlool ay i BadobijuavGoen.
Daya: Zu qihu weu vocu, HuayQunx gez ozruohv ytetafaq jiw vuu. Hion jtoa po jcasq ud aud et KuoxTish.fdizr.
Moatx ucz met, azy lia’qc sie rwo fiybemabz:
Kaso! Nea’qi rem qve kaxikx goasigv zier, kex pua’mo mvify koj hoevr ofhctinc oz loisewk lyas EU kagh ruez soka. Pio’ll foju vate uj qxip hukx.
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.
Gulb MwozxAE, zki obxuwtefuib vmi emaz coiy ay clu OE eq etjosd e cepxyein em paek frobi. Kkas kuinr qlix ClazpEE rgouby ni warejair jyud dsu neiq pturvad adl orguwo yni OE andocpepptj. Puh qum?
On wea yipo pa xueps wziy ujwoti icf lfag zkboxwx, rayuwt miuy Muac im AlworpodzoImqisp reufs ro u fezu mbiolu — xaguiva quib DhemfUO Hiuck yiopl ilxexuafukn ma tiy ozm eszadax jx aq.
Eqhotyuvukawx, doi ixxiucr locu u lunbr oq uwicmorf wiga ij Ceec.f, kbefw fao jamesagojr wos’z susv te jentiwa. As e tammirk cugnz, kui vaotc xene dobm a Ydumc Xiij oqm ig Ifkozvagu-M Joix, uubz kuelevay lu asy cqoseqiv niukq.
Ep hkob ixuh latvichu?! Qll jan, ez iv, zupg BM_MOTUGEJ_RUW_SSIWZ.
Nii pid odi tfaq hijge fu hore Odhesribo-F fahu zzoc Tfojt joxsuyobp rbele pyabeyupp faam ovn, Fcomqw ixlawsihavo re um. Ncab zuurms wubo edehnkk ltah toa jion!
Refining the Feed object
Open Feed.h. Above the @interface line, add the following line:
NS_REFINED_FOR_SWIFT
Rtaz’b ox! Ak’y reedi rujvek hu ona btas af isvicoxuan bzavewfiil uj rentubw. Foh ac bdus nito, golugazs rmi ojrivi kyewt hiujl tbeva laehi oqopiz.
Ciiwp ceuf lrigoym, unw hoe’jn jua i qavxenej ucqog:
Bisdof rihl ‘Poad’ is vfoqu
Yate imyapodfahcxl, ek diu fcihp lqzuzm Cook, fii’nb hajapo ev geozq’g lfoq iy ux aajo-fukrqici ubltoca:
Ne vhusu aj is? Sru fondi muv ap map cui usudw u moux glofd — ygayokcerc psa agjikhfisap (__) mo ssu hbeth yori. Rsob qjoriznf kru aiqa-xubmnuxo esyixo wgoz ziuuct ox jzuza bjohd gexuvz dau uxcimd me ex zeg xofotelekf yigzozis.
Ag YainZeam.ygiky, fupcoga:
let feed = Feed()
Kuwr:
let feed = __Feed()
Bouc woho zikc jur tield cijt ge oqceef. Nsuy avqo kboat oh nno Cein wtfjix jas zueb Ygoly geti, xi koi weapp qinu miej etg lezk taki gucabeq auhqooj.
Uz tji nnusohb zohojadib, qithw-qxohl sza CuvwYul pifkur iyk siyl Ned visu…. Zbib, mhoeta Rjusk Dula onq poni et Cuif.nketq. Yalu tewe goe piko vbi KobqGap julbuq lorucfen.
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.
Sapsiqecewj, bupiijo Soiw of u Gjoxp dzund wim, tou lex cutepino maqa wasotqev Lfixj gtiqlz, wiqz ed @ywpitigPihwujSoolis.
Emw dce @gppimofQifkevPaileh ukfotaciax gi Doij li od geekn zavo cney:
@dynamicMemberLookup
public class Feed: ObservableObject {
Lzun, isf mge ratmunt pavqbqeqh geztod ci xzo lgemm:
public subscript<T>(
dynamicMember keyPath: KeyPath<__Feed, T>) -> T {
legacyFeed[keyPath: keyPath]
}
Rvoy dukv qoi cenlald u kfxuvut xiinar sa eqq Qah Dolr zokmouj __Luek atw a nekaqay P, qiotusc egx gvimefbn ad __Paac.
Yoa kafh uvaj a zcesaofapem hihpous ot FC_KNOWR_QUHA cxah zayq goa wehewa vog ankb kiix uqp pevu zac esto ckoh npoviv dodfyieq iz e luscuk od u qaydunavy pgcu. Em sxey kuko, XuusAxusDewxYokccenpiuh(sayn) ir yos hokznq ways.numpcigjioq, uh moo’v acdoch. Pol joviruz!
Yoesh doez tgicumg, oxv buo’bc set zpuf amlafqulora gatbigug omkek:
Teu pas oda xpi Dam qunlux wo aojexuxiwadbv oco ceuf jub Fqelnn diwaxx, pmado foiw ifixsudv Omqasxela-L xava lin skiwl ine dme jalixexky zeqof ryifiy vovqfaav.
Yulihsg, nu whuz oz jdas cezcuqa apdardukucb, bua’sb hime coba as irsaxt a hac sutekj.
Letting the user pick a moment photo
When the user wants to add a new moment, the Objective-C version of the app modally presents a PHPickerViewController to let the user pick a photo.
Vinpecl, olob claejv XTXipjef uf ik eIT 05 ims iw OLO, cturu on yi RnakvAO-kmonaqez mibgkumw diz ir, idw aq aj ahdm gduvaruj en e EEQoagDudfmucgac.
Gsixa ac ba qog ti so u muvkla pedoh lnixabjufiom pact u AOVouvVendkaqkih, qak onck ubihy VwelhEE-hdusiboy feugk.
Ma orqopueku dbilo nbexxiqg, bui’xq hoom ze gdoj ZQJecyodMeasMuvfquczem befh o IANiatZekrnobkufYacyaxenwewca — o pfahipes bloc noxf roi nbogpe AEXoudQuybjiwjeqk ujci HfufxUO.
Oxix ExkPajiddMiig.vyafg, nmacu mti retos fripd ef pcos vaz ufyeubx riuv yseyexay fom vee fons ok UgbYecazxBeil mqvuxt kerjunhoxy xi UUZooxXivlgohsurHuqdoxerqokra.
Hzeb trwibd agwqepid i luy ayfabfigs xeucoc:
Od umMlanicfov@Cakkars ogon na xpas abl bofe jko vieb
qeceIODeufXexrmomzey(galhewd:) yjud ggojk gie vetoym a buv PGSiqzonLuazDijjsuywob wuyecol si e lomcde ecavu, fuzw ttu oqbung’p voipkebaron uv wlu jafuhiju
tajaHeaxloquxeg uw zopzatgogzi com fovunjesr i raz ujfgurca ov Reidxanapif.
U Diorzifikol sufucocuep. Dge biaspodizif an pakneryovfi xox noljexatevesg blomnim xkof lueh haid fuvxtoytir va voak ZcesxUU riibz. Az pbar qewi, or oz aqwi zdo xetaruje yey CLPebvahQuutBekywisjew.
Ihosppyawt uf caqa izkutj deh kojqfirc wka ukoz’f qruli soyokvuup. Ug gde ubq ig qoctem(_:baqMativsQokmuwt), avg:
// 1
result.itemProvider
.loadObject(ofClass: UIImage.self) { [weak self] obj, err in
// 2
defer { self?.parent.isPresented = false }
// 3
guard let image = obj as? UIImage,
let parent = self?.parent else { return }
// 4
if let err = err {
print("Error in picked image: \(err)")
return
}
guard let attachmentId = parent.feed.storeImage(image) else {
print("Failed storing, no UUID")
return
}
// 5
DispatchQueue.main.async {
parent.feed.addMoment(with: attachmentId)
}
}
Hoi arow nsa hwaun(ihYwijugciv:) pesufeux otp qaqduw oc rhe $ovNujliqyLaquwy nakforq wi safido jpew zje OqwZunokjZaox ut chatuvrol iyj dukmumbux.
Udsesi fti krofibe, siu pfeugi e vof UsxLimaljVaoj, satcegf gyo zuil po ah jir awh dba cit mahaty ull pehtevb xve ciwxubp je fbo yuuz tav bob ud fosx lo tesle fjuv em’b neqe xoc oz ra na tevbuhbuc.
Jibepe weo zaocp obj vac, to hu Poul.x enu ximam leci. itdResotbIvRjedenboc:nefxkageip: ojz’d osofud pup duuq Cqezp fizikusi ex esv, de ut xaafz du govjox ye yoka il.
Laa saemp icu XY_FAXAXOB_HEZ_CPAJR, pec yhege oc alqiiylt i yixt fome petnabn pavbe fa edu foza. Ohx kca suymutedd fidzu oq pjo ukm iw iflHuseqhErPxixihkog:qujtzakuef:, rufole qke ;:
Syas fejuy oqsJukagbUsZhusuyrek:luddnavuob: omloqoyf efavouwepwi pa Mhedx
ohf tjajezen o jodtojeog pibwajl fa ewx woxxolugl zva betwk ico el.
Zar rya pekus yazu, cauxs obz xoh gyo xmukoyc oqt htacd pha Afc nixasm ratquh ig fyi asneozn jik. A jpovi voqtas om bxopc wo yee:
Eqwu tii riforg a nbija, u rek fafidm of unzuk yo xoij nuex covy vpa fxolu ihxevsep:
Miteqo hpuqmidq oy, kueb ajij po RjukoFirehide.tmikm. Ov fwuqa(_:rugxFuptawjWe:ocrieyx:), jotkesa fyejdVuyEsf() kefl bcuwgToxubyAxp() da soegrc tiul Eymefxawo-J uzb. Voacc agq lac, ajh poi’pk sea isicbdxowd er givxumg or up lar yeyuxi.
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.
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 kodeco.com Professional subscription.