Extension Setup
Create a lightweight ExtensionKit target that depends on DynamicLakeKit.
Overview
A DynamicLake extension presents UI. Keep the main app, settings, networking, and larger app logic in the companion app when possible.
Steps
- Create a companion macOS app target.
- Choose the companion app Dock behavior.
LSUIElementis recommended for extension-only containers and optional for normal visible apps. - Create an ExtensionKit target.
- Add DynamicLakeKit with Swift Package Manager to the companion app target and the extension target. See AddSPM.
- If you use a prebuilt framework instead of SPM, embed and sign DynamicLakeKit in the companion app target.
- Create an entry point that conforms to
DynamicLakeExtension. - Bind the extension point with
@AppExtensionPoint.Bind. - Add
DynamicLakeLiveActivityfor compact UI. - Add
DynamicLakeSneakPeekonly when the activity needs expanded preview UI. - Add
DynamicLakeSettingsonly when the extension should expose settings inside DynamicLake Pro. - Set the extension point identifier in the extension target's Info.plist (see below). Without this exact value, DynamicLake's ExtensionKit discovery will never find your extension - the extension can still send activity updates over the companion-app API, but its UI will never be hosted.
import DynamicLakeKit
import SwiftUI
@main
struct ClockDynamicLakeExtension: DynamicLakeExtension {
@available(macOS 26.2, *)
@AppExtensionPoint.Bind
var boundExtensionPoint: AppExtensionPoint {
AppExtensionPoint.Identifier(
host: DynamicLakeExtensionPoint.hostBundleIdentifier,
name: DynamicLakeExtensionPoint.name
)
}
var body: some DynamicLakeExtensionScene {
DynamicLakeLiveActivity {
ClockLiveActivityView()
}
DynamicLakeSneakPeek {
ClockSneakPeekView()
}
DynamicLakeSettings {
ClockSettingsView()
}
}
}
Container App Dock Behavior
For extension-only companion apps, you can set LSUIElement to true in the companion app target's Info.plist so macOS treats it as an agent app. This keeps the container from remaining visible as a normal Dock app or showing a normal menu bar when it launches for registration, settings, or helper work.
<key>LSUIElement</key>
<true/>
In Xcode, this is Application is agent (UIElement). Set it to YES for extension-only containers. Leave it unset or false when the product intentionally has its own main window or should appear in the Dock. DynamicLake does not require this key and does not block extension loading when it is off.
Required Info.plist Key
The extension target's Info.plist must declare EXAppExtensionAttributes with an EXExtensionPointIdentifier that matches DynamicLake Pro's extension point exactly:
<key>EXAppExtensionAttributes</key>
<dict>
<key>EXExtensionPointIdentifier</key>
<string>com.aviorrok.DynamicLakePro.DynamicLakePro.extension</string>
</dict>
Copy this value rather than retyping it — a mismatch (even a small one, like an old point name from a prior DynamicLakeKit version) is silent: the extension still builds, runs, and can send activity state, but AppExtensionIdentity discovery in the host app will never resolve it as hostable, so its live-activity UI never appears.
If the extension includes DynamicLakeSettings, advertise that capability in the same extension target Info.plist. DynamicLake Pro uses this key to show the gear button in Extensions settings.
<key>DynamicLakeSupportsSettings</key>
<true/>
DynamicLake Pro hosts the settings scene in a fixed 420 x 520 point window. Keep the root settings layout designed for that size and add internal scrolling for longer forms.
Optional Metadata
Add metadata keys to the extension target's Info.plist to make the extension easier to understand in DynamicLake settings and Playground.
<key>DynamicLakeExtensionDisplayName</key>
<string>Clock</string>
<key>DynamicLakeExtensionDescription</key>
<string>Shows the current time and upcoming alarms in DynamicLake.</string>
<key>DynamicLakeExtensionDeveloperName</key>
<string>Example Studio</string>
<key>DynamicLakeExtensionWebsiteURL</key>
<string>https://example.com/clock</string>
<key>DynamicLakeExtensionSupportURL</key>
<string>mailto:[email protected]</string>
<key>DynamicLakeExtensionMinimumDynamicLakeKitVersion</key>
<string>1.0.0</string>
All metadata keys are optional. If DynamicLakeExtensionDisplayName is missing, DynamicLake falls back to CFBundleDisplayName, then CFBundleName, then the extension bundle name.
Use http, https, or mailto URLs for website and support links. Localize the visible strings through InfoPlist.strings when shipping to multiple languages.
If you change extension plist values after the extension was already built and registered once, macOS can keep stale PlugInKit metadata around. After updating the plist, fully quit both the extension and the host app, rebuild, and if the extension still isn't picked up, force re-registration with:
pluginkit -a "/path/to/YourApp.app"
Keep It Small
Put only extension UI, state models, and required assets in the extension target. The companion app opens, closes, and updates the activity with DynamicLakeActivityCenter.
For a complete build, install, and troubleshooting walkthrough, start with CreateYourFirstExtension.
Topics
Entry Point
DynamicLakeExtensionDynamicLakeExtensionSceneDynamicLakeLiveActivityDynamicLakeSneakPeekDynamicLakeExtraLiveActivityDynamicLakeSettingsDynamicLakeExtensionInfoKey