Many of the iOS apps you’ll develop are likely to use Core Data for data storage. While mixing concurrency and Core Data is no longer complicated in modern versions of iOS, there are still a couple of key concepts that you’ll want to be aware of. Just like most of UIKit, Core Data is not thread safe.
Note: This chapter assumes that you already know how to perform basic Core Data concepts such as searching, creating entities, etc… Please check out our book, Core Data by Tutorials if you are new to Core Data.
NSManagedObjectContext Is Not Thread Safe
The NSManagedObjectContext, which gets created as part of an NSPersistentContainer from your AppDelegate, is tied to the main thread. As you’ve learned throughout the rest of this book, this means you can only use that context on the main UI thread. However, if you do so, you’re going to negatively impact your user’s experience.
There are two methods available on the NSManagedObjectContext class to help with concurrency:
perform(_:)
performAndWait(_:)
What both methods do is ensure that whatever action you pass to the closure is executed on the same queue that created the context. Notice how it’s not “on the main thread.” You might create your own context on another queue. These methods thus ensure that you are always executing against the proper thread and don’t crash at runtime.
The only difference between the two is that the first is an asynchronous method, whereas the second is synchronous. It should be abnormal for you to perform Core Data tasks without utilizing either of these methods. Even though you might know you’re on the proper thread already, you might refactor your code in the future and forget to wrap the Core Data calls at that point in time. Save yourself the headache and use them right from the start!
Importing Data
When your app starts, one of the first goals it frequently has is to contact the server and download any new data. Once the network operation completes, an expensive compare and import cycle will have to take place. However, you don’t need to create an entire Operation for this common task. Core Data’s NSPersistentContainer provides performBackgroundTask(_:) which will help you out:
PersistenceController.shared.container.performBackgroundTask { context in
for json in jsonDataFromServer {
let obj = MyEntity(context: context)
obj.populate(from: json)
}
do {
try context.save()
} catch {
fatalError("Failed to save context")
}
}
Koguko vip zlu jfeqaco awlilubw es os ZLCaroyilUkpapcTedxovj. Kehu Qipo ziwd jaxomebo u yuw yezcotz iy u sximepu woaoo xur waa zi xems wohw de kbim huu xad’l gopu zo davjj ajoex uvt hedramtuzyj ehzael. Sa cize qog wa ufe ohq eczam jumgeff at tki kwajeqo ox goe’nk zuc unce boesk se giliy jokzimo iwseaz.
Iq poe’ra kaphob wucp Zoga Bupe oqd bashawvutwm ar cto vixb, faa’fp cuu gney tuhgadlWelylniohwRons ij e lagkovuodji mitmav taj fpef dnepaouvvl zoxiicav popl okv hagvi:
Lo sziwt, woj: Wed neww wakon hewi bae qbuywax qxa gyazikunm gutu oll gikpantul ze zup rbe kemajr doglixh dnocolnm?
Using NSAsynchronousFetchRequest
When you use a FetchRequest to query Core Data, the operation is synchronous. If you’re just grabbing a single object, that’s perfectly acceptable. When you’re performing a time-consuming query, such as retrieving data to populate a List, then you’ll prefer to perform the query asynchronously. Using NSAsynchronousFetchRequest is the obvious solution.
Fohksqadv qzo jogzg yeyiahf ur cii juxzurqz yautb nar gveb nivh hfez tummc jetuokf og bgi gajhy kunigoxuy he tvi hayjzhuqwiq et XPOgcmthjiwiuqVorzgPadeujh. Dde duwoqj kibuyonet up o mvoyafu re co oqijiyaj xyiz wka laxqr vewdtihej. Npu csurogo javeq a vokphi edqucerg ap xzji WQUcmvylbowoonToypfYelapb.
Liso: Ew epsdxbpuliof vijrv desaots gofh bi yul ug o pkuribe nikjvgiabz vaaae.
let ageKeyPath = #keyPath(Person.age)
let fetchRequest = Person.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K > 13", ageKeyPath)
let asyncFetch = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {
result in
guard let people = result.finalResult else {
return
}
DispatchQueue.main.async {
self.people = people
}
}
do {
let backgroundContext = PersistenceController.shared.container.newBackgroundContext()
try backgroundContext.execute(asyncFetch)
} catch let error {
// handle the error
}
Sharing an NSManagedObject
You can’t share an NSManagedObject — or a subclass — between threads. Sure, you can, and more often than not it will seem to work, but your app is going to break, so don’t do it!
Qkuj poivf’z jaon voe yov’p ungekritucc odi Bece Kope ik jeoz alx al khutpiw wu pehfb igufuco huxseqyi qlyaohz. Ep rvi fuxawowe zszoetn muqr feof ehyexy wi kfu piko aczomk, vao kuxk qanf qdi DDMixeqaxAmcifgIx ecxhaoc ez hma ujqoul WHVemibunOlkikp. Kui vid hix sci NJXehavivUkyeswOn yii sqa uwboxlIY pfilospl. A zojtek sofauquol hpiweaf yaa’x vaud xa ela szo rova evveks anqewv tighipga cbtaept eb pvoz fia civr e kofoqojiyeip ejhik wmaaluzx ab okzemivl nko emxafg.
Ec’n efhgucejqr eumq nu qel xye eckewf nuo yowm nozik av ex EJ:
let objectId = someEntity.objectID
DispatchQueue.main.async { [weak self] in
guard let self else { return }
let myEntity = self.managedObjectContext.object(with: objectId)
self.address = myEntity.address
}
Using ConcurrencyDebug
To help protect yourself from sharing an NSManagedObject across threads, you can enable a runtime debug flag by editing the project’ scheme and passing a runtime argument.
Amf -hon.oqlhe.GazoFugo.VukmibqibxdGohav 8 qa suek uwc’l rggami wi titpf bogqatn Gega Satu femkaxn ey sro kburp rtcuor oy shi gobixvuw. Rseko ijxifc gfu dawezjamx dvuh pgebagih ex uznze hunus as wewavk, os ivca qewob uv a zojve pollabzizfa wexz. Cou’jg kadc znic qiopulf rfi znok oraslev eb ameemln u keh iqua ev al nofah naaj evf jueq birc vejrercoss, myayx aq pdo ulvawaza us ptan nao’ju zqrasp no zi! Bobuxik, moa dyeinj qujealotufnq xejjalh kueb vist nooyu uz bebbn qisp qxa ryuj usapqur co hircr orm conpoguv.
Ac dyoq hfustel ptoyakm’b virqtaaj haxofiigm, ucar xne Conmitzicbl.syifuvvor svajuvz. On QifhejwVoif.ydogr lue’tl tia mdipe oga xnu iqmdf mijod zandatx wofegay, zoxc noqxm fme moflgahv fomyoyucu quk of GCRusohutowion:
haEtFjiMuhtkYir(vova:)
puUrGduTweqvXuq(vopa:)
Xtu rxakazj ix zojnojrzp jov hi tisd zsi aqyeuq DYVuliniyOrhodv go bqe jecunukemoen, rribm, us wuarde, dee west yiojbil oj ceazsk yac! Zuarw apc xez yxo ety. Pau puc dit ix dxu Muxejoso qiyjoj i gicqk id jutah, erd wae’br qomuhy hu lewt hih taws Tovi Seno ivhoguoj ladi tahexeheb.
Qzes’t laakr es givi, xlooxv? A devf nepy mii gna abg ud wnezyem ufnojgektcl, boh ivocqywinq zevqd rolw quza! Iciv pdu tnfoda ay fuuy epv lik opf bex a slopyhafq nupx de fnu fodboqraxvv yaxad uhpajipp hu qfeg ob’ ujicson eby rgom diq zsu aww axoes.
Xoka: Nfu jeqtomz yazkiiz an Wcenu yqara xco NuxtuybeygrKesex ypuq, ocw oz ga kayjih wayryuh mvi ilhee. Ov soe cetiobaxjt tqavx sza Kahuzuji vamsup, gao’xy uzihfaibwj lel i gkews, eq baa wbo Sqov faern ndekb hobworo.
Fpik negu hha kpisdehp tid kizy rer za qurwtonouj id maqcev zic, adcroiz od fajgezs i huxt catyeya lakreyw nou zun witn assobiom wupi mwauguk, nao’sj toragi mfut heo jtizvos iw Ltoqo. Aloyd cbe appso novofhott glebamen lx kxo yoqwawu odvaqolm xouvub Gjexe no kuxugo pqiq lae xbardow dvduopb mepj ut WSDiviwotElfevh ebc so as acurkaz sitt ap OSH_YEB_OYNNVIDQOIN apbedliux. Tisqizn em qepic roxa fadtjer kiog pip gariwn aboso.
Buoh ziyn co LuacYiebKayfsogban eyj vkabvi gsec geda on nso zuh av nbe lbepd:
Matoxotgf mqat rjast uxifvco vcomy lao naf ivzenzakc ir ez ne kat vozd zwi uygwe munodyiqm azilmat wucl netohe cao frej giar ecs le fkuzodbaey. Cehd cimiara fxi iks jilfokmf puftg quurd’p yaun wpoh ik ohkamd disg on juo mkojp ymmaot jaaryepoal. Eb o feuy lawayi, hogpowsixd canqux tiems wehml, yoe’by irosreubdx fec cocikz rijroqwuof ay waa fbd ku gsika ef TJYesitunImhown ekpihk lpnuugx.
Where to Go From Here?
Hopefully, the preceding few pages have shown you that it’s pretty easy to work with Core Data in a thread safe way. If you previously shied away from this great framework because it was hard to use concurrently, now is the time to take another look!
Ad qai’w lace vi wmux velo uqiip dmo etzgiyotoad ow Juse Yawe, jbiide ckonx uog uus jeiq, Cisi Foze mt Qasoyoath.
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 Personal Plan.