Skip to main content

Build dependencies (iOS)

Requires Bugsee iOS SDK 6.1.5 or newer

Dependency capture ships with the BugseeAgent post-action in 6.1.5+ and is on by default. The same blob feeds Bugsee's vulnerability scan, so capturing dependencies is also the gate for OSV-backed CVE reporting.

What gets captured

iOS projects resolve dependencies through one (or more) of three package managers. The agent reads the resolved lockfiles at the project root — it never re-resolves, so it captures exactly what your build linked. It parses each manager that's present, in order (CocoaPods → SPM → Carthage):

ManagerFile parsedNotes
CocoaPodsPodfile.lockThe DEPENDENCIES: section lists the direct pods your Podfile declared; the full lock lists every resolved pod. Subspecs (Pod/Sub) collapse to their root pod — one entry per pod.
Swift Package ManagerPackage.resolvedHandles v1 (object.pins) and v2 (pins) layouts. Each entry carries the package's Git URL, which the vulnerability scanner needs. Found at the project root, or under *.xcodeproj / *.xcworkspace xcshareddata/swiftpm/.
CarthageCartfile.resolvedThe full resolved set. github / git entries carry a Git URL and are scannable; binary entries are listed but can't be scanned (no Git URL to map to an advisory database).

Each entry the agent emits is a DependencyEntry:

FieldMeaning
idStable identifier for the package
name / versionPackage name and resolved version
directtrue if the entry is one your project declares directly; false for transitive entries (CocoaPods is the only iOS manager that distinguishes the two)
type"library" (an external package), "project", or "file"
ecosystem"cocoapods" or "spm" — required so the vulnerability scanner queries OSV against the correct package universe. binary Carthage entries have no ecosystem and are not scanned
urlThe package's Git URL (SPM and Git-based Carthage entries) — OSV's SwiftURL query keys off this

After parsing every manager, the agent de-duplicates (on a collision it keeps the entry carrying a url, since dropping it would silently exclude the package from the vuln scan) and sorts direct entries first. The list is bounded by a max-count cap; entries past the cap are dropped and the upload is flagged truncated: true.

What gets uploaded

The inline dependencies_summary is a compact aggregate that always ships with the build POST:

{
"total": 94,
"direct": 12,
"transitive": 82,
"by_type": {
"library": 94,
"project": 0,
"file": 0
},
"truncated": false,
"collected_at": "2026-06-10T14:30:00Z",
"collection_config": {
"scope": "resolved",
"include_selected_reason": false,
"max_count": 5000
}
}

The detail blob — gzipped JSON — is shipped to a separate presigned PUT URL (dependencies_upload_endpoint) when dependency capture is enabled:

{
"schema_version": 1,
"truncated": false,
"collection_config": { "scope": "resolved", "include_selected_reason": false, "max_count": 5000 },
"dependencies": [
{
"id": "github.com/Alamofire/Alamofire",
"name": "Alamofire",
"version": "5.9.1",
"direct": true,
"type": "library",
"ecosystem": "spm",
"url": "https://github.com/Alamofire/Alamofire.git"
}
]
}

The collection_config block is captured on every blob so the worker's diff logic can refuse to compare apples-to-oranges builds.

Configuration

Dependency capture is on by default. The only knob is a single environment variable, set on the scheme's Archive action or exported in CI:

# Opt out — also disables the vulnerability scan, which derives from this blob:
export BUGSEE_DEPENDENCIES_ENABLED=0

This mirrors the Android Gradle plugin's bugsee.buildInfo.dependencies.enabled DSL flag. Unlike Android there are no scope / maxCount knobs to set — the graph comes from the resolved lockfiles, not a Gradle configuration. See Where to set the env vars for placement options.

What you see in the dashboard

The build detail page's Dependencies tab is rendered when dependencies_status == 'ready'. Three parts:

Stat chip strip

Across the top: Total, Direct, Transitive, and counts by type. When truncated is true, a warning chip appears so the cap doesn't silently hide the tail. While the worker job is still in flight, a status pill replaces the chips — uploading, processing, or failed.

Vulnerability strip

The Dependencies tab is also where Bugsee's vulnerability findings show up — see the dedicated Vulnerabilities page for the details. The strip renders between the chip row and the table whenever a scan summary is populated.

Per-dependency table

A scrollable, sortable, filterable table listing every captured entry. Columns include id, direct/transitive, type, ecosystem, and resolved version. Because the same wire schema backs both platforms, this is the same table as on Android.

Worker pipeline

The uploaded dependencies blob is routed to the dependency-processing job on the Bugsee worker: bootstrap (dependencies_status='processing') → download with caps → validate schema_version → re-upload the gzipped bytes to the per-build S3 prefix → diff vs the previous build (gated on identical collection_config) → final dependencies_status='ready'.

When dependencies_status flips to 'ready', the worker also enqueues the vulnerability scan for the same build — vulnerabilities are derived from the dependencies list, so the scan can't run before the dependencies blob lands.

Found an issue, typo, or wrong statement on this page? Report it now →