Swift 6.4 LLDB debugging changes what must survive the trip from CI to a developer’s Mac. Released on September 15, 2026, Swift 6.4 replaces ambiguous module-name lookups with precise module-dependency tracking, and binary Swift modules are no longer embedded in Darwin dSYM bundles under the new arrangement. A smaller dSYM therefore says little about whether a downloaded iOS build supports LLDB expression evaluation. Preserve the compiler-matched module dependency closure alongside the executable and symbols, then test it after the original build directory is inaccessible.
The Swift project says most Xcode and SwiftPM users benefit automatically without project modifications. That is the strongest reason against introducing manual compiler configuration into an ordinary local build. The operational gap appears when CI artifacts cross machines, or when custom orchestration bypasses the Swift driver: automatic dependency tracking cannot recover artifacts that an export job discarded.
Use Xcode 27 as the baseline for this walkthrough. Apple’s SDK and system-requirements table pairs it with the Swift 6.4 compiler and iOS 27 SDK, requires macOS Tahoe 26.6 or later, and lists iOS deployment targets from 15 through 27. Record the actual installed build identifiers; the marketing version alone does not identify the complete toolchain.
Why Variable Inspection Can Pass While Expressions Fail
Apple’s WWDC22 debugging session describes two relevant LLDB execution paths. Variable inspection uses debug information and reflection metadata. Expression evaluation invokes an embedded compiler and imports modules. A readable stored field validates only part of the debugger’s environment; a computed getter exercises the machinery that must understand and execute Swift code.
Create a small iOS fixture with a separate Swift module named DebugModelKit. Its model should expose one stored property and one deterministic computed property. This example deliberately avoids network requests and other deliberate side effects in the getter:
// DebugModelKit
public struct DebugModel {
public let storedValue: Int
public init(storedValue: Int) {
self.storedValue = storedValue
}
public var computedValue: Int {
storedValue * 2
}
}
Call a function like this from the application, and place a breakpoint on the print line, after initialization. Build the fixture with debug information and without optimization so that variable availability does not obscure the module-restoration test.
import DebugModelKit
@inline(never)
func inspectModel() {
let model = DebugModel(storedValue: 21)
print(model.computedValue) // Break here.
}
The acceptance test must evaluate model.computedValue through LLDB. Observing the application print the value does not test LLDB expression evaluation. Apple’s session also demonstrates a missing-module problem involving a static archive. That historical example establishes the failure mechanism; it does not establish that all current static libraries have a debugging defect.
Inspect Swift 6.4 LLDB Debugging References in CI
Capture the selected toolchain before building. These commands collect identifiers from the active developer environment; verify that this is also the environment used by the CI build job.
xcodebuild -version
xcrun swiftc --version
xcrun --sdk iphoneos --show-sdk-version
xcrun lldb --version
Retain the complete Xcode build identifier, compiler version, SDK, target architecture, target triple, and LLDB version. Capture the debugger version on the receiving Mac too. Swift compiler version and Swift language mode are separate settings: a Swift 6.4 compiler identity does not establish the language mode used by the project.
Next, retain the generated frontend commands and inspect the object-compilation jobs. Swift 6.3 began recording explicit Swift dependencies. The current Swift driver implementation supplies module information to appropriate compile jobs when explicit dependency support is available. Its non-caching branch passes a module output path; its caching branch can pass a cache key, with a separate self-key case. An artifact exporter that assumes every module reference is a filesystem path will mishandle some cached builds.
The option definitions separate -emit-module-path, which selects an emitted module artifact, from -debug-module-path, which communicates the module reference used by debug information. For each relevant compile job, identify the emitted module, the debug module reference, and whether that reference depends on a cache. Confirm explicit-module compilation from the actual commands, since a project setting alone does not verify what ran.
For custom build systems, inspect the installed toolchain and pin the Swift repository revision corresponding to the release before changing invocation generation. The driver and option-definition pages track the live main branch. Verify the release-specific behavior before using them to generate commands for Xcode 27. Ordinary Xcode projects should not receive speculative frontend flags simply because a transferred build fails to import a module.
Separate Symbolication and Interactive-Debugging Packages
Define two deliverables with different acceptance criteria. The symbolication package contains the exact executable and matching dSYMs. The interactive-debugging package adds the required module dependency closure, build configuration, matching source revision, and restoration instructions. This artifact contract is an editorial recommendation derived from the documented mechanisms. Apple has not prescribed this archive format.
The dependency closure extends beyond the module that defines the visible model. Include the application and dependency module artifacts required by the selected build configuration, and document which SDK-provided dependencies the destination toolchain supplies. Retaining only the top-level module can leave its imports unresolved. Binary Swift modules remain tied to their producing compiler, so rebuilding a dependency with an arbitrary local compiler is not an equivalent restoration strategy.
A manifest can make the contract inspectable. The schema below illustrates the contract using placeholders. Populate it with captured build values, and record each actual module reference according to the caching mode found in the compile commands.
{
"source_revision": "<exact revision>",
"xcode_version": "27",
"xcode_build": "<complete build identifier>",
"swift_compiler": "<complete swiftc --version output>",
"swift_language_mode": "<captured build setting>",
"sdk": "<SDK identity and version>",
"architecture": "<built architecture>",
"target_triple": "<captured frontend target>",
"lldb_version": "<complete LLDB version>",
"configuration": "<configuration and optimization settings>",
"recorded_build_root": "/BUILDROOT",
"module_reference_mode": "<path or cache-backed>",
"module_references": [
{
"module": "DebugModelKit",
"debug_reference": "<captured module reference>",
"restoration": "<artifact location or cache retrieval procedure>"
}
],
"frontend_commands": "frontend-commands.txt",
"uuid_inventory": "uuids.txt"
}
For cache-backed builds, retaining a key without retaining retrievable content leaves the package incomplete. Document the cache service or exported content, access requirements, and retention period. Keep that content available for as long as the associated build is promised to support interactive debugging. If the restoration process depends on credentials unavailable to developers, test that failure explicitly.

