Illegal NSTableViewDataSource

Hello:


I am getting the following error:


Illegal NSTableView data source: Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:


My tableView implementation is as follows:


import Cocoa
import CoreData
class RegistrationReportsViewController: NSViewController {
   
    override func viewDidLoad() {
        super.viewDidLoad()
        showRegisteredStudents()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.reloadData()
    }
    private lazy var fetchedResultsController: NSFetchedResultsController = {
        let fetchRequest: NSFetchRequest = Registration.fetchRequest()
       let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
        frc.delegate = (self as! NSFetchedResultsControllerDelegate)
        return frc
    }()
   
var items: [NSManagedObject] = []
   
var managedObjectContext = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let persistentContainer: NSPersistentContainer = {
           let container = NSPersistentContainer(name: "ScorcentMasterReview")
          
           container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error {
                   fatalError("Unresolved error \(error)")
               }           })
           return container
       }()

  

    @IBOutlet weak var tableView: NSTableView!

         var firstNameItem = "LAST"
         var middleNameItem = "MIDDLE"
         var lastNameItem = "FIRST"
 
func showRegisteredStudents() {
guard (NSApplication.shared.delegate as? AppDelegate) != nil  else {
               return
           }
        let managedContext = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let fetchRequest = NSFetchRequest(entityName: "Registration")
        fetchRequest.returnsObjectsAsFaults = false
           fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]
    do {
          
       let items = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
       print ("There are \(items.count) items")
    
        _ = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
             // Configure Fetched Results Controller
       print("Records are \(items)")
       self.tableView.reloadData()
       return()
    } catch {
    fatalError("Failed to fetch employees: \(error)")
    }
}
   
}
    extension RegistrationReportsViewController: NSTableViewDataSource{
    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
    let numberOfRows:Int = items.count
        return numberOfRows
        }
    }
   
   
  //NSTableViewDelegate
extension RegistrationReportsViewController: NSTableViewDelegate{
    func configureCell(cell: NSTableCellView, row: Int, column: Int){

    let registration = fetchedResultsController.fetchedObjects! [row]
             switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
             }
        
  func tableView(tableView: NSTableView!, objectValueForTableColumn tableColumn: NSTableColumn!, row: Int) -> AnyObject!{
    _ = fetchedResultsController.fetchedObjects! [row]
        let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
        let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
//       cell?.textField?.stringValue = registration[(tableColumn?.identifier.rawValue)!]!
    configureCell(cell: cell!, row: row, column: column)
            return cell
       
   }

}

I would appreciate any help that points me in the right direction. Thanks.

Answered by Claude31 in 401877022

It is normal delegate is not called when 0 row. But as soon as at least 1, should be called.


Your code is wrong:

extension RegistrationReportsViewController: NSTableViewDelegate{
    func configureCell(cell: NSTableCellView, row: Int, column: Int){
    let registration = fetchedResultsController.fetchedObjects! [row]
        print ("Start NSTableView Delegate")
             switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
             }

        func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
         let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
         let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
         configureCell(cell: cell!, row: row, column: column)
         return cell
               }
          }
}


Look, viewFor is defined inside configureCell.

So it is not visible as a delegate func.


Correct code is:

extension RegistrationReportsViewController: NSTableViewDelegate {

    func configureCell(cell: NSTableCellView, row: Int, column: Int) {

        let registration = fetchedResultsController.fetchedObjects! [row]
        print ("Start NSTableView Delegate")
        switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
        }
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{

        let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
        let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
        configureCell(cell: cell!, row: row, column: column)
        return cell
     }

}


Note: a clear writing, with correct identation, does help to see it immediately.

Hello Calude31:


Here is the code I am running. This code runs with no errors, however the table view comes up blank. Seems like the NSTableViewDelegate is not read.


import Cocoa
import CoreData
class RegistrationReportsViewController: NSViewController {
    required init?(coder aDecoder: NSCoder) {
        self.items = []
        super.init(coder: aDecoder)
    }
    override func viewDidLoad() {
           super.viewDidLoad()
           tableView.dataSource = self     // Do this FIRST
           tableView.delegate = self
           tableView.reloadData()
           showRegisteredStudents()
               //  not needed, done in showRegisteredStudents
       }
    private lazy var fetchedResultsController: NSFetchedResultsController = {
        let fetchRequest: NSFetchRequest = Registration.fetchRequest()
       let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
        frc.delegate = (self as! NSFetchedResultsControllerDelegate)
        return frc
    }()
var items: [NSManagedObject]
   
var managedObjectContext = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let persistentContainer: NSPersistentContainer = {
           let container = NSPersistentContainer(name: "ScorcentMasterReview")
           container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error {
                   fatalError("Unresolved error \(error)")
               }           })
           return container
       }()
    @IBOutlet weak var tableView: NSTableView!
         var firstNameItem = "LAST"
         var middleNameItem = "MIDDLE"
         var lastNameItem = "FIRST"
