Core data crash with method "hasChanges" - ios

I have CoreDataStack
I've added debug options for CoreData debugging
"-com.apple.CoreData.ConcurrencyDebug 1"
class CoreDataStack {
public enum SaveStatus {
case saved, rolledBack, hasNoChanges, error
}
private var modelName: String
var viewContext: NSManagedObjectContext
var privateContext: NSManagedObjectContext
var persisterContainer: NSPersistentContainer
init(_ modelName: String) {
self.modelName = modelName
let container = NSPersistentContainer(name: modelName)
container.loadPersistentStores { persisterStoreDescription, error in
print("CoreData", "Initiated \(persisterStoreDescription)")
guard error == nil else {
print("CoreData", "Unresolved error \(error!)")
return
}
}
self.persisterContainer = container
self.viewContext = container.viewContext
self.viewContext.automaticallyMergesChangesFromParent = true
self.privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
self.privateContext.persistentStoreCoordinator = container.persistentStoreCoordinator
self.privateContext.automaticallyMergesChangesFromParent = true
}
func createTemporaryViewContext() -> NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.parent = self.privateContext
context.automaticallyMergesChangesFromParent = true
return context
}
func saveTempViewContext(tempContext context: NSManagedObjectContext, completion: ((CoreDataStack.SaveStatus) -> Void)? = nil) {
guard context.hasChanges || privateContext.hasChanges else {
completion?(.hasNoChanges)
return
}
context.performAndWait {
do {
try context.save()
}
catch {
completion?(.error)
return
}
}
privateContext.perform { [weak self] in
do {
try self?.privateContext.save()
completion?(.saved)
}
catch {
self?.privateContext.rollback()
completion?(.rolledBack)
}
}
}
class ViewController: UIViewController {
#objc
func save(_ sender: UIBarButtonItem) {
let coreDataStack = CoreDataStack()
let tempMainContext = coreDataStack.createTemporaryViewContext() //child main context from private context
var people = People(context: tempMainContext)
self.people.name = "John Doe"
self.people.age = 25
coreDataStack.saveTempViewContext(tempContext: tempMainContext) { status in
print(status)
}
}
}
I have "privateContext" attached to coordinator
I've created "tempMainContext" from private context
When I call "saveTempViewContext" I want to save tempMainContext which pushes changes to parent (privateContext) and this privateContext saves to persistent store
So the error occurred in line
privateContext.hasChanges
I know thats this line executes in main thread. And I need to call method "perform" or "performAndWait" to perform on the right queue.
like this
var contextHasChanges: Bool = false
var privateContextHasChanges: Bool = false
context.performAndWait {
contextHasChanges = context.hasChanges
}
privateContext.performAndWait {
privateContextHasChanges = privateContext.hasChanges
}
guard context.hasChanges || privateContext.hasChanges else {
completion?(.hasNoChanges)
return
}
But it so weird to call "performAndWait" just to check that context has changes. And when I call "performAndWait" it block current thread in my case it MainThread. And I don't want block the main thread even for short time.
How could we resolve this issue ?
UPD 1
attached debug stack
UPD 2
In my CoreDataStack init method in below I'v added code.
I just check if the private context has changes and It will crash
let privateContextHasChanges = privateContext.hasChanges
I think it's because at that line we are in MainThread and we touch private context which init with "privateQueueConcurrencyType" and I investigate that if I touch other property for example "privateContext.name" or "privateContext.parent" it works fine.
But if I touch property like this:
privateContext.hasChanges
privateContext.registeredObjects
privateContext.updatedObjects
maybe other
it will crash again
So I can make a conclusion that these properties are not thread safe.
Can anyone confirm that ?
UPD 3
After I'v read post Unexpected Core Data Multithreading Violation
I'v made conclusions:
If I'm on the main thread and my context's type is .main I do not need any changes and I safe
If I'm on some place and I don't know want kind of thread I'm on I always need to do "perform" or "performAndWait" to synchonize queue attached to context.
Almost always do "perform" and not "performAndWait" except you can't do without it.