Inspecting a stored value can succeed even when expression evaluation lacks a required module.
Keep these private debugging artifacts under controlled access. Compiler modules belong in the developer-facing debugging package, not in the application distributed to users. Associate both packages with the same build identity and source revision so that CI retention policies cannot silently separate an executable from its modules.
Restore Swift 6.4 LLDB Debugging on Another Mac
Use a separate Mac or a fresh checkout with the original build directory unavailable. A local test beside CI’s surviving output can pass through accidental filesystem access. Restore the executable, dSYMs, module artifacts, and any required cache content according to the manifest, then check the receiver’s toolchain against the recorded producer configuration.
Compare executable and dSYM UUIDs before investigating Swift imports. Apple’s symbolication guidance requires matching UUIDs; matching filenames are insufficient. With illustrative artifact names, inspect both sides using:
dwarfdump --uuid artifacts/App.app/App
dwarfdump --uuid artifacts/App.app.dSYM
Compare the UUID for each architecture you intend to debug. Stop if the executable and symbols do not match. A module search adjustment cannot repair symbols belonging to another build. Keep the UUID output in the package inventory so that artifact selection remains auditable after download.
Restore the exact source revision and establish source-path mapping. If the debug information records a canonical root of /BUILDROOT, and the receiving checkout is at /Users/developer/work/app, the corresponding LLDB command is:
settings set target.source-map /BUILDROOT /Users/developer/work/app
This example assumes the build actually recorded /BUILDROOT; adding the setting afterward does not rewrite other recorded paths. LLVM documents ordered prefix substitution, with the first matching entry winning. Inspect mapping order when several prefixes overlap, and verify that the breakpoint displays the expected source revision.
Source mapping locates source files. It does not prove that module paths or cache references have been restored. Treat module retrieval as a separate restoration step governed by the captured references. A source window can display the correct file while the expression compiler still lacks a required import.
Test Symbolication and Expression Evaluation Separately
At the fixture breakpoint, run the following checks and record their outcomes independently:
(lldb) frame variable model
(lldb) expression -- model.computedValue
The first command checks variable inspection. The second requests computed-property evaluation through LLDB’s expression machinery; the fixture’s expected value is 42. An inspection pass followed by an import-related expression failure should direct investigation toward the module and compiler environment. Record the expression failure separately so the inspection pass cannot mask it.
After an import-related failure, run swift-healthcheck and retain its configuration diagnostics alongside the failed expression. Distinguish three diagnostic tracks: source-view failures require checking source revision and path mapping; symbol mismatches require selecting the correct executable and dSYMs; expression-import failures require inspecting module references, dependency availability, and toolchain compatibility. Clearing caches indiscriminately can remove evidence without correcting the artifact contract.
Add negative tests to the transfer workflow. Deliberately omit an application module, then repeat both commands in a fresh session. Separately, restore an incorrect module path and repeat the checks. Capture which operation fails and the resulting diagnostics; restore the correct artifact closure and retest. These are proposed tests, with no execution results claimed here. Their purpose is to detect whether the acceptance check accidentally succeeds using artifacts outside the package.
Run the baseline with a debuggable application configuration. If attachment is blocked by the installed build’s signing or debugging permissions, resolve that before interpreting module tests. Otherwise, an attachment failure can be incorrectly reported as a Swift expression-evaluation regression.
Migrate Linker Integration and Measure the Full Package
The Swift project’s September module-tracking post permits removing Darwin -add_ast_path handling for Swift 6.4 explicit-module builds. Custom systems that bypass the driver must provide the module reference themselves. Gate that migration on the actual toolchain and compilation mode, and preserve the complete debugging configuration while changing linker integration. Deleting a flag without validating the replacement dependency references leaves the transfer contract untested.
The release announcement reports smaller debugging artifacts and improved reliability qualitatively. It supplies no numerical latency benchmark or dSYM-size percentage for this change. Measure dSYM bytes and total interactive-debugging package bytes separately: reducing embedded content can shrink one file while increasing the separately retained package. Cache storage and retention also belong in the storage assessment when the package depends on them.
For runtime measurements, record first computed-expression latency in an isolated session, repeated-expression latency, and successful evaluations across fresh sessions. Publish the fixture, hardware, compiler build, dependency count, and cache state with the results. A warm session and a restored cold session exercise different conditions; combining them hides the cost developers encounter after downloading a build.

Retain the complete debugging package, restore it on a fresh Mac, and test symbols and expressions separately.
Use the lighter symbolication package when the task is crash analysis. Retain the interactive-debugging package when developers need to evaluate Swift expressions against that exact CI build. Before declaring the latter transferable, make the original build directory unavailable and require expression -- model.computedValue to return 42 in a fresh session, with module retrieval accounted for in the manifest.