First of all, I'm building my application with Xcode 12.4 (12D4e) and running it on macOS 11.2.3 (20D91).
Most of this background shouldn't be relevant to my issue, but it should be working, so clearly I'm missing something. My application downloads a set of objects from a server (think the users, computers, and groups in an Active Directory database) and displays them in a view-based NSOutlineView in the sidebar. I'm using Core Data. As objects are downloaded, I put them in the managed object context, then on the UI side I have a set of NSFetchedResultsController objects to pull the objects for display in the sidebar. I have three manual sidebar supercategories with a separate FRC for each. I am trying to make a delegate for my FRCs which will insert the items into the sidebar as they are downloaded.
My NSFetchedResultsControllerDelegate conformance looks like this:
ObjectSidebarController has an IBOutlet myScrollView which references the scroll view in the sidebar. I build the NSOutlineView programmatically. Again, the NSOutlineView is view-based, not cell-based.
I have a bunch of print statements in my NSOutlineViewDataSource and NSOutlineViewDelegate letting me know when their methods are called. When I make the myOutlineView.insertItems call, I see the data source being asked for the supercategory I just inserted the new section into, and for child 0 of that supercategory:
But it doesn't ask the NSOutlineViewDelegate for the views. I have confirmed my code is called in the main thread, not a background thread. When I collapse the supercategory and open it back up, I get this in my logs:
So it seems to me I need to do something to tell the NSOutlineView that I want it to get the views for the supercategory and its child. I have tried calling NSOutlineView.reloadItem(_:reloadChildren:) immediately after inserting the item passing it the same object I just did for the insert's parent, but no change in logs or behavior.
Does anybody have any idea what I'm doing wrong? Based on the documentation, it looks like this should work.
Most of this background shouldn't be relevant to my issue, but it should be working, so clearly I'm missing something. My application downloads a set of objects from a server (think the users, computers, and groups in an Active Directory database) and displays them in a view-based NSOutlineView in the sidebar. I'm using Core Data. As objects are downloaded, I put them in the managed object context, then on the UI side I have a set of NSFetchedResultsController objects to pull the objects for display in the sidebar. I have three manual sidebar supercategories with a separate FRC for each. I am trying to make a delegate for my FRCs which will insert the items into the sidebar as they are downloaded.
My NSFetchedResultsControllerDelegate conformance looks like this:
Code Block Swift extension ObjectSidebarController: NSFetchedResultsControllerDelegate { func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { print("NSFRCD beginning updates.") (myScrollView.documentView! as! NSOutlineView).beginUpdates() } func controller( _ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { let myOutlineView:NSOutlineView = myScrollView.documentView! as! NSOutlineView switch type { case .insert: print("Inserting a section.") print("Index: \(sectionIndex)") let parent = myOutlineView.child(topLevelSectionIndex!, ofItem: nil)! print("inParent: \(String(describing: parent))") myOutlineView.insertItems( at: IndexSet([sectionIndex]), inParent: parent, withAnimation: .slideLeft) case .delete: print("Deleting a section.") default: return } } func controller( _ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { return } func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { print("NSFRCD ending updates.") (myScrollView.documentView! as! NSOutlineView).endUpdates() } }
ObjectSidebarController has an IBOutlet myScrollView which references the scroll view in the sidebar. I build the NSOutlineView programmatically. Again, the NSOutlineView is view-based, not cell-based.
I have a bunch of print statements in my NSOutlineViewDataSource and NSOutlineViewDelegate letting me know when their methods are called. When I make the myOutlineView.insertItems call, I see the data source being asked for the supercategory I just inserted the new section into, and for child 0 of that supercategory:
Code Block text Inserting a section. Index: 0 Entering outlineView(_: child:2 ofItem:nil) inParent: <supercategory> Entering outlineView(_: child:2 ofItem:nil) Entering outlineView(_: child:0 ofItem:Optional(<supercategory>)) About to return: <_NSDefaultSectionInfo: 0x600002eb6f80> Entering outlineView(_: child:2 ofItem:nil) Entering outlineView(_: child:0 ofItem:Optional(<supercategory>)) About to return: <_NSDefaultSectionInfo: 0x600002eb6f80> NSFRCD ending updates.
But it doesn't ask the NSOutlineViewDelegate for the views. I have confirmed my code is called in the main thread, not a background thread. When I collapse the supercategory and open it back up, I get this in my logs:
Code Block text Entering outlineView(_: numberOfChildrenOfItem:Optional(<supercategory>)) Entering outlineView(_: child:0 ofItem:Optional(<supercategory>)) About to return: <_NSDefaultSectionInfo: 0x600002eb6f80> Entering outlineView(_: viewFor:Optional(<NSTableColumn: 0x6000022152d0> identifier: objectName width: 200) item:<_NSDefaultSectionInfo: 0x600002eb6f80>)
So it seems to me I need to do something to tell the NSOutlineView that I want it to get the views for the supercategory and its child. I have tried calling NSOutlineView.reloadItem(_:reloadChildren:) immediately after inserting the item passing it the same object I just did for the insert's parent, but no change in logs or behavior.
Does anybody have any idea what I'm doing wrong? Based on the documentation, it looks like this should work.
Figured it out. My supercategories were defined as a struct with two properties: name and frc. While reading over some forum hits for the umpteenth time, this one finally clicked. Switched them over to use a class instead, and now everything is happy. From:
to this instead:
+5 lines of code, and -a million lines added for troubleshooting.
Code Block Swift struct sidebarCategoryItem { let name:String let frc:NSFetchedResultsController<CPMObject_CD> }
to this instead:
Code Block Swift class sidebarCategoryItem { let name:String let frc:NSFetchedResultsController<CPMObject_CD> init(name initName:String, frc initFRC:NSFetchedResultsController<CPMObject_CD>) { self.name = initName self.frc = initFRC } }
+5 lines of code, and -a million lines added for troubleshooting.