How to create a NSSearchField outlet for item in the toolbar?

Using Xcode 11/12 I want to create an outlet for a searchField located in the toolbar in the view controller.

Tried control drag to the class for the view controller but that did not work.

I know how to create action outlets for toolbar items but in this case I want to create a NSSearchField outlet in my view controller.

How is this done?

Answered by BigEagle in 635144022
With the help from another developer I got this all working. Here is the solution:
This assumes the search field is last item.
Code Block
override func viewWillAppear() {
guard let toolbar = self.view.window?.toolbar else {
return
}
guard let searchFieldToolbarItem = toolbar.items.last else {
return
}
guard let searchField = searchFieldToolbarItem.view as? NSSearchField else {
return
}
searchField.target = self
searchField.action = #selector (procSearchFieldInput (sender:))
}
@objc func procSearchFieldInput (sender:NSSearchField) {
print ("\(#function): \(sender.stringValue)")
}

If the dragged outlet doesn't descend into NSToolbar (and the NSToolbarItem containing your view), you can always do the reverse by disclosing the toolbar, then clicking into the custom Item and selecting your NSSearchField. From there and the Connections inspector, drag the New Referencing Outlet to your view controller.
I have made some progress on this by using a custom window like in the following code:

Code Block
import Cocoa
class CustWindow: NSWindow {
    @IBOutlet var toolBar: NSToolbar!
}

connecting my toolbar search filed to the outlet above then in the view controller I have the following:

Code Block
   override func viewWillAppear() {
          if let window = self.view.window as? CustWindow {
            print ("\(window.toolbar.identifier)")
            let toolBarItem = NSToolbar(identifier: window.toolbar.identifier)
          print(toolBarItem)
          }
      }


Sounds like your way may be easier. What did you mean by "Disclosing the toolbar"?

You don't need a custom NSWindow subclass to declare storage and an outlet for NSToolbar, it already has a reference.
It's not entirely clear to me what you're trying to achieve. Is the goal to have a reusable view controller which discovers an NSSearchField located in the window containing the view?
If so, you should strongly consider using an identifier for the search field, which the view controller declares as a public constant. The contract then is the window (e.g. representing a document) contains its own arrangement and customization of the toolbar and field (which uses the identifier). At some point it has the view of this view controller inserted into its hierarchy. When that happens, viewWillAppear() or similar mechanism searches the toolbar of the view's containing window and locates the NSToolbarItem with its defined identifier. What it does with that reference is then up to you.
Normally IBOutlets and IBActions are used when there's a strong contract between the interface file and a class, usually the File's Owner. If you're looking to discover a view from a reusable class (like a view controller provided by a framework), something like an identifier or tag is a better "fit".
I currently have a view controller which has a search field in it connected by an @IBOutlet and I have this working fine. So, I wanted to move the search field from the view to the toolbar of the window controller. I initially though I could connect the new search field to the view controller to enable the search function to search a table view in my custom view controller class. However, I ran in an issue exposing the search field in the toolbar to my custom view controller. Does this help? I will look over your response tomorrow and see what I can glean from it.

thanks for the reply
Accepted Answer
With the help from another developer I got this all working. Here is the solution:
This assumes the search field is last item.
Code Block
override func viewWillAppear() {
guard let toolbar = self.view.window?.toolbar else {
return
}
guard let searchFieldToolbarItem = toolbar.items.last else {
return
}
guard let searchField = searchFieldToolbarItem.view as? NSSearchField else {
return
}
searchField.target = self
searchField.action = #selector (procSearchFieldInput (sender:))
}
@objc func procSearchFieldInput (sender:NSSearchField) {
print ("\(#function): \(sender.stringValue)")
}

Sounds like what you really want is a "shared" search field that can delegate to the current view controller.
In that case you'd use a protocol which your view controller(s) conform to, then an API (possibly in your window controller) which inserts a search field (if it doesn't already exist, or discovers one using an identifier) and connects to the current view controller to send it the relevant messages and provide a reference. The view controller should "attach" then "detach" using the protocol, and the true ownership should be by e.g. the window controller.
How to create a NSSearchField outlet for item in the toolbar?
 
 
Q