context.hasChanges isn'n thread safe. It requires to be used in the context thread.
https://developer.apple.com/forums/thread/133378

Related

How to perform CoreData context.count from a background thread?

Currently, the following is my implementation regarding CoreData.
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "xxx")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
// persistent store.
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
private(set) lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return backgroundContext
}()
}
class NSAttachmentRepository {
static let INSTANCE = NSAttachmentRepository()
private init() {
}
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
let viewContext = coreDataStack.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSAttachment>(entityName: "NSAttachment")
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: "name == %#", name)
do {
let count = try viewContext.count(for: fetchRequest)
if count > 0 {
return true
}
} catch {
error_log(error)
}
return false
}
}
My strategy dealing with core data are
To perform non-blocking write call from main thread (UI thread), I will use CoreDataStack.INSTANCE.backgroundContext
To perform blocking read call from main thread (UI thread), I will use CoreDataStack.INSTANCE.persistentContainer.viewContext
This work fine all the time, until I need to perform the following operation
To perform blocking read call from background thread (non UI thread)
We need to run the code in PHPickerViewControllerDelegate's loadFileRepresentation callback. If we check using Thread.isMainThread (returns false) inside loadFileRepresentation callback, it is executed in a background thread.
When I perform call NSAttachmentRepository.INSTANCE.isExist(name) in function where Thread.isMainThread is false, I will get the following crash
CoreData`+[NSManagedObjectContext
Multithreading_Violation_AllThatIsLeftToUsIsHonor]:
I attempt to "fix" the problem by modifying the code from using coreDataStack.persistentContainer.viewContext to coreDataStack.backgroundContext
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
////let viewContext = coreDataStack.persistentContainer.viewContext
let backgroundContext = coreDataStack.backgroundContext
let fetchRequest = NSFetchRequest<NSAttachment>(entityName: "NSAttachment")
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: "name == %#", name)
do {
////let count = try viewContext.count(for: fetchRequest)
let count = try backgroundContext.count(for: fetchRequest)
if count > 0 {
return true
}
} catch {
error_log(error)
}
return false
}
However, I am still getting the same crash error.
Do you have any idea how I can perform CoreData context.count from a background thread?
It's not enough to just use a background context. You need to use that context on its own queue. You checked that you're not running on the main queue, but you could be on any queue, and the background context only works on one of them. The error message you see is what Core Data says when you're using it on the wrong queue.
Any time you use backgroundContext, you need to wrap the code in a call to perform or performAndWait, to ensure that your code runs on the background context's queue. Since your isExist function is synchronous, it needs to use performAndWait so that it can get a result before returning.
Thanks for pointer from #Tom Harrington
Here's the code snippet on how to tackle such threading issue.
func isExist(_ name: String) -> Bool {
let coreDataStack = CoreDataStack.INSTANCE
let viewContext = coreDataStack.persistentContainer.viewContext
var result = false
viewContext.performAndWait {
let fetchRequest = NSFetchRequest<NSAttachment>(entityName: "NSAttachment")
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: "name == %#", name)
do {
let count = try viewContext.count(for: fetchRequest)
if count > 0 {
result = true
} else {
result = false
}
} catch {
error_log(error)
}
}
return result
}

CoreData: This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation

I am facing this issue once or twice a day for the last week when I open my app & the app tries any save operation on the context, I still can't find a way to reproduce it.
I have searched many question on SO for fix, but most of them point 2 issues
Core Data Migration issue(which I don't have as I am on the same Model version no.)
failure of loading the persistent store (which is also doesn't happen in my case as my Core Data Stack doesn't initialise the main UI if the loadPersistentStores method on the persistentContainer fails)
I am using the Core Data stack setup mentioned in the below link:
https://williamboles.me/progressive-core-data-migration/
Here is my CoreData Setup class:
lazy var persistentContainer: NSPersistentContainer = {
let persistentContainer = NSPersistentContainer(name: "ABC")
let description = persistentContainer.persistentStoreDescriptions.first
description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where
description?.shouldMigrateStoreAutomatically = false
description?.type = storeType
return persistentContainer
}()
lazy var managedObjectContext: NSManagedObjectContext = {
let context = self.persistentContainer.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
context.automaticallyMergesChangesFromParent = true
return context
}()
lazy var _managedObjectContext: NSManagedObjectContext = {
let context = self.persistentContainer.viewContext
context.automaticallyMergesChangesFromParent = true
return context
}()
// MARK: - Singleton
private static var privateShared : CoreDataManager?
class func shared() -> CoreDataManager { // change class to final to prevent override
guard let uwShared = privateShared else {
privateShared = CoreDataManager()
return privateShared!
}
return uwShared
}
class func destroy() {
privateShared = nil
}
// MARK: - Init
init(storeType: String = NSSQLiteStoreType, migrator: CoreDataMigratorProtocol = CoreDataMigrator()) {
self.storeType = storeType
self.migrator = migrator
}
// MARK: - SetUp
func setup(completion: #escaping () -> Void) {
loadPersistentStore {
completion()
}
}
// MARK: - Loading
private func loadPersistentStore(completion: #escaping () -> Void) {
migrateStoreIfNeeded {
self.persistentContainer.loadPersistentStores { description, error in
guard error == nil else {
fatalError("was unable to load store \(error!)")
}
completion()
}
}
}
private func migrateStoreIfNeeded(completion: #escaping () -> Void) {
guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
fatalError("persistentContainer was not set up properly")
}
if migrator.requiresMigration(at: storeURL, toVersion: CoreDataMigrationVersion.current) {
DispatchQueue.global(qos: .userInitiated).async {
self.migrator.migrateStore(at: storeURL, toVersion: CoreDataMigrationVersion.current)
DispatchQueue.main.async {
completion()
}
}
} else {
completion()
}
}
And I initialise the Core Data stack using the following code in the App Delegate:
CoreDataManager.shared().setup {[unowned self] in
self.showMainUI()
}
My App crashes after the Home Controller is loaded & some part of my code does a save operation on certain NSManagedObject Model
This how I save to Context:
let context = CoreDataManager.shared().managedObjectContext // background context
context.performAndWait {
if let entityDescription = NSEntityDescription.entity(forEntityName: Entity_Name, in: context) {
if let runEntityObject = NSManagedObject(entity: entityDescription, insertInto: context) as? MY_Model {
// Create the Object
guard context.hasChanges else { return }
do {
try context.save() // Crashes here once or twice a day :(
}
catch {
print(error.localizedDescription)
}
}
}
}
Some SO answers also mention of threading issues but I am using the performAndWait Block so the save happen on the same queue
Would be really helpful If someone pointed me in the right direction regarding this issue
After going through my AppDelegate file many times, I found that I was doing a Core Data save operation in the applicationDidBecomeActive method which is also called when the app starts from a suspended state.
So if my Core Data stack setup closure didn't finish before the applicationDidBecomeActive is called the app would crash.
After removing it, the app was working fine without any crashes

Core data Crash: EXC_BAD_ACCESS while trying access data got using predicate

I have save values to entity method which save new data and updates existing data.
func saveSteps(_ serverJson: [[String: Any]]){
let stepService = StepService(context: context);
if(serverJson.count > 0){
for step in serverJson {
let stepTitle = step["stepTitle"] as? String ?? ""
let stepDescription = step["stepDescription"] as? String ?? ""
let stepId = step["_id"] as? String ?? ""
let predicate: NSPredicate = NSPredicate(format: "stepId=%#", stepId)
let stepList = stepService.get(withPredicate: predicate);
if(stepList.count == 0){
stepService.create(stepId: stepId, stepTitle: stepTitle, stepDescription: stepDescription);
}else{
if let updatableStep = stepList.first{
updatableStep.stepDescription = stepDescription //EXC_BAD_ACCESS Error Here
updatableStep.stepName = stepName
updatableStep.stepTitle = stepTitle
stepService.update(updatedStep: updatableStep)
}else{
stepService.create(stepId: stepId, stepTitle: stepTitle, stepDescription: stepDescription);
}
}
saveContext()
}
My Create update and get methods are in stepService
func create(stepId:String, stepDescription: String, stepTitle:String){
let newItem = NSEntityDescription.insertNewObject(forEntityName: "Steps", into: context) as! Steps //EXC_BAD_ACCESS Error Here
newItem.stepId = stepId
newItem.stepTitle = stepTitle
newItem.stepDescription = stepDescription
}
func update(updatedStep: Steps){
if let step = getById(id: updatedStep.objectID){
step.stepId = updatedStep.stepId
step.stepTitle = updatedStep.stepTitle
step.stepDescription = updatedStep.stepDescription
}
func get(withPredicate queryPredicate: NSPredicate) -> [Steps]{
let fetchRequest: NSFetchRequest<Steps> = Steps.fetchRequest()
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = queryPredicate
do {
let response = try context.fetch(fetchRequest)
return response
} catch let error as NSError {
// failure
print(error)
return [Steps]()
}
}
}
Mysave context method is
// Creating private queue to save the data to disk
lazy var savingModelcontext:NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.coordinator
return managedObjectContext
}()
// Creating Context to save in block main queue this will be temporary save
lazy var context:NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.parent = self.savingModelcontext
return managedObjectContext
}()
func saveContext () {
guard savingModelcontext.hasChanges || context.hasChanges else {
return
}
context.performAndWait {
do {
try self.context.save()
} catch let error as NSError {
print("Could not save in Context: \(error.localizedDescription)")
}
}
savingModelcontext.perform {
do {
try self.savingModelcontext.save()
} catch let error as NSError {
print("Could not save savingModelContext: \(error.localizedDescription)")
}
}
}
There are two places that core data crashes with same error message one is when i access the data to update the method and other is when i am trying to create a new item using NSEntityDescription.insertNewObject with entity name.
while saing i have tried Dispach queue with qos of userInitiated and default. I didn't use background as user might open some thing that might use this.
The problem is the crash is not consistanct and has never crashed when doing a debug which leads me to belive it is concurrency issue but the data is never deleted or read when being updated.
PS: I have read the questions with same and similar issues but i could not get a working answer here the question.
Kidly point out if i have made any mistakes
Thanks any help is appreciated
The Reason fo the crash was i was trying to change was that i was using and using private queue context in main thread and vise versa. to fix this using Group Dispach queue with desired QOS for private queue context and main thread for using main queue objects.

iOS Core Data - How to avoid crash when many context save at same time?

I try to avoid crash when many contexts save at same time.
The following class has one operation queue that operate only one work at same time. It has three context. First, defaultContext is main queue type, this is not directly updated and is only visible to the user. Other two contexts is localContext and externalContext.
LocalContext is for user's schedule addition and external Context is for external schedule update like cloud sync. Local context and external context is child of defaultContext and set it's automaticallyMergesChangesFromParent property to true. Even if user update and external update are implemented at same time. Since they run sequentially in the same queue, there is no data loss.
It works great when data input is small. But app gets slower when too much data is coming in. Is there any better way?
Here's my code.
class DataController {
static let shared = DataController()
var schedules: [Schedule] = []
var persistentContainer: NSPersistentContainer
let persistentContainerQueue = OperationQueue()
private init() {
persistentContainerQueue.maxConcurrentOperationCount = 1
persistentContainer = NSPersistentContainer(name: "CoreDataConcurrency")
persistentContainer.loadPersistentStores { (description, error) in
if let error = error {
fatalError("Failed to load Core Data stack: \(error)")
}
}
}
lazy var defaultContext: NSManagedObjectContext = {
[unowned self] in
self.persistentContainer.viewContext
}()
lazy var localContext: NSManagedObjectContext = {
[unowned self] in
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = self.defaultContext
context.automaticallyMergesChangesFromParent = true
return context
}()
lazy var externalContext: NSManagedObjectContext = {
[unowned self] in
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = self.defaultContext
context.automaticallyMergesChangesFromParent = true
return context
}()
func enqueueCoreDataOperation(context: NSManagedObjectContext, changeBlock: #escaping () -> (NSManagedObjectContext)) {
persistentContainerQueue.addOperation {
let changedContext = changeBlock()
guard changedContext.hasChanges else {
return
}
changedContext.performAndWait({
do {
try changedContext.save()
if let parentContext = changedContext.parent {
do {
try parentContext.save()
} catch {
fatalError()
}
}
} catch {
fatalError()
}
})
}
}
func addSchedule(title: String, date: Date, context: NSManagedObjectContext) {
let changeBlock: () -> (NSManagedObjectContext) = {
let schedule = NSEntityDescription.insertNewObject(forEntityName: "Schedule", into: context) as! Schedule
schedule.title = title
schedule.date = date
return context
}
enqueueCoreDataOperation(context: context, changeBlock: changeBlock)
}
func updateSchedule(schedule: Schedule, modifiedTitle: String, context: NSManagedObjectContext) {
let scheduleInContext = context.object(with: schedule.objectID) as! Schedule
let changeBlock: () -> (NSManagedObjectContext) = {
scheduleInContext.title = modifiedTitle
return context
}
enqueueCoreDataOperation(context: context, changeBlock: changeBlock)
}
}
You could batch up the incoming data into smaller batches so each operation takes less time and add a priority to the operations so cloud based changes have a lower priority. Then they will no longer block the other changes. But I strongly suspect that you are doing something wrong in you import operation that is making take too long. Are you doing a fetch for each imported entity? Please share that code.

EXC_BAD_ACCESS code=EXC_ARM_DA_ALIGN

I've tested my code with 3 x iPhone 5's, a 5s, 6, 6s, and 7.
I'm getting the above error on all of the iPhone 5 devices only. No idea what is going on here but perhaps the fact that the 5's are 32bit devices may be a clue?
I'm calling the following method from a viewcontroller class
func startRecording() {
disableControls()
CoreDataStack.shared.performForegroundTask { (context) in
let sessionInfo = SessionInfo(context: context)
sessionInfo.startTime = Date().timeIntervalSince1970
sessionInfo.userId = self.config.userId
sessionInfo.devicePosition = self.config.devicePosition.rawValue
sessionInfo.deviceType = self.config.deviceType.rawValue
sessionInfo.deviceNumber = self.config.deviceNumber
sessionInfo.deviceSide = self.config.deviceSide.rawValue
do {
try context.obtainPermanentIDs(for: [sessionInfo])
} catch {
print("Error obtaining permanent ID for session info record")
return
}
CoreDataStack.shared.saveViewContextAndWait()
DispatchQueue.main.async {
guard sessionInfo.objectID.isTemporaryID == false else {
print("ObjectID is temporary")
return
}
self.recording = true
self.statusLabel.text = "Recording..."
self.recordManager.start(sessionUID: sessionInfo.uid)
}
}
}
The config variable is a simple struct:
struct Configuration {
var userId: String = "Unknown"
var deviceType: DeviceType = .phone // enum: String
var deviceSide: DeviceSide = .notApplicable // enum: String
var deviceNumber: Int16 = 1
var devicePosition: DevicePosition = .waist // enum: String
}
The CoreDataStack is here:
final class CoreDataStack {
static let shared = CoreDataStack()
private init() {}
var errorHandler: (Error) -> Void = { error in
log.error("\(error), \(error._userInfo)")
}
private struct constants {
static let persistentStoreName = "Model"
}
private lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: constants.persistentStoreName)
container.loadPersistentStores(completionHandler: { [weak self] (storeDescription, error) in
if let error = error {
self?.errorHandler(error)
}
})
return container
}()
lazy var viewContext: NSManagedObjectContext = {
self.persistentContainer.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
try! self.persistentContainer.viewContext.setQueryGenerationFrom(.current)
return self.persistentContainer.viewContext
}()
private lazy var backgroundContext: NSManagedObjectContext = {
let context = self.persistentContainer.newBackgroundContext()
context.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
return context
}()
func performForegroundTask(_ block: #escaping (NSManagedObjectContext) -> Void) {
self.viewContext.performAndWait {
block(self.viewContext)
}
}
func performBackgroundTask(_ block: #escaping (NSManagedObjectContext) -> Void) {
backgroundContext.perform {
block(self.backgroundContext)
}
}
func saveBackgroundContext() {
viewContext.performAndWait {
do {
if self.viewContext.hasChanges {
try self.viewContext.save()
}
} catch {
self.errorHandler(error)
}
self.backgroundContext.perform {
do {
if self.backgroundContext.hasChanges {
try self.backgroundContext.save()
self.backgroundContext.refreshAllObjects()
}
} catch {
self.errorHandler(error)
}
}
}
}
func saveViewContext() {
viewContext.perform {
if self.viewContext.hasChanges {
do {
try self.viewContext.save()
} catch {
self.errorHandler(error)
}
}
}
}
func saveViewContextAndWait() {
viewContext.performAndWait {
if self.viewContext.hasChanges {
do {
try self.viewContext.save()
} catch {
self.errorHandler(error)
}
}
}
}
}
The code is bombing out on the following line in the startRecording method:
try context.obtainPermanentIDs(for: [sessionInfo])
Edit:
I've created a stripped down test application consisting of only the CoreDataStack and a model with one entity with one attribute of type string. I'm still getting the same error on 3x iPhone 5's only. 5s, 6, 6s, 7 all work fine.
It would imply the problem lies with the CoreDataStack perhaps?
Github repo here
A couple of people have asked if I solved this, so here is what I did. This is not really a solution but more of a workaround. It may not be appropriate for everyone but worked for me.
All I did was remove the line
try! self.persistentContainer.viewContext.setQueryGenerationFrom(.current)
from the CoreDataStack and the issue went away...
I'll take a couple of guesses based on a short look at your code.
The old-school developer in me is drawn to the Configuration member var deviceNumber: Int16 = 1. Incorrect alignment settings or old compilers might cause the next item to have the wrong alignment. You could try making it the last item in the struct.
Another item which stands out is the assignment sessionInfo.deviceNumber = self.config.deviceNumber. This looks to be assigning an Int16 to an NSNumber which may be a problem. (I am assuming that SessionInfo is an NSManagedObject based on the overall code and its initializer taking a context argument. That would mean all numeric members are NSNumbers.)
Try changing the line to
sessionInfo.deviceNumber = NSNumber(int:self.config.deviceNumber)
I'm not yet familiar with the iOS 10 additions to Core Data, but from what I'm reading the viewContext is read-only. But viewContext is being used to create a new object in this code. Is the Xcode console showing any more information when you get to this point in the debugger?
NSPersistentContainer is a setup for a core data stack with clear expectation on how to use it. You are misusing it. viewContext is readonly so remove performForegroundTask, saveViewContext and saveViewContextAndWait. Don't change the viewContext's mergePolicy. In performBackgroundTask just use NSPersistentContainer's performBackgroundTask which has the same method signature. Don't use newBackgroundContext. If you are using NSPersistentContainer your CoreDataStack should be doing next to nothing.
If you want a different custom stack unlike what NSPersistentContainer sets up then don't use NSPersistentContainer - just create your own stack. But the setup that you are trying to write has major problems. Writing from both background contexts and the viewContext has major problems when writes happen at the same time. mergePolicy can help that but you can end up missing information that you thought you saved. You are much better off learning to use the stack that NSPersistentContainer sets up.

Resources