func showRegisteredStudents() {
guard (NSApplication.shared.delegate as? AppDelegate) != nil  else {
               return
           }
        let managedContext = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let fetchRequest = NSFetchRequest(entityName: "Registration")
        fetchRequest.returnsObjectsAsFaults = false
           fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]
    do {
      let readItems = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
            print ("There are \(readItems.count) items")
             _ = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
                  // Configure Fetched Results Controller
            print("Records are \(readItems)")
            items = readItems
        print("There are \(items.count) items")// NOW items is filled
        tableView.reloadData()
            return()
      } catch {
           fatalError("Failed to fetch employees: \(error)")
      }
    }
    }
extension RegistrationReportsViewController: NSTableViewDataSource{
    // DataSource
    func numberOfRows(in tableView: NSTableView) -> Int {
    let numberOfRows:Int = items.count
        print("dataSource rows:", numberOfRows)
        return numberOfRows
        }
}
        // Delegate
  extension RegistrationReportsViewController: NSTableViewDelegate{
    func configureCell(cell: NSTableCellView, row: Int, column: Int){
    let registration = fetchedResultsController.fetchedObjects! [row]
        print ("Start NSTableView Delegate")
             switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
             }
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
         let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
         let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
         configureCell(cell: cell!, row: row, column: column)
         return cell
               }
          }
}

debug log:

CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME

CoreData: annotation: sql connection fetch time: 0.0003s

CoreData: annotation: fetch using NSSQLiteStatement <0x60000212a940> on entity 'Registration' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME' returned 1 rows with values: (

"<Registration: 0x60000212aa30> (entity: Registration; id: 0xd17d57bbd01100d <x-coredata://7F2F361B-2C31-4CC0-9E6D-660E8277D9B9/Registration/p3>; data: {\n firstName = Waithley;\n lastName = Williams;\n middleName = Lionel;\n})"

)

CoreData: annotation: total fetch execution time: 0.0009s for 1 rows.

CoreData: details: SQLite: EXPLAIN QUERY PLAN SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME

3 0 0 SCAN TABLE ZREGISTRATION AS t0

14 0 0 USE TEMP B-TREE FOR ORDER BY

There are 1 items

Records are [<Registration: 0x60000212aa30> (entity: Registration; id: 0xd17d57bbd01100d <x-coredata://7F2F361B-2C31-4CC0-9E6D-660E8277D9B9/Registration/p3>; data: {

firstName = Waithley;

lastName = Williams;

middleName = Lionel;

})]

There are 1 items

dataSource rows: 1

Hello Claude31:


It seems that the the NSTableViewDataSource is read three times. Two of these times it is empty: Looke at the log below:

line 64 above is the test:


     print("dataSource rows:", numberOfRows)


CoreData: annotation: Connecting to sqlite database file at "/Users/wlionelwilliams/Library/Containers/AppDev.ScorcentMasterReview/Data/Library/Application Support/ScorcentMasterReview/ScorcentMasterReview.sqlite"

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'

CoreData: sql: pragma recursive_triggers=1

CoreData: sql: pragma journal_mode=wal

CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_MODELCACHE'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'ACHANGE'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'ATRANSACTIONSTRING'

CoreData: annotation: Connecting to sqlite database file at "/Users/wlionelwilliams/Library/Containers/AppDev.ScorcentMasterReview/Data/Library/Application Support/ScorcentMasterReview/ScorcentMasterReview.sqlite"

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'

CoreData: sql: pragma recursive_triggers=1

CoreData: sql: pragma journal_mode=wal

CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_MODELCACHE'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'ACHANGE'

CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'ATRANSACTIONSTRING'

dataSource rows: 0

dataSource rows: 0

CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME

CoreData: annotation: sql connection fetch time: 0.0003s

CoreData: annotation: fetch using NSSQLiteStatement <0x60000212a940> on entity 'Registration' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME' returned 1 rows with values: (

"<Registration: 0x60000212aa30> (entity: Registration; id: 0xd17d57bbd01100d <x-coredata://7F2F361B-2C31-4CC0-9E6D-660E8277D9B9/Registration/p3>; data: {\n firstName = Waithley;\n lastName = Williams;\n middleName = Lionel;\n})"

)

CoreData: annotation: total fetch execution time: 0.0009s for 1 rows.

