---
title: "DropletBundleInfo, DroppyKit"
description: "Parsed metadata of one .droplet bundle, read from its Info.plist without loading any code."
canonical: "https://getdroppy.app/docs/droppykit/api/droplet-bundle-info"
last-updated: "2026-09-09"
---

# DropletBundleInfo

Parsed metadata of one .droplet bundle, read from its Info.plist without loading any code.

`struct DropletBundleInfo`

## Initializers

### init(bundleURL:)

`init(bundleURL: URL) throws`

Reads metadata from a bundle directory on disk. Opens only `Contents/Info.plist`; never touches the executable.

### init(infoDictionary:)

`init(infoDictionary: [String : Any]) throws`

Parses metadata from an Info.plist dictionary.

Every gate here runs on metadata alone, before any of the bundle's code is loaded. Since DroppyKit 0.3.0 that includes the three rules [`DropletInfoPlistKey`](https://getdroppy.app/docs/droppykit/api/droplet-info-plist-key) had only been declaring: the id's charset, the package type, and the bundle identifier.

## Properties

### bundleIdentifier

`let bundleIdentifier: String`

Bundle identifier (`CFBundleIdentifier`). Always `app.getdroppy.droplet.<dropletID>`; the parser refuses anything else.

### dropletID

`let dropletID: DropletID`

The droplet's stable identifier.

### kitABI

`let kitABI: Int`

DroppyKit ABI generation the bundle was built against.

### kitMinAPI

`let kitMinAPI: SemanticVersion`

Minimum DroppyKit API version the bundle requires.

### minAppVersion

`let minAppVersion: String?`

Minimum host app version, when declared.

### minMacOS

`let minMacOS: String?`

The oldest macOS this droplet runs on, when it needs more than Droppy's own minimum. `nil` for every bundle built before 1.12.0.

### name

`let name: String`

User-visible name (`CFBundleName`, falling back to the id).

### permissions

`let permissions: [DropletPermission]`

Every capability string the manifest declared, in declaration order.

Includes strings this SDK build does not recognise: [`DropletPermission`](https://getdroppy.app/docs/droppykit/api/droplet-permission) is open, so an unknown string round-trips as itself rather than being dropped. Hosts treat anything outside [`known`](https://getdroppy.app/docs/droppykit/api/droplet-permission#known) as not granted, which is how a bundle built against a newer SDK loads with less capability instead of refusing to load.

The 0.2.x split into `permissions` plus a separate `unknownPermissions` array is gone; recover it with `permissions.filter { !$0.isKnown }` when you need it.

### principalClassName

`let principalClassName: String?`

Principal class name (`NSPrincipalClass`), when present.

### replacesBuiltIn

`let replacesBuiltIn: String?`

Built-in extension this bundle shadows during dual-mode, when any.

### runsOnThisSystem

`var runsOnThisSystem: Bool { get }`

Whether this Mac's macOS is at least `minMacOS`.

### urlAliases

`let urlAliases: [String]`

Deep-link aliases.

### version

`let version: String`

Bundle version (`CFBundleShortVersionString`).

## Type methods

### isSystemAtLeast(_:)

`static func isSystemAtLeast(_ version: String) -> Bool`

Whether the running macOS is at least `version`, compared numerically.
