As I struggled with creating my software I made a decision to study some fundamentals and created playground challenge utilizing Core Information in SwiftUI, however I encountered downside I am unable to resolve and even root trigger. Let me start with describing what I attempted to attain and what the issue is.
I created 3 fashions:
ListEntity
– have title and to-many relationship toListItemEntity
ListItemEntity
– has simply flag and a couple of to-one relationship toListEntity
andNameEntity
NameEntity
– has simply title and to-many relationship toListItemEntity
For these fashions I created 3 primary views to point out and add knowledge. Views for displaying and including ListEntity
(first for checklist of them and second with their particulars) works nice with none downside. The issue I’ve is with third view which consists of searchable checklist of NameEntity
(with add button, which simply add new NameEntity
of title Identify <counts of names>
). Every NameEntity
row in checklist is clickable and by clicking it you need to add (or delete) ListItemEntity
to presently edited ListEntity
with chosen NameEntity
. That works nice so long as you add just one merchandise. When I attempt to add one other I get error someplace in context save methodology. The issue is, my save methodology is inside do { } catch { }
block and it ought to print error to console, however as a substitute it crashes complete software.
Right here is error I am getting:
error: Critical software error. Exception was caught throughout Core Information change processing. That is often a bug inside an observer of NSManagedObjectContextObjectsDidChangeNotification. -[NameEntity compare:]: unrecognized selector despatched to occasion 0x600002118ff0 with userInfo (null)
With name stack:
*** First throw name stack:
(
0  CoreFoundation           0x0000000180491128 __exceptionPreprocess + 172
1  libobjc.A.dylib           0x000000018008412c objc_exception_throw + 56
2  CoreFoundation           0x00000001804a5f78 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3  CoreFoundation           0x0000000180495278 ___forwarding___ + 1280
4  CoreFoundation           0x000000018049759c _CF_forwarding_prep_0 + 92
5  Basis             0x0000000180dde258 _NSCompareObject + 60
6  CoreData              0x00000001863c8564 +[NSFetchedResultsController _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 220
7  CoreData              0x00000001863c82b4 -[NSFetchedResultsController _updateFetchedObjectsWithInsertChange:] + 832
8  CoreData              0x00000001863c9e00 __82-[NSFetchedResultsController(PrivateMethods) _core_managedObjectContextDidChange:]_block_invoke + 2364
9  CoreData              0x0000000186369e80 developerSubmittedBlockToNSManagedObjectContextPerform + 156
10 CoreData              0x0000000186369d5c -[NSManagedObjectContext performBlockAndWait:] + 212
11 CoreData              0x00000001863c94a8 -[NSFetchedResultsController _core_managedObjectContextDidChange:] + 96
12 CoreFoundation           0x00000001803c1878 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 140
13 CoreFoundation           0x00000001803c179c ___CFXRegistrationPost_block_invoke + 84
14 CoreFoundation           0x00000001803c0c8c _CFXRegistrationPost + 404
15 CoreFoundation           0x00000001803c0668 _CFXNotificationPost + 688
16 Basis             0x0000000180d84cb4 -[NSNotificationCenter postNotificationName:object:userInfo:] + 88
17 CoreData              0x000000018635ce54 -[NSManagedObjectContext _postObjectsDidChangeNotificationWithUserInfo:] + 320
18 CoreData              0x000000018636f640 -[NSManagedObjectContext _createAndPostChangeNotification:deletions:updates:refreshes:deferrals:wasMerge:] + 1244
19 CoreData              0x000000018635eb9c -[NSManagedObjectContext _processRecentChanges:] + 2884
20 CoreData              0x0000000186360cc0 -[NSManagedObjectContext save:] + 340
21 Playground             0x00000001024b8da8 $sSo22NSManagedObjectContextC10PlaygroundE12saveIfNeededyyKF + 108
22 Playground             0x00000001024b8eac $sSo22NSManagedObjectContextC10PlaygroundE9forceSaveyyF + 60
23 Playground             0x00000001024c3d30 $s10Playground8NameTileV4bodyQrvgyycfU_ + 1220
24Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c5908800 OUTLINED_FUNCTION_11 + 620
25Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c58365c0 OUTLINED_FUNCTION_31 + 1824
26Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51f742c OUTLINED_FUNCTION_21 + 32
27Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c4f04840 OUTLINED_FUNCTION_2 + 6392
28Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c4f0c7c4 OUTLINED_FUNCTION_2 + 39036
29Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51f742c OUTLINED_FUNCTION_21 + 32
30Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51f7448 OUTLINED_FUNCTION_21 + 60
31Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51f742c OUTLINED_FUNCTION_21 + 32
32Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c58f6554 OUTLINED_FUNCTION_17 + 2340
33Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c58f6b18 OUTLINED_FUNCTION_17 + 3816
34Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51e3f04 OUTLINED_FUNCTION_7 + 9760
35Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51e9298 OUTLINED_FUNCTION_7 + 31156
36 UIKitCore              0x0000000184a3583c -[UICollectionView _selectItemAtIndexPath:animated:scrollPosition:notifyDelegate:deselectPrevious:performCustomSelectionAction:] + 1176
37 UIKitCore              0x0000000184a650b4 -[UICollectionView touchesEnded:withEvent:] + 452
38 UIKitCore              0x000000018531a718 forwardTouchMethod + 264
39 UIKitCore              0x000000018531a718 forwardTouchMethod + 264
40 UIKitCore              0x000000018531a718 forwardTouchMethod + 264
41 UIKitCore              0x0000000184e30458 _UIGestureEnvironmentUpdate + 5912
42 UIKitCore              0x0000000184e2ea60 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 288
43 UIKitCore              0x0000000184e2e7d0 -[UIGestureEnvironment _updateForEvent:window:] + 156
44 UIKitCore              0x0000000185329f00 -[UIWindow sendEvent:] + 3088
45 UIKitCore              0x000000018530998c -[UIApplication sendEvent:] + 576
46 UIKitCore              0x000000018538a5c0 __dispatchPreprocessedEventFromEventQueue + 1708
47 UIKitCore              0x000000018538d474 __processEventQueue + 5524
48 UIKitCore              0x0000000185385e38 __eventFetcherSourceCallback + 156
49 CoreFoundation           0x00000001803f1f18 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
50 CoreFoundation           0x00000001803f1e60 __CFRunLoopDoSource0 + 172
51 CoreFoundation           0x00000001803f15d0 __CFRunLoopDoSources0 + 232
52 CoreFoundation           0x00000001803ebcb8 __CFRunLoopRun + 768
53 CoreFoundation           0x00000001803eb5a4 CFRunLoopRunSpecific + 572
54 GraphicsServices          0x000000018e9fbae4 GSEventRunModal + 160
55 UIKitCore              0x00000001852f02e4 -[UIApplication _run] + 868
56 UIKitCore              0x00000001852f3f5c UIApplicationMain + 124
57Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51fc1b0 OUTLINED_FUNCTION_70 + 500
58Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c51fc050 OUTLINED_FUNCTION_70 + 148
59Â SwiftUI Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001c4f02fa4 OUTLINED_FUNCTION_2 + 92
60 Playground             0x00000001024c9598 $s10Playground0A3AppV5$mainyyFZ + 40
61 Playground             0x00000001024c9648 major + 12
62 dyld                0x0000000102655544 start_sim + 20
63Â ??? Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0x00000001028660e0 0x0 + 4337328352
64Â ??? Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â 0xc719000000000000 0x0 + 14346498087965425664
)
Now right here is my code, beginning with knowledge definition first:
<?xml model="1.0" encoding="UTF-8" standalone="sure"?>
<mannequin kind="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22522" systemVersion="23C71" minimumToolsVersion="Computerized" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
<entity title="ListEntity" representedClassName="ListEntity" syncable="YES">
<attribute title="title" elective="YES" attributeType="String"/>
<relationship title="gadgets" elective="YES" toMany="YES" deletionRule="Cascade" destinationEntity="ListItemEntity" inverseName="checklist" inverseEntity="ListItemEntity"/>
</entity>
<entity title="ListItemEntity" representedClassName="ListItemEntity" syncable="YES">
<attribute title="flag" elective="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship title="checklist" elective="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ListEntity" inverseName="gadgets" inverseEntity="ListEntity"/>
<relationship title="title" elective="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NameEntity" inverseName="gadgets" inverseEntity="NameEntity"/>
</entity>
<entity title="NameEntity" representedClassName="NameEntity" syncable="YES">
<attribute title="title" elective="YES" attributeType="String"/>
<relationship title="gadgets" elective="YES" toMany="YES" deletionRule="Nullify" destinationEntity="ListItemEntity" inverseName="title" inverseEntity="ListItemEntity"/>
</entity>
</mannequin>
class DataProvider {
static let shared = DataProvider()
non-public let container: NSPersistentContainer
var viewContext: NSManagedObjectContext {
container.viewContext
}
non-public init() {
container = NSPersistentContainer(title: "Information")
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
container.loadPersistentStores { description, error in
print(description.url?.path(percentEncoded: false) ?? "No URL")
if let error {
fatalError("Couldn't load persistent shops: (error.localizedDescription)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
}
}
extension NSManagedObjectContext {
func saveIfNeeded() throws {
guard hasChanges else { return }
attempt save()
}
func forceSave() {
do {
attempt saveIfNeeded()
} catch {
print(error.localizedDescription)
}
}
}
@objc(ListEntity)
public class ListEntity: NSManagedObject {}
public extension ListEntity {
@nonobjc class func fetchRequest() -> NSFetchRequest<ListEntity> {
NSFetchRequest<ListEntity>(entityName: "ListEntity")
}
@NSManaged var title: String?
@NSManaged var gadgets: NSSet?
var wrappedName: String {
title ?? "No title"
}
var wrappedItems: [ListItemEntity] {
if let gadgets = gadgets as? Set<ListItemEntity> {
return gadgets.sorted(utilizing: KeyPathComparator(.wrappedName))
}
return []
}
}
// MARK: Generated accessors for gadgets
public extension ListEntity {
@objc(addItemsObject:)
@NSManaged func addToItems(_ worth: ListItemEntity)
@objc(removeItemsObject:)
@NSManaged func removeFromItems(_ worth: ListItemEntity)
@objc(addItems:)
@NSManaged func addToItems(_ values: NSSet)
@objc(removeItems:)
@NSManaged func removeFromItems(_ values: NSSet)
}
extension ListEntity: Identifiable {}
@objc(ListItemEntity)
public class ListItemEntity: NSManagedObject {}
public extension ListItemEntity {
@nonobjc class func fetchRequest() -> NSFetchRequest<ListItemEntity> {
NSFetchRequest<ListItemEntity>(entityName: "ListItemEntity")
}
@nonobjc class func fetchRequestFor(_ checklist: ListEntity, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchRequest<ListItemEntity> {
let fetchRequest = Self.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "checklist == %@", checklist)
fetchRequest.sortDescriptors = sortDescriptors
return fetchRequest
}
@nonobjc class func fetchRequest(with title: NameEntity, on checklist: ListEntity, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchRequest<ListItemEntity> {
let fetchRequest = Self.fetchRequest()
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "list == %@", list),
NSPredicate(format: "name == %@", name),
])
fetchRequest.sortDescriptors = sortDescriptors
return fetchRequest
}
@NSManaged var flag: Bool
@NSManaged var checklist: ListEntity?
@NSManaged var title: NameEntity?
var wrappedName: String {
title?.wrappedName ?? "No title"
}
}
extension ListItemEntity: Identifiable {}
@objc(NameEntity)
public class NameEntity: NSManagedObject {}
public extension NameEntity {
@nonobjc class func fetchRequest() -> NSFetchRequest<NameEntity> {
NSFetchRequest<NameEntity>(entityName: "NameEntity")
}
@nonobjc class func fetchRequest(containing title: String, fetchLimit: Int? = nil, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchRequest<NameEntity> {
let fetchRequest = Self.fetchRequest()
if let fetchLimit {
fetchRequest.fetchLimit = fetchLimit
}
let trimmedName = title.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmedName.isEmpty {
fetchRequest.predicate = NSPredicate(format: "title CONTAINS[cd] %@", trimmedName)
}
fetchRequest.sortDescriptors = sortDescriptors
return fetchRequest
}
@NSManaged var title: String?
@NSManaged var gadgets: NSSet?
var wrappedName: String {
title ?? "No title"
}
var wrappedItems: [ListItemEntity] {
if let gadgets = gadgets as? Set<ListItemEntity> {
return gadgets.sorted(utilizing: KeyPathComparator(.wrappedName))
}
return []
}
}
// MARK: Generated accessors for gadgets
public extension NameEntity {
@objc(addItemsObject:)
@NSManaged func addToItems(_ worth: ListItemEntity)
@objc(removeItemsObject:)
@NSManaged func removeFromItems(_ worth: ListItemEntity)
@objc(addItems:)
@NSManaged func addToItems(_ values: NSSet)
@objc(removeItems:)
@NSManaged func removeFromItems(_ values: NSSet)
}
extension NameEntity: Identifiable {}
Lastly my Views:
struct ListEntitiesView: View {
@Setting(.managedObjectContext)
non-public var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: ListEntity.name, ascending: true)],
animation: .default
)
non-public var lists: FetchedResults<ListEntity>
var physique: some View {
NavigationStack {
Checklist {
ForEach(lists) { checklist in
NavigationLink(worth: checklist) {
ListEntityTile(checklist)
}
}
}
.navigationTitle("ListEntities")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
Button {
let checklist = ListEntity(context: viewContext)
checklist.title = "Checklist (lists.depend + 1)"
viewContext.forceSave()
} label: {
Label("Add Checklist", systemImage: "plus")
}
}
.navigationDestination(for: ListEntity.self) { checklist in
ListEntityView(checklist)
}
}
}
}
struct ListEntityTile: View {
@ObservedObject
non-public var checklist: ListEntity
@FetchRequest
non-public var gadgets: FetchedResults<ListItemEntity>
non-public var totalItems: Int {
gadgets.depend
}
non-public var flaggedItems: Int {
gadgets.filter(.flag).depend
}
init(_ checklist: ListEntity) {
_list = ObservedObject(wrappedValue: checklist)
_items = FetchRequest(fetchRequest: ListItemEntity.fetchRequestFor(checklist))
}
var physique: some View {
HStack {
Textual content(checklist.wrappedName)
Spacer()
Textual content("(flaggedItems) / (totalItems)")
}
}
}
struct ListEntityView: View {
@Setting(.managedObjectContext)
non-public var viewContext
@ObservedObject
non-public var checklist: ListEntity
@FetchRequest
non-public var gadgets: FetchedResults<ListItemEntity>
init(_ checklist: ListEntity) {
_list = ObservedObject(wrappedValue: checklist)
_items = FetchRequest(fetchRequest: ListItemEntity.fetchRequestFor(checklist, sortDescriptors: [NSSortDescriptor(keyPath: ListItemEntity.name, ascending: true)]))
}
var physique: some View {
Checklist {
ForEach(gadgets) { merchandise in
ListItemTile(merchandise)
}
}
.navigationTitle(checklist.wrappedName)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
NavigationLink {
AddListItemEntity(checklist)
} label: {
Label("Add Merchandise", systemImage: "plus")
}
}
}
}
struct ListItemTile: View {
@Setting(.managedObjectContext)
non-public var viewContext
@ObservedObject
non-public var merchandise: ListItemEntity
init(_ merchandise: ListItemEntity) {
_item = ObservedObject(wrappedValue: merchandise)
}
var physique: some View {
Button {
merchandise.flag.toggle()
viewContext.forceSave()
} label: {
Label(merchandise.wrappedName, systemImage: merchandise.flag ? "checkmark.circle" : "circle")
}
}
}
struct AddListItemEntity: View {
@Setting(.managedObjectContext)
non-public var viewContext
@ObservedObject
non-public var checklist: ListEntity
@State
non-public var searchName = ""
@FetchRequest(sortDescriptors: [])
non-public var names: FetchedResults<NameEntity>
init(_ checklist: ListEntity) {
_list = ObservedObject(wrappedValue: checklist)
}
var physique: some View {
Checklist {
TextField("Search title", textual content: $searchName.animation())
NamesList(for: checklist, containing: searchName)
}
.toolbar {
Button {
let title = NameEntity(context: viewContext)
title.title = "Identify (names.depend + 1)"
viewContext.forceSave()
} label: {
Label("Add Identify", systemImage: "plus")
}
}
}
}
struct NamesList: View {
@ObservedObject
non-public var checklist: ListEntity
@FetchRequest
non-public var names: FetchedResults<NameEntity>
init(for checklist: ListEntity, containing title: String) {
_list = ObservedObject(wrappedValue: checklist)
_names = FetchRequest(
fetchRequest: NameEntity.fetchRequest(containing: title, sortDescriptors: [NSSortDescriptor(keyPath: NameEntity.name, ascending: true)]),
animation: .default
)
}
var physique: some View {
ForEach(names) { title in
NameTile(of: title, for: checklist)
}
}
}
struct NameTile: View {
@Setting(.managedObjectContext)
non-public var viewContext
@FetchRequest
non-public var gadgets: FetchedResults<ListItemEntity>
@ObservedObject
non-public var checklist: ListEntity
@ObservedObject
non-public var title: NameEntity
non-public var merchandise: ListItemEntity? {
gadgets.first { $0.title == title }
}
init(of title: NameEntity, for checklist: ListEntity) {
_list = ObservedObject(wrappedValue: checklist)
_name = ObservedObject(wrappedValue: title)
_items = FetchRequest(
fetchRequest: ListItemEntity.fetchRequestFor(checklist),
animation: .default
)
}
var physique: some View {
Button {
if let merchandise {
viewContext.delete(merchandise)
} else {
let newItem = ListItemEntity(context: viewContext)
newItem.flag = false
newItem.title = title
newItem.checklist = checklist
}
viewContext.forceSave()
} label: {
Label(title.wrappedName, systemImage: merchandise != nil ? "checkmark.circle" : "circle")
}
}
}
I believed that I should have in some way misconfigured ListItemEntity
to ListEntity
relationship, however I managed to run this code inside my DataProvider
and it labored with none downside:
let checklist = ListEntity(context: viewContext)
checklist.title = "Checklist 1"
viewContext.forceSave()
var names = [NameEntity(context: viewContext), NameEntity(context: viewContext)]
for title in names {
title.title = "Identify (names.firstIndex(of: title) ?? 0)"
}
viewContext.forceSave()
let gadgets = [ListItemEntity(context: viewContext), ListItemEntity(context: viewContext)]
for merchandise in gadgets {
merchandise.flag = false
merchandise.checklist = checklist
merchandise.title = names.popLast()
}
viewContext.forceSave()
I imagine it have to be one thing easy and apparent as that is quite simple use case I believe, however my lack of expertise get my to level I am unable to do something greater than ask on your assist.