CoreData: details: SQLite: EXPLAIN QUERY PLAN SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZFIRSTNAME, t0.ZLASTNAME, t0.ZMIDDLENAME FROM ZREGISTRATION t0 ORDER BY t0.ZLASTNAME

3 0 0 SCAN TABLE ZREGISTRATION AS t0

14 0 0 USE TEMP B-TREE FOR ORDER BY

There are 1 items

Records are [<Registration: 0x60000212aa30> (entity: Registration; id: 0xd17d57bbd01100d <x-coredata://7F2F361B-2C31-4CC0-9E6D-660E8277D9B9/Registration/p3>; data: {

firstName = Waithley;

lastName = Williams;

middleName = Lionel;

})]

There are 1 items

dataSource rows: 1

Accepted Answer

It is normal delegate is not called when 0 row. But as soon as at least 1, should be called.


Your code is wrong:

extension RegistrationReportsViewController: NSTableViewDelegate{
    func configureCell(cell: NSTableCellView, row: Int, column: Int){
    let registration = fetchedResultsController.fetchedObjects! [row]
        print ("Start NSTableView Delegate")
             switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
             }

        func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
         let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
         let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
         configureCell(cell: cell!, row: row, column: column)
         return cell
               }
          }
}


Look, viewFor is defined inside configureCell.

So it is not visible as a delegate func.


Correct code is:

extension RegistrationReportsViewController: NSTableViewDelegate {

    func configureCell(cell: NSTableCellView, row: Int, column: Int) {

        let registration = fetchedResultsController.fetchedObjects! [row]
        print ("Start NSTableView Delegate")
        switch column {
             case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
             case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
             case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
             default:
                 break
        }
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{

        let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
        let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
        configureCell(cell: cell!, row: row, column: column)
        return cell
     }

}


Note: a clear writing, with correct identation, does help to see it immediately.

Hello Claude31:


I get a crash on line 5 of this code when I move viewForTableColumn outside configureCell:


func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
   
          let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView
          let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
          configureCell(cell: cell!, row: row, column: column)
          return cell
       }

Crash log: unexpectedly found nil when implicitly unwrapping ...

That means your cell is nil, because it was not yet created. You did not see before because it was never called.


So, do this:


extension RegistrationReportsViewController: NSTableViewDelegate {
   
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
       
        if tableColumn == nil { return nil }    // Extra safety
        if let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView {
            let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
            let registration = fetchedResultsController.fetchedObjects! [row]
            print ("Start NSTableView Delegate")
           
            switch column {
            case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
            case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
            case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
            default:
                break
            }
            return cell
        }
        return nil  // Failed
    }
   
}

A possible cause: why do you use the column identifier for the cell ?

You should use the identifier that you defined for cells.


Look in IB hierarchy of objects.

Should have

View

ScrollView

ClipView

TableView

Column1 => That's the identifier you use

TableCellView => That's the identifier you have to use

TableViewCell

Column2


Then the old code should work as well as the new one.


I usually use:


let cellIdentifier = "cellID"     // The one that was set in IB
 if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: self) as? NSTableCellView {

Hello Claude31:


I am not clear how to use the cell Identifier rather than the column Identifier in Code. lastName, firstName, middleName are members of the entity: Registration so I can use:


cell.textField?.stringValue = registration.firstName ?? "FNCellID"


But I don't understand how to use the cell ID otherwise.

Do you create the tableView in IB ?


If so, look at hierarchy of objects in IB for the viewController.

Should have

View

ScrollView

ClipView

TableView

Column1 => That's the identifier you use

TableCellView => That's the identifier you have to use

TableViewCell

Column2


Do you get this ?


Then set the identifier for the tableViewCell (if you have multiple columns, if they use the same cell prototype, just need to define once.


Note: you could also give the same identifier for each column and its tableCellView (not the TableViewCell !) and keep using your code.


To check the problem is caused by the identifier, add a print:


extension RegistrationReportsViewController: NSTableViewDelegate {
   
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
       
        if tableColumn == nil { return nil }    // Extra safety
        if let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView {
            let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
            let registration = fetchedResultsController.fetchedObjects! [row]
            print ("Start NSTableView Delegate")
           
            switch column {
            case 0:
                cell.textField?.stringValue = registration.lastName ?? ""
            case 1:
                cell.textField?.stringValue = registration.firstName ?? ""
            case 2:
                cell.textField?.stringValue = registration.middleName ?? ""
            default:
                break
            }
            return cell
        }
        print("Could not create a cell with Identifier", tableColumn!.identifier).  // ADD THIS PRINT
        return nil  // Failed
    }
   
}

With the suggested changes (as I understand them), this is what I have:


extension RegistrationReportsViewController: NSTableViewDelegate {
     
     func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
         
        if tableColumn == nil { return nil }
        let cellIdentifier = "NSTV"     // The one that was set in IB
        if (tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: self) as? NSTableCellView) != nil {
        // Extra safety
         if let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView {
             let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
             let registration = fetchedResultsController.fetchedObjects! [row]
             print ("Start NSTableView Delegate")
             
             switch column {
             case 0:
                 cell.textField?.stringValue = registration.lastName ?? "LNCellID"
             case 1:
                 cell.textField?.stringValue = registration.firstName ?? "FNCellID"
             case 2:
                 cell.textField?.stringValue = registration.middleName ?? "MNCellID"
             default:
                 break
             }
             return cell
         }
            print("Could not create a cell with Identifier", tableColumn!.identifier)  // ADD THIS PRINT
         return nil  // Failed
     }
      return nil
}
}

