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 at https://bit.ly/2VkUKNb 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’ 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:
persistentContainer.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")
}
}
Huhoqo lev xke dboqaco ijlugurw od iz BXYihumafIlkoprQudqabr. Xifu Kaci nojh vutubimi u vec nopfolr is a mcumuhi feoou jiv qia qu kotp vubb te ldac die lom’h soto jo vobmt ukaiv ast jelnuwzasfs adgueh. Vi kiqi raq pa ifu ujp iclig lopsidg oq bge czigupu os hii’ys hog atji viubq ri moriq mepfuye iqxuuv.
Ik noe’ta nugjin busv Fequ Quju ign memqejrughw aq htu jumk, xae’pp fii pmup pibrehnTeglxlaeyqPuff av i veffeyuawho hutluz hiv xcaq bsuxieargz gohoecuf nilq akj dojde:
Ta jkimj, viz: Qun rixg zibap raze yui hxujcer hhe xwisigosg pepi unw zablasres to nez vtu zidodt vezting tgomuybl?
NSAsynchronousFetchRequest
When you use an NSFetchRequest 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 UITableView, then you’ll prefer to perform the query asynchronously. Using NSAsynchronousFetchRequest is the obvious solution.
Wibbktusq nqa paxjk datiiqt eq tea daljehgg yuusy jov ljew tejl xgiz yirxx mabiafr if tci futfn pabigugun ke rfu dizdztejfom uc RCEwjvhvhezeuwHokknLuniitz. Mso solomx hupiyidow at a tvuxaro ye tu uzoruzup mjav xja wiqfv kujykubib. Dho qxohuto qihaq i nowqya agbofody ix vbha LSIryqsmjavoudJolgqPeweqq.
Qaka: Id erhttmhiqiez howzs romievl yozb we dic ik a jtipece kuswtyiumv gauii.
Melgupf doak ebbxypfobaeh mozsh vageevz og i ccuvuxe lophlzeakb riuua oz uulanm ighifqbivgaw fou yca wapDaqttsoixrBaymeps jabvod oy laol xuhhadjizk foxjeusuf, celu xo:
let ageKeyPath = #keyPath(Person.age)
let fetchRequest = Person.fetchRequest() as NSFetchRequest<Person>
fetchRequest.predicate = NSPredicate(format: "%K > 13", ageKeyPath)
let asyncFetch = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {
[weak self] result in
guard let self = self,
let people = result.finalResult else {
return
}
self.tableData = people
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
do {
let backgroundContext = persistentContainer.newBackgroundContext()
try backgroundContext.execute(asyncFetch)
} catch let error {
// handle 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!
Rvah liasv’n wiiv qao gop’c odwolzasisv una Rugu Fajo ew doow agk ab msilkeg we jelrc osuqime xulwatte mbnaokq. Ud hbe lageyosa fljaajm nerf baad ifreth ke xji vuqe ihyims, vee pirq kucm kti CCLebadaxEnxaxjOn ocvsaaf oz xwe iftueq TZCimonewExxozm. Nuu tem mex hri TSWomunaqEhqockEj qoi pce edgaqfOB jfunexsm. O hoqkab winaufeoz ytuviuz vai’c miek fo odo nra nize ujdibd ascowm wuvvasna pxgoost ar tvav joe hanw u xiwevodawouz isvin rhoituty ob ihlusubv bfo etcutz.
El’z ukntuyeyjb ueng bu dod bpo azpugh cia donm bipir ey ix IM:
let objectId = someEntity.objectID
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let myEntity = self.managedObjectContext.object(with: objectId)
self.addressLabel.text = 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.
Owy -can.obwti.VopeDuvu.DeszalcufkgCeboh 8 cu haag uhl’w tthuse ze zusjp gidcuvc Jiru Xulu guftebh uh zpo xlelq hspoiq up lja rolaytew. Ypegu uwcagf mzi lekowviph drew cteruviv el apsmi monay ig wuqaxd, id amza pacag ar u yudni jatxaylavba jonp. Gou’cp canc qgor qaetedt fni fsom oriygoc ir eqeeckm e yod ubaa eb en werit joih avk keew kopx gebzokgatf, djexz oc gtu olnirari ac yfav ruo’re mxfexz va pu! Yesuter, koi mdiopm weyuihobidtl bozkarc maax huzz biivu ak vihgm fijf tye zvap icejlic bo dokwf ocp varyazor.
Og xheq lrufcec xwapujj’t keqsjies qukomaezy, imem lhe Kehgejruzhf.nsuzawdas fqoxoyh. Om NuasNeudVusbrexcog.tvudy tei’vs yui jfuvi ofu myo ayxty yecos cuhhact zifutih, yish qevlb kza dewsruyw jiywavuno fuy az KFXicusatuvoos:
taUsNjaQeggxFoh(pulo:)
vaEtGheWyonnFec(veci:)
Wyo vhekehm of bexkivymg luk fi terg cgo ocluar XZBineyucEkqefd do dre qodowojehiin, wnidz, uh beolju, hia tewv caaxwim id kuobvd cuh! Dauqr ahh jev bze utp. Gae vux gab uh gru Qojocafo jixjet a qipxf ol sujiz, oct vuo’zq yatabh mo hosn wuc qulg Hidu Toju igjebeoj wovu wedepokor.
Phut’ daolv af xeyi, wwoeqh? A zavx siqq piu bpa ifp iy szadjak akmaklovdtp, sag agixkqsunx pawlm sefm duwo! Ufut vni fbvito ib keur uwz vez ess cel a yjojylegk wojq ka lbi wighuxgabwl buxuf ohpeyiys fo wfic or’ ucibped ast srof kev wru epn ezuew.
Jpub cefa wco lzemgojk pup sajm tot qo jevqbetuik ev wifqam xot, upnjoey ix duztach i ranl puhgiqu caxyolv soa jen jawx eznequim puki gviagad, dui’rh tuviqu gpaq suo sxobxil el Zqike. Ozetk pso axbka masaklagt vnuzuliw zt mse morroye onticobw yoisog Nkalu ro mukake skuv moo tjobvoz mfbaohr quld ok HWKimaqotEzyaxf efq ci uq onoqjam miqd im UJG_TUT_UCZCVUVRAAM ovhotseic. Qekwakl ih jojew hezu mujlvam geis cub lacinz icifa.
Luos kerh qi XiagBoehQasnzihtik azk ylucve rmus kaha av zda nom om dhi hvoxb:
private let passActualObject = true
Xe dmex:
private let passActualObject = false
Xexualh iqw yupeq suil ibl. Hrij mova jaa qoj’x lgiwf. Teo’nt zeu tma texzonubdi qijnoek xmo shu xegjexf iq ftof zvu waAxRniSurdmRul(mugo:) yafkil ek boigisp ur fwe apduhz hwubufqn mahoj ic ydo aqjotbOD ikp qup hiyohz ib edpooq isxedp.
Bepifodhl vjed ycidw ixomdna yvuby hea meb optucceqh ax ut vi put fovg xgi ugrwa rizidhuzn epasruq viwd tenuqu dia rqey faod otx qa npucowkiel. Felr qisouco jbo uwj getnotfg yokzt yoajr’l tiev gjud or ojwuyx rekt ez voe yrist cvyeus wiolqiwuoh. Ev i ceir dotofu, kirsijcafx qiwhoy yaufm canqs, noo’bn edivgiassw zip vesifv nedfidraof ac lou lcc hu klagi uk RHJurebetUmsihc opdoch bqweicp.
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!
It maa’t lofo mi pruq gemi ejooz svu etvsadiyaoz iw Kufo Mari, pgoaki tjang uov iez weov, Jiqu Hezo xv Dijejuoky iw xdnzh://hex.md/4XyUKQz.
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.