0

I want to include a view-based NSTableView in the popover of a Safari App Extension.

Starting with the default project in Xcode, I made the SFSafariExtensionViewController the delegate and datasource for the table view as it is the only content on the popover, and mostly this works.

I can populate the table and implement methods like tableView(_:shouldSelectRow:), yet methods which return a notification object such as tableViewSelectionDidChange(_:) do not get called.

Whilst those methods show a cludgy way of knowing when a row is selected, I am left with no way of knowing when a cell is edited.

As I had to connect the delegate outlet of the NSTableView to the File Owner to allow the delegated methods to work, I also tried connecting the dataSource outlet too, but this rightly did not help.

Here is the essence of my code (which for now includes returning dummy table data to test editing):

class SafariExtensionViewController: SFSafariExtensionViewController {

    @IBOutlet weak var tableView: NSTableView!

    static let shared: SafariExtensionViewController = {

        let shared = SafariExtensionViewController()
        shared.preferredContentSize = NSSize(width:445, height:421)
        return shared

    }()

    override func viewDidLoad() {

        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self

    }


    func textDidEndEditing(_ notification: Notification) {

        NSLog("I will NEVER appear in the console")

    }

}


extension SafariExtensionViewController: NSTableViewDataSource {

    func numberOfRows(in tableView: NSTableView) -> Int {

        return 5

    }

}


extension SafariExtensionViewController: NSTableViewDelegate {

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

        let cellView = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? NSTableCellView
        cellView?.textField?.stringValue = "Blah"
        return cellView

    }

    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
        NSLog("I will appear in the console")
        return true
    }


    func tableViewSelectionDidChange(_ notification: Notification) {

        NSLog("I will NEVER appear in the console")

    }

    func controlTextDidEndEditing(_ obj: Notification) {

        NSLog("I will NEVER appear in the console")

    }

}

(Obviously I do not need both textDidEndEditing(_:) and controlTextDidEndEditing(_:) but I am just trying everything.)

I am guessing the problem is something to do with the table view not being registered for notifications within a SFSafariExtensionViewController? That object inherits from NSViewController, though, so I would have thought these methods should work automatically.

This is my first time using swift, and it is a long time since I wrote a Mac app. But the actual functionality of the extension works, now I just want to have the ability to customize the settings through the UI.

However there seems to be very little written about Safari app extension programming, Apple's documentation is sparse, and I have not even been able to find any code examples featuring a table view in a popover to learn from.

I am probably missing something very obvious, but I have run out of searches to try on here and the web in general, so any help will be appreciated.

UPDATE:

I think I have an answer, by explicitly linking the NSTextFields in the table to the File's Owner as a delegate, the tableViewSelectionDidChange(_:) and controlTextDidEndEditing(_:) methods are now working. There must have been something else wrong causing the former to not work that I accidentally broke and fix, but it makes some sense for the latter.

That is all I need for the functionality to work, however I am still confused why the textDidEndEditing(_:) is still not working when I am led believe it should.

And in Apple's documentation, textDidEndEditing(_ :) is a method of an NSTextField, which links to a page saying controlTextDidEndEditing(_ :) is deprecated

And I misunderstanding anything?

Rebecka
  • 1,213
  • 8
  • 14
  • That's because you don't have all delegates related to the table view? I only see `NSTableViewDelegate`. – El Tomato Jan 09 '20 at 23:09
  • What other delegates are needed than the NSTableViewDelegate and NSTableViewDataSource? SFSafariExtensionViewController inherits from NSViewController and my understanding is that the table view becomes the delegate for any contained NSTextFields so nothing extra is required for those methods. – Rebecka Jan 10 '20 at 09:48

1 Answers1

0

I think you are not setting up the outlet properly please confirm this. Also check you setting up reusable identifier? identifier. for me all delegate calling without no issue after that.

IBOUTLET Setting

Muhammad Shauket
  • 2,643
  • 19
  • 40
  • I have tableView linked to the File's Owner under Referencing Outlets as in your screen shot, as well as the delegate under Outlets. The latter was necessary as Xcode warned `Action 'tableTextFieldUpdated:' sent by 'Table View Cell' is connected to 'File's Owner,' an invalid target (Objects inside view based table views may only be connected to the table view's delegate.)` – Rebecka Jan 10 '20 at 10:05
  • What do you mean by reusable identifier? I have an identifiers for the NSTableColumns an NSTextFields, though linking the fields to an outlet gives the warning `Outlet 'urlField' of 'File's Owner' is connected to 'Url Field,' an invalid destination (Object may be repeated at runtime.)` – Rebecka Jan 10 '20 at 10:15
  • what is this "identifier" let cellView = tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView – Muhammad Shauket Jan 10 '20 at 10:21
  • That was a mistake in stripping down the code to post the skeleton of it here, I had already corrected it in the question to "tableColumn!.identifier". In my code I had stored this in a constant so I could tailor the data to the appropriate column. – Rebecka Jan 11 '20 at 09:06