Integrating a Toolbar and Touch Bar into Your App
At a glance
| Item | Summary |
|---|---|
| Purpose | Offer the same font-size and font-style commands through an NSToolbar and an NSTouchBar. |
| Architecture | A content view controller owns the text view; one window controller owns command logic and constructs both command surfaces. |
| Shared state | currentFontSize is the binding point between toolbar controls, the touch-bar slider, and text mutations. |
Project structure
ToolbarSample/
├── AppDelegate.swift
├── ViewController.swift
├── WindowController.swift
└── WindowController+TouchBar.swift
The touch-bar extension keeps a secondary framework surface separate while reusing the window controller’s formatting commands and state.
Overall architecture
flowchart LR
AppDelegate --> WindowController
WindowController --> Toolbar["NSToolbar items"]
WindowController --> TouchBar["NSTouchBar items"]
Toolbar --> Commands["font size / style commands"]
TouchBar --> Commands
Commands --> TextView["ViewController.NSTextView"]
Reference code
ToolbarSample/WindowController.swift:86 — command logic reaches the single content text view through the window graph.
func contentTextView() -> NSTextView {
let viewController = self.contentViewController as? ViewController
return viewController!.textView
}Ownership and state
classDiagram
WindowController *-- NSToolbar : outlet and delegate
WindowController *-- NSTouchBar : creates
WindowController --> ViewController : content controller
ViewController *-- NSTextView : outlet
WindowController *-- Int : currentFontSize
NSSliderTouchBarItem --> WindowController : value binding
Ownership evidence
ToolbarSample/WindowController.swift:17 — the window controller holds the shared control state and toolbar objects.
class WindowController: NSWindowController, NSToolbarDelegate {
// ...
}The storyboard owns initial controller and outlet construction. WindowController is the mutation authority for formatting and control synchronization; ViewController owns the actual text-view outlet and only customizes text-view delegate behavior.
Class and protocol design
| Type | Responsibility | Contract |
|---|---|---|
WindowController |
Command state, text formatting, toolbar factory, printing | NSToolbarDelegate |
WindowController extension |
Touch-bar factory and item actions | NSTouchBarDelegate |
ViewController |
Hold the editable text view and suppress system text items | NSTextViewDelegate |
AppDelegate |
Enable application-wide touch-bar customization UI | NSApplicationDelegate |
No app-defined protocol is needed because both surfaces call the same concrete command owner through target/action and binding.
Access control
| Boundary | Effect and rationale |
|---|---|
private toolbar identifier extension |
Keeps identifier constants file-scoped and prevents unrelated files from depending on them. |
private touch-bar customization identifier extension |
Provides the same namespace boundary for the touch-bar file. |
Implicit internal controllers and commands |
Supports storyboard and cross-file extension access inside the app target only. |
@objc dynamic currentFontSize |
Exposes the property to Cocoa binding/KVO; this is runtime visibility, not public API. |
The two identifier boundaries are visible at ToolbarSample/WindowController.swift:11 and ToolbarSample/WindowController+TouchBar.swift:10.
Logic ownership and placement
| Logic | Owner |
|---|---|
| Font conversion and selection handling | WindowController |
| Toolbar item factory and allowed/default sets | WindowController.swift |
| Touch-bar item factory | WindowController+TouchBar.swift |
| Text-system touch-bar filtering | ViewController |
| App-wide customization opt-in | AppDelegate |
Design patterns
| Pattern | Evidence | Purpose |
|---|---|---|
| Shared command controller | ToolbarSample/WindowController.swift:94 |
One formatting implementation serves two UI surfaces. |
| Delegate factory | ToolbarSample/WindowController.swift:281 |
Creates toolbar items from identifiers. |
| Target/action | ToolbarSample/WindowController+TouchBar.swift:57 |
Routes controls to the window command owner. |
| Cocoa binding | ToolbarSample/WindowController+TouchBar.swift:83 |
Synchronizes the touch-bar slider with shared size state. |
| Capability extension | ToolbarSample/WindowController+TouchBar.swift:20 |
Isolates touch-bar integration without duplicating the controller. |
Naming conventions
- Surface-specific code uses a
+TouchBarfilename while retaining the owning type name. - Commands describe user intent:
setTextViewFontSize,setTextViewFont, andchangeFontSizeBySlider. - Identifier names mirror controls:
fontSize,fontStyle,popover, andpopoverSlider.
Architecture takeaways
- Route multiple command surfaces through one mutation owner.
- Separate framework-specific factories while sharing state and domain commands.
- Use file-private identifier namespaces to avoid string scattering.
- For a larger app, inject a formatting command object rather than reaching through
contentViewController; this sample stays deliberately controller-centric.
Source map
| Source | Role |
|---|---|
ToolbarSample/WindowController.swift:17 |
Shared command and toolbar owner |
ToolbarSample/WindowController+TouchBar.swift:20 |
Touch-bar factory |
ToolbarSample/ViewController.swift:10 |
Text-view owner |
ToolbarSample/AppDelegate.swift:14 |
Customization opt-in |