But NONE of the print tests print anything!

I do get the hierarchy you outlined:


View

ScrollView

ClipView

TableView

Column1 => That's the identifier you use (Where in the code I used this?)

TableCellView => That's the identifier you have to use

TableViewCell

Column2



Where did I use the Column identifier? Not sure.

You use it here :

if let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView {


if I have different cell prototypes for each colum, then I create an identifier for each.


extension RegistrationReportsViewController: NSTableViewDelegate {
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
                
        if tableColumn == nil { return nil }    // Extra safety
       
        var cellIdentifier = ""
        var cellContent = ""
        let registration = fetchedResultsController.fetchedObjects! [row]
        let column = tableView.tableColumns.firstIndex(of: tableColumn!)!
        switch column {
        case 0:
            cellContent = registration.lastName ?? ""
            cellIdentifier = "CellIdLastName"   // The one you give in IB of course
        case 1:
            cellContent = registration.firstName ?? ""
            cellIdentifier = "CellIdFirstName"  // Take care of uppercases
        case 2:
            cellContent = registration.middleName ?? ""
            cellIdentifier = "CellIdMiddleName"
        default:
            break
        }

        if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: self) as? NSTableCellView {
            print ("Start NSTableView Delegate")
           
            cell.textField?.stringValue = cellContent
            return cell
        }
        print("Could not create a cell with Identifier", tableColumn!.identifier).  // ADD THIS PRINT
        return nil  // Failed
    }
    
}


So, at the end, you should have in IB:


View

ScrollView

ClipView

TableView

Column1 => lastName column

TableCellView => CellIdLastName

TableViewCell

Column2 => firstName column

TableCellView => CellIdFirstName

TableViewCell

Column3 => middleName column

TableCellView => CellIdMiddleName

TableViewCell

now i am getting error:

An instance of NSFetchedResultsController requires a fetch request with sort descriptors

That's a totally different point.


Do you know how to use CoreData ?

If not, you should first study it before trying to code this (note: that was my initial suggestion to have a simpler solution to begin with than CoreData, powerful but more complex to master for a beginner).


Anyhow, have a look here, that may provide some hint to solution.

https://stackoverflow.com/questions/43463064/an-instance-of-nsfetchedresultscontroller-requires-a-non-nil-fetchrequest-and-ma


Look also at a typical code: (h ttps://medium.com/@maddy.lucky4u/swift-4-core-data-part-6-using-nsfetchresultcontroller-with-uitableview-a17b045854ce)


NSFetchedResultsController refers to a class

fetchRequest refers to an entity

and you define sort descriptor


lazy var fetchedResultsController: NSFetchedResultsController = {
  // Initialize Fetch Request
  let appDelegate = UIApplication.shared.delegate as! AppDelegate

  /*Before you can do anything with Core Data, you need a managed object context. */
  let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext

  /*As the name suggests, NSFetchRequest is the class responsible for fetching from Core Data.
  Initializing a fetch request with init(entityName:), fetches all objects of a particular entity. This is what you do here to fetch all Person entities.
  */
  let fetchRequest = NSFetchRequest(entityName: "Person")

  // Add Sort Descriptors
  let sortDescriptor = NSSortDescriptor(key: "name", ascending: false)
  fetchRequest.sortDescriptors = [sortDescriptor]

  // Initialize Fetched Results Controller
  let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)

  // Configure Fetched Results Controller
// fetchedResultsController.delegate = self

  return fetchedResultsController
  }()


You should close the thread now, because the original question about the delegate was solved (that's what I understand) and this thread is much too long.

Mark the correct answer on my answer of Jan 11, 2020 11:00 PM.

Open a new thread about Core Data.

Hello Claude31:

You have been very patient and thorough in your assistance.


Thanks Very Much !!

Illegal NSTableViewDataSource
 
 
Q