The IPlugCocoaUI example demonstrates how to build native plugin interfaces using Cocoa (macOS) and UIKit (iOS) with Interface Builder storyboards. This approach is ideal when you need native controls, existing UIKit components, or want to design your UI visually.
What This Example Demonstrates
Interface Builder storyboard integration
Cocoa/UIKit native controls
Bidirectional parameter communication
Real-time audio metering
Custom message passing
Preset management from UI
Architecture
C++ Plugin Audio processing and parameter definitions (IPlugCocoaUI.mm)
View Controller UI logic and event handling (Swift or Objective-C)
Storyboard Visual UI layout created in Interface Builder
CocoaEditorDelegate Base class providing C++/Cocoa communication
C++ Plugin Implementation
Loading the Storyboard
IPlugCocoaUI :: IPlugCocoaUI ( const InstanceInfo & info)
: iplug :: Plugin (info, MakeConfig (kNumParams, kNumPresets))
{
GetParam (kParamGain)-> InitGain ( "Volume" , - 70.0 );
MakePreset ( "Gain = -70dB" , - 70. );
MakePreset ( "Gain = -10dB" , - 10. );
MakePreset ( "Gain = 0dB" , 0. );
#ifdef OS_MAC
NSStoryboard * pStoryBoard = [NSStoryboard
storyboardWithName:@ "IPlugCocoaUI-macOS-MainInterface"
bundle: [NSBundle bundleWithIdentifier:
[NSString stringWithUTF8String: GetBundleID ()]]];
auto * vc = (IPlugCocoaUIViewController * )
[pStoryBoard instantiateControllerWithIdentifier:@ "main" ];
[vc retain];
[vc setEditorDelegate: this ];
mViewController = vc;
vc . view . frame = MAKERECT ( 0. f , 0. f ,
( float ) PLUG_WIDTH, ( float ) PLUG_HEIGHT);
#endif
}
The storyboard is loaded from the plugin bundle, allowing you to design the UI visually in Xcode’s Interface Builder.
Opening the Window
void* IPlugCocoaUI :: OpenWindow ( void* pParent )
{
PLATFORM_VIEW * platformParent = (PLATFORM_VIEW * ) pParent;
#ifdef FRAMEWORK_BUILD
auto * vc = [[(PLATFORM_VC*) [ platformParent nextResponder ]
childViewControllers ] objectAtIndex : 0 ];
[ vc setEditorDelegate : this ];
mViewController = vc ;
#else
IPlugCocoaUIViewController * vc =
(IPlugCocoaUIViewController*) mViewController ;
[ platformParent addSubview : vc . view ];
#endif
OnUIOpen ();
return vc . view ;
}
Handling Window Resize
void IPlugCocoaUI :: OnParentWindowResize ( int width , int height )
{
auto * vc = (IPlugCocoaUIViewController * ) mViewController;
vc . view . frame = MAKERECT ( 0. f , 0. f , ( float ) width, ( float ) height);
}
Audio Processing with Metering
void IPlugCocoaUI :: ProcessBlock ( sample ** inputs , sample ** outputs , int nFrames )
{
const double gain = GetParam (kParamGain)-> DBToAmp ();
const int nChans = NOutChansConnected ();
for ( int s = 0 ; s < nFrames; s ++ ) {
for ( int c = 0 ; c < nChans; c ++ ) {
outputs [c][s] = inputs [c][s] * gain;
}
}
// Send peak data to UI for metering
mSender . ProcessBlock (inputs, nFrames, kCtrlTagVUMeter);
}
void IPlugCocoaUI :: OnIdle ()
{
mSender . TransmitData ( * this );
}
IPeakSender analyzes the audio signal and sends peak values to the UI thread for VU meter display.
Message Handling
Receiving Messages from UI
bool IPlugCocoaUI :: OnMessage ( int msgTag , int ctrlTag , int dataSize , const void* pData )
{
if (msgTag == kMsgTagHello)
{
DBGMSG ( "MsgTagHello received on C++ side \n " );
return true ;
}
else if (msgTag == kMsgTagRestorePreset)
{
RestorePreset (ctrlTag);
}
return CocoaEditorDelegate :: OnMessage (msgTag, ctrlTag, dataSize, pData);
}
Observing Parameter Changes
void IPlugCocoaUI :: OnParamChange ( int paramIdx )
{
DBGMSG ( "Param change %i : %f \n " , paramIdx, GetParam (paramIdx)-> Value ());
}
View Controller (Swift)
While the example includes a Swift view controller, here’s the typical pattern:
class IPlugCocoaUIViewController : IPlugCocoaViewController {
@IBOutlet weak var gainSlider: NSSlider !
@IBOutlet weak var vuMeter: VUMeterView !
override func viewDidLoad () {
super . viewDidLoad ()
// Setup UI elements
gainSlider. minValue = getParameterMin (kParamGain)
gainSlider. maxValue = getParameterMax (kParamGain)
gainSlider. doubleValue = getParameterDefault (kParamGain)
}
// Called when user moves slider
@IBAction func gainSliderChanged ( _ sender : NSSlider) {
let paramIdx = kParamGain
if sender.isPressed {
beginInformHostOfParamChangeFromUI (paramIdx)
}
sendParameterValueFromUI (paramIdx, sender. doubleValue )
if ! sender.isPressed {
endInformHostOfParamChangeFromUI (paramIdx)
}
}
// Called when parameter changes from host/automation
override func onParamChangeUI ( _ paramIdx : Int , _ value : Double ) {
if paramIdx == kParamGain {
gainSlider. doubleValue = value
}
}
// Called when control data arrives from C++
override func sendControlMsgFromDelegate (
ctrlTag : Int ,
msgTag : Int ,
msg : Data !
) {
if ctrlTag == kCtrlTagVUMeter {
// Extract peak values and update meter
let peaks = extractPeakData ( from : msg)
vuMeter. setLevel (peaks[ 0 ])
}
}
// Send preset change message to C++
@IBAction func presetButtonClicked ( _ sender : NSButton) {
let presetIdx = sender. tag
sendArbitraryMsgFromUI (
msgTag : kMsgTagRestorePreset,
ctrlTag : presetIdx,
msg : nil
)
}
}
Communication Methods
Parameter Changes // Start gesture
beginInformHostOfParamChangeFromUI (paramIdx)
// Update value
sendParameterValueFromUI (paramIdx, normalizedValue)
// End gesture
endInformHostOfParamChangeFromUI (paramIdx)
Custom Messages sendArbitraryMsgFromUI (
msgTag : kMsgTagHello,
ctrlTag : 0 ,
msg : dataObject // Optional NSData
)
Parameter Updates override func onParamChangeUI ( _ paramIdx : Int , _ value : Double ) {
// Update UI controls with new parameter value
}
Control Messages override func sendControlMsgFromDelegate (
ctrlTag : Int ,
msgTag : Int ,
msg : Data !
) {
// Handle custom data from C++
}
MIDI Messages override func onMidiMsgUI (
_ status : UInt8 ,
_ data1 : UInt8 ,
_ data2 : UInt8 ,
_ offset : Int
) {
// Handle incoming MIDI
}
Creating the Storyboard
Create storyboard in Xcode
Add a new Storyboard file to your project (e.g., IPlugCocoaUI-macOS-MainInterface.storyboard).
Design the UI
Drag controls (sliders, buttons, labels) onto the view controller in Interface Builder.
Set the storyboard ID
Select the view controller and set its Storyboard ID (e.g., “main”) in the Identity Inspector.
Create IBOutlets and IBActions
Connect UI elements to your view controller code using outlets and actions.
Set the custom class
Set the view controller’s custom class to your subclass of IPlugCocoaViewController.
AUv3 View Configuration
bool IPlugCocoaUI :: OnHostRequestingSupportedViewConfiguration (
int width ,
int height
) {
#ifdef OS_MAC
// Logic/GB offer one option with 0w, 0h
return ((width + height) == 0 );
#else
return true ;
#endif
}
Advantages of Cocoa UI
Visual Design Design your UI visually in Interface Builder with drag-and-drop
Native Controls Use standard macOS/iOS controls with native look and behavior
Auto Layout Built-in support for responsive layouts and constraints
Accessibility Automatic accessibility support with VoiceOver
Like SwiftUI, Cocoa UI is only available on Apple platforms. For cross-platform plugins, consider IGraphics or web-based UIs.
CocoaEditorDelegate Methods
Inherited from IPlugCocoaViewController:
parameterCount() - Get total number of parameters
getParameterName(idx) - Get parameter name
getParameterMin/Max/Default(idx) - Get parameter range info
getParameterValue(idx) - Get current parameter value
getParameterStep(idx) - Get parameter step size
getParameterLabel(idx) - Get parameter units/label
getBundleID() - Get plugin bundle identifier