Add the package

Add DroppyKit to your droplet with Swift Package Manager.

A droplet is a Swift package that builds a dynamic library and links DroppyKit. Droppy loads the built bundle into its own process, so the library type matters: a static library has nothing for the loader to open.

Requirements

macOS14.0 or later
Swift6.2 or later (Xcode 26)
Droppy15.3.0 or later

In Package.swift

// swift-tools-version: 6.2
import PackageDescription

let package = Package(
    name: "WorldClock",
    platforms: [.macOS(.v14)],
    products: [
        .library(name: "WorldClock", type: .dynamic, targets: ["WorldClock"])
    ],
    dependencies: [
        .package(url: "https://gitlab.com/droppyformac1/droppykit.git", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "WorldClock",
            dependencies: [.product(name: "DroppyKit", package: "droppykit")]
        ),
        .executableTarget(
            name: "WorldClockHarness",
            dependencies: [
                "WorldClock",
                .product(name: "DroppyKitHarness", package: "droppykit")
            ]
        )
    ]
)

The second target is the development harness. It is not shipped in your droplet — see The harness.

In Xcode

File ▸ Add Package Dependencies, then paste https://gitlab.com/droppyformac1/droppykit.git. Add DroppyKit to your droplet target and DroppyKitHarness to the harness target.

Do not build with a bare swift build

Important. Always build the shipping bundle with Scripts/build-droplet.sh.

A shipped droplet must contain its own code and not a copy of DroppyKit. Droppy already has DroppyKit as a resilient framework; a droplet carrying a second copy gives every shared type two metadata records, and the cast the loader makes between Droppy's Droplet and yours fails with no useful diagnostic. A plain swift build produces exactly that second copy, because SwiftPM folds the dependency in statically.

droppykit build does the link itself: it builds DroppyKit resiliently, wraps it in a framework whose install name is the one Droppy ships, and links your target's object file against that. The bundle then carries an undefined reference Droppy resolves at load.

It also compiles with -enable-library-evolution. Without it a droplet emits direct-access imports the resilient framework does not export, and the bundle passes every structural check, loads in the harness, and dies inside Droppy at dyld with Symbol not found.

Scripts/validate-droplet.sh refuses a bundle carrying either wrong shape, so a mistake here is caught before submission rather than after install.

# 0 in a correct bundle, non-zero in a broken one.
nm -u WorldClock.droplet/Contents/MacOS/WorldClock | grep -c '9DroppyKit.*vau'

# Must name the framework, and must not name libDroppyKit.
otool -L WorldClock.droplet/Contents/MacOS/WorldClock | grep DroppyKit

See also