SwiftUI/CoreData: Simultaneous accesses to 0x7f92efc61cb8, but modification requires exclusive access

I'm working on an iOS app using SwiftUI and CoreData and am running into a problem that I cannot seem to figure out.

To provide a little bit of information of what I am trying to do:

I have two CoreData entities that have a One-To-Many relationship:

Tarantula - A tarantula may molt many times (To Many) and when deleted, all of the molts shold also be removed (Cascade)

Molt - A molt belongs to a single Tarantula (To One) and when
deleted, should have the reference removed from the Tarantula (Nullify)

I then have a view that lists all of the molts for a given Tarantula that allows adding and deleting molts. It looks like this:


Code Block swift
struct MoltListView: View {
private static let DATE_FORMATTER: DateFormatter = {
let d = DateFormatter()
d.dateFormat = "MMM d, y"
return d
}()
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject private var tarantula: Tarantula
@FetchRequest private var molts: FetchedResults<Molt>
@State private var userMessage: String = ""
@State private var displayMessage: Bool = false
init(tarantula: Tarantula) {
self.tarantula = tarantula
self._molts = FetchRequest(entity: Molt.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Molt.date, ascending: false)],
predicate: NSPredicate(format: "tarantula = %@", tarantula))
}
var body: some View {
List {
Section(header: Text("Summary")) {
Text("\(molts.count) Molt\(molts.count == 1 ? "" : "s")")
}
Section(header: Text("Molts")) {
NavigationLink(destination: MoltView(tarantula: tarantula, molt: Molt.newModel())) {
Text("Add Molt").foregroundColor(.blue)
}
ForEach(molts, id: \.self) { molt in
NavigationLink(destination: MoltView(tarantula: tarantula, molt: molt)) {
Text(MoltListView.DATE_FORMATTER.string(from: molt.modelDate))
}
}
.onDelete(perform: deleteItems)
}
}.alert(isPresented: $displayMessage) {
Alert(title: Text("Save Failure"), message: Text(userMessage), dismissButton: .default(Text("Ok")))
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { molts[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
viewContext.rollback()
userMessage = "\(error): \(error.localizedDescription)"
displayMessage.toggle()
}
}
}
}


The error I am experiencing comes from whenever I try to delete a molt from the list view. The app instantly crashes and the error is:

Code Block swift
Simultaneous accesses to 0x7f92efc61cb8, but modification requires exclusive access

Find the complete error here:


I have tried removing the animation block and have played around with removing different UI components/restructuring.

The only way I have been able to prevent this error is to remove the delete rule on the Molt->Tarantula relationship from Nullify to No Action. However, this seems more like a hack to me instead of a fix.

Was hoping for some help on this issue.


Accepted Reply

Hi TylerKenneth43,

The behavior you've reported is the result of a system bug, and should be fixed in a future release. As a workaround, you can prevent the race condition by wrapping your deletion logic in NSManagedObjectContext.perform:

Code Block swift
private func deleteItems(offsets: IndexSet) {
    withAnimation {
        viewContext.perform {
            offsets.map { molts[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                viewContext.rollback()
                userMessage = "\(error): \(error.localizedDescription)"
                displayMessage.toggle()
            }
        }
    }
}


Please let me know if the information above does not resolve your issue(s).

Cheers,
Paris

Replies

Hi TylerKenneth43,

Please submit a focused sample project via Feedback Assistant along with your description above and the complete error output. Once submitted, please reply here with your Feedback ID so we can take a closer look into the issue and provide some insight into the underlying cause.
Hello ppinkney,

Thank you for the reply.

I have submitted the feedback in Feedback Assistant. The ID is FB8924572.
Hi TylerKenneth43,

The behavior you've reported is the result of a system bug, and should be fixed in a future release. As a workaround, you can prevent the race condition by wrapping your deletion logic in NSManagedObjectContext.perform:

Code Block swift
private func deleteItems(offsets: IndexSet) {
    withAnimation {
        viewContext.perform {
            offsets.map { molts[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                viewContext.rollback()
                userMessage = "\(error): \(error.localizedDescription)"
                displayMessage.toggle()
            }
        }
    }
}


Please let me know if the information above does not resolve your issue(s).

Cheers,
Paris
Hello Paris,

After making the changes you've proposed, the app is no longer crashing when attempting to perform the delete.

Thank you for the quick turnaround time on this problem.

Another onDelete crash with ColumnNavigationViewStyle.

withAnimation/withAnimation(.easeInOut(duration: 1)) is not working but DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(400)) works.

Xcode 13.3, iOS 15.4

Simultaneous accesses to 0x13ee25640, but modification requires exclusive access.
Previous access (a modification) started at SwiftUI`GraphHost.invalidate() + 124 (0x112d9adcc).
Current access (a read) started at:
0  libswiftCore.dylib         0x000000010b1c40d0 swift::runtime::AccessSet::insert(swift::runtime::Access*, void*, void*, swift::ExclusivityFlags) + 428
1  libswiftCore.dylib         0x000000010b1c42f0 swift_beginAccess + 72
2  SwiftUI              0x0000000112d9b6f8 GraphHost.isUpdating.getter + 24
3  SwiftUI              0x0000000112dbab28 StoredLocation.isValid.getter + 36
4  SwiftUI              0x0000000112db9848 StoredLocationBase.set(_:transaction:) + 52
5  SwiftUI              0x0000000112ec55a4 UITableViewListCoordinator.tableView(_:didEndEditingRowAt:) + 96
6  SwiftUI              0x0000000112ec5610 @objc UITableViewListCoordinator.tableView(_:didEndEditingRowAt:) + 160
7  UIKitCore             0x000000011d2e4d74 -[UITableView _sendDidEndEditingForIndexPath:] + 120
8  UIKitCore             0x000000011d3138e4 -[UITableView _endSwipeToDeleteRowDidDelete:] + 208
9  UIKitCore             0x000000011d2fd744 -[UITableView _setEditing:animated:forced:] + 1084
10  UIKitCore             0x000000011d2fd680 -[UITableView willMoveToSuperview:] + 132
11  UIKitCore             0x000000011d621360 __UIViewWillBeRemovedFromSuperview + 528
12  UIKitCore             0x000000011d62119c -[UIView(Hierarchy) removeFromSuperview] + 92
13  UIKitCore             0x000000011d5aaa6c -[UIScrollView removeFromSuperview] + 68
14  UIKitCore             0x000000011d609908 -[UIView dealloc] + 432
15  SwiftUI              0x0000000112b9f6d4 LayoutComputer.EngineDelegate.deinit + 36
16  SwiftUI              0x0000000112b9f704 LayoutComputer.EngineDelegate.__deallocating_deinit + 12
17  libswiftCore.dylib         0x000000010b1c6538 _swift_release_dealloc + 28
18  SwiftUI              0x0000000112b9f6d4 LayoutComputer.EngineDelegate.deinit + 36
19  SwiftUI              0x0000000112b9f704 LayoutComputer.EngineDelegate.__deallocating_deinit + 12
20  libswiftCore.dylib         0x000000010b1c6538 _swift_release_dealloc + 28
21  SwiftUI              0x0000000112b9f6d4 LayoutComputer.EngineDelegate.deinit + 36
22  SwiftUI              0x0000000112b9f704 LayoutComputer.EngineDelegate.__deallocating_deinit + 12
23  libswiftCore.dylib         0x000000010b1c6538 _swift_release_dealloc + 28
24  AttributeGraph           0x0000000137808b40 AG::Node::destroy(AG::Graph&) + 92
25  AttributeGraph           0x00000001377ff1cc AG::Subgraph::invalidate_now(AG::Graph&, unsigned long) + 1256
26  AttributeGraph           0x000000013780003c AG::Subgraph::invalidate_and_delete_(bool) + 260
27  SwiftUI              0x0000000112d9ad50 GraphHost.invalidate() + 156
28  SwiftUI              0x0000000112eeac20 closure #1 in _UIHostingView.deinit + 68
29  SwiftUI              0x0000000112eeab30 _UIHostingView.__deallocating_deinit + 180
30  SwiftUI              0x0000000112eeac74 @objc _UIHostingView.__deallocating_deinit + 24
31  SwiftUI              0x0000000112ef0f8c @objc UIHostingController.__ivar_destroyer + 32
32  libobjc.A.dylib          0x000000010296bd9c object_cxxDestructFromClass(objc_object*, objc_class*) + 112
33  libobjc.A.dylib          0x000000010298006c objc_destructInstance + 76
34  libobjc.A.dylib          0x0000000102986abc _objc_rootDealloc + 60
35  UIKitCore             0x000000011d0fea14 -[UIResponder dealloc] + 152
36  UIKitCore             0x000000011c976268 -[UIViewController dealloc] + 1192
37  libobjc.A.dylib          0x000000010296bd9c object_cxxDestructFromClass(objc_object*, objc_class*) + 112
38  libobjc.A.dylib          0x000000010298006c objc_destructInstance + 76
39  libobjc.A.dylib          0x0000000102986abc _objc_rootDealloc + 60
40  CoreFoundation           0x0000000103c840c4 cow_cleanup + 168
41  CoreFoundation           0x0000000103c83fe4 -[__NSDictionaryM dealloc] + 144
42  libobjc.A.dylib          0x000000010296bd9c object_cxxDestructFromClass(objc_object*, objc_class*) + 112
43  libobjc.A.dylib          0x000000010298006c objc_destructInstance + 76
44  libobjc.A.dylib          0x0000000102986abc _objc_rootDealloc + 60
45  libobjc.A.dylib          0x000000010296bd9c object_cxxDestructFromClass(objc_object*, objc_class*) + 112
46  libobjc.A.dylib          0x000000010298006c objc_destructInstance + 76
47  libobjc.A.dylib          0x0000000102986abc _objc_rootDealloc + 60
48  UIKitCore             0x000000011d0fea14 -[UIResponder dealloc] + 152
49  UIKitCore             0x000000011c976268 -[UIViewController dealloc] + 1192
50  libobjc.A.dylib          0x0000000102989628 AutoreleasePoolPage::releaseUntil(objc_object**) + 200
51  libobjc.A.dylib          0x00000001029894d4 objc_autoreleasePoolPop + 240
52  UIKitCore             0x000000011d5d5528 -[_UIAfterCACommitBlock run] + 84
53  UIKitCore             0x000000011d5d5980 -[_UIAfterCACommitQueue flush] + 188
54  UIKitCore             0x000000011d0d8a20 _runAfterCACommitDeferredBlocks + 640
55  UIKitCore             0x000000011d0c8644 _cleanUpAfterCAFlushAndRunDeferredBlocks + 128
56  UIKitCore             0x000000011d0fb44c _afterCACommitHandler + 76
57  CoreFoundation           0x0000000103b7c4dc __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
58  CoreFoundation           0x0000000103b76954 __CFRunLoopDoObservers + 572
59  CoreFoundation           0x0000000103b76d1c __CFRunLoopRun + 968
60  CoreFoundation           0x0000000103b765c8 CFRunLoopRunSpecific + 572
61  GraphicsServices          0x000000010d26356c GSEventRunModal + 160
62  UIKitCore             0x000000011d0c994c -[UIApplication _run] + 992
63  UIKitCore             0x000000011d0ce858 UIApplicationMain + 112
64  SwiftUI              0x0000000112d0b4c4 closure #1 in KitRendererCommon(_:) + 160
65  SwiftUI              0x0000000112d0b41c runApp<A>(_:) + 164
66  SwiftUI              0x000000011273ea8c static App.main() + 80
67  Star                0x00000001002ba6d0 static StarApp.$main() + 40
68  Star                0x00000001002badfc main + 12

Hi, any news on this ? I recently was running in exactly the same issue and wanted to know if problem solved ?

2024 here and the suggested solution doesn't work for me. I modified the Xcode template project for 'CoreData' such that the "item delete" occurs in a subview as a 'delete' button action. On delete I called one of:

extension NSManagedObjectContext {
    public func singleDelete (_ object: NSManagedObject) {
        self.delete(object)
        try! self.save()
    }

    public func atomicDelete (_ object: NSManagedObject) {
        self.perform {
            self.delete(object)
            try! self.save()
        }
    }
}

the atomicDelete helper extension being the suggested solution herein (less the do/catch logic). Both crashed in '#Preview' and on a simulator. Here is the subview:

extension Item {
    var label: String {
        "\(uuid!.uuidString.components(separatedBy: "-")[0]) (@ \(itemFormatter.string(from: timestamp!)))"
    }
}

struct ItemBriefView: View {   // Shown in navigationStack `List/ForEach`
    @Binding var item: Item
    var body: some View {
        Text(item.label)
    }
}

struct ItemView: View {
    @Environment(\.managedObjectContext) private var context
    @Environment(\.dismiss) var dismiss
    var item: Item

    var body: some View {
            Form {
                Text(item.label).padding(.bottom, 20)
                Button ("Delete") {
                    context.atomicDelete(item)
                    dismiss()
                }
            }
    }
}

The crashes are in the forced unwraps in \Item.lablel. (Telling me to use ?? is NOT a solution; I initialize every single managed object property and relationship).

Now, the solution/work-around I'm using is a wrapper view defined and used as such:

struct ItemView: View {
    @Environment(\.managedObjectContext) private var context
    @Environment(\.dismiss) var dismiss
    var item: Item

    var body: some View {
        ManagedObjectDeleteWrapper (object: item) { item in
            Form {
                Text(item.label).padding(.bottom, 20)
                Button ("Delete") {
                    context.singleDelete(item)
                    dismiss()
                }
            }
        }
    }
}

struct ManagedObjectDeleteWrapper<Object, Root> : View where Object : NSManagedObject, Root : View {
    var object: Object
    var root: (Object) -> Root

    var body: some View {
        Group {
            if object.isFault { EmptyView() }
            else { root (object) }
        }
    }
}

With this, one can detect a slight flash in the UI - which is better than a crash and the least of my SwiftUI 'oh, why is that happening' problems.

Cheers.