Environment: Skip 1.9.7, Xcode 26.3, Swift 6.3.0 (swift-6.3-RELEASE), macOS 26.5.2 (arm64), iOS 26.3 simulator. Skip Fuse.
Summary
A stock skip create app (Skip Fuse) installs and launches on the iOS simulator but hangs
on a blank launch screen and never reaches main. No crash, no logs, no error.
The cause is dynamic-library resolution order. The linked app binary carries an rpath
pointing into the build tree:
$ otool -l CarpoolApp.app/CarpoolApp | grep -A2 LC_RPATH
path /usr/lib/swift
path <PROJECT>/.build/Darwin/DerivedData/Build/Products/Debug-iphonesimulator/PackageFrameworks
path @executable_path/Frameworks
The PackageFrameworks entry is ordered ahead of @executable_path/Frameworks, so dyld
loads CarpoolApp.framework from the build tree rather than from the app's own bundle — even
though the framework is correctly embedded in the bundle by the Embed Frameworks phase.
DYLD_PRINT_SEARCHING=1 confirms it:
find path "@rpath/CarpoolApp.framework/CarpoolApp"
...
found: dylib-from-disk: "<PROJECT>/.build/.../PackageFrameworks/CarpoolApp.framework/CarpoolApp"
...and then output stops. A sample of the hung process shows 100% of samples parked in
dyld4::JustInTimeLoader::loadDependents → SyscallDelegate::mapFileReadOnly →
dyld3::open → open_with_subsystem. The process is blocked in a file open, not spinning.
Why this matters even though it usually "works"
Loading the framework from the build tree normally succeeds, so this is invisible most of the
time. But it makes a launched app depend on its build directory remaining readable at runtime,
which is not a property an installed app should have. When that open blocks for any reason, the
app hangs before main with no diagnostic whatsoever — the hardest possible failure to debug.
In our case the trigger is location: the project lives under ~/Documents, a macOS
privacy-protected directory. We confirmed this by experiment:
| Project location |
Injected rpath present |
Result |
~/Documents/Development/... |
yes |
hangs at blank launch screen |
~/Development/... (same scaffold, unmodified) |
yes |
launches normally |
~/Documents/Development/..., rpath stripped post-link |
no |
launches normally |
We did not prove the macOS mechanism behind the blocking open (TCC is the strong suspect;
tccd logged nothing and TCC.db was unreadable). Ruled out: iCloud Drive sync (not enabled),
endpoint-security extensions (none installed), code-signature differences between the two
framework copies (both ad-hoc, identical), architecture mismatch, and stale DerivedData.
Reproduction
skip create a Fuse app in a project directory under ~/Documents.
skip app launch.
- iOS installs and launches to a blank white screen and stays there. Android is unaffected.
otool -l the app binary and observe the PackageFrameworks rpath ahead of
@executable_path/Frameworks.
Where the rpath comes from
Not from the Skip scaffold's own settings. Darwin/CarpoolApp.xcconfig and the pbxproj set
only:
LD_RUNPATH_SEARCH_PATHS = @executable_path/Frameworks
and xcodebuild -showBuildSettings confirms that is the effective value. Xcode's SwiftPM
integration injects -rpath $BUILT_PRODUCTS_DIR/PackageFrameworks at link time for the
type: .dynamic library product declared in Package.swift, outside of any visible build
setting.
So this may be Xcode behaviour rather than a Skip bug per se — but the scaffold's
.library(name:, type: .dynamic, ...) product is what triggers it, and the resulting failure
mode lands squarely on Skip users. Worth either documenting or defending against in the
scaffold.
Workaround we're using
A post-link build phase in the app target, after Embed Frameworks and before Xcode's CodeSign
step:
RPATH="${BUILT_PRODUCTS_DIR}/PackageFrameworks"
APP="${TARGET_BUILD_DIR}/${EXECUTABLE_FOLDER_PATH}"
for BIN in "${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}" "${APP}/${PRODUCT_NAME}.debug.dylib"; do
[ -f "${BIN}" ] || continue
if otool -l "${BIN}" | grep -q "path ${RPATH} (offset"; then
install_name_tool -delete_rpath "${RPATH}" "${BIN}"
fi
done
With the rpath removed the app resolves only its embedded framework and launches correctly,
with the build tree left entirely in place.
Question for maintainers
Is the PackageFrameworks rpath needed at runtime for Skip Fuse apps, or is embedding the
frameworks sufficient? If the latter, stripping it in the scaffold would make Skip apps
independent of their build directory and remove a very hard-to-diagnose class of launch hang.
Environment: Skip 1.9.7, Xcode 26.3, Swift 6.3.0 (swift-6.3-RELEASE), macOS 26.5.2 (arm64), iOS 26.3 simulator. Skip Fuse.
Summary
A stock
skip createapp (Skip Fuse) installs and launches on the iOS simulator but hangson a blank launch screen and never reaches
main. No crash, no logs, no error.The cause is dynamic-library resolution order. The linked app binary carries an rpath
pointing into the build tree:
The
PackageFrameworksentry is ordered ahead of@executable_path/Frameworks, so dyldloads
CarpoolApp.frameworkfrom the build tree rather than from the app's own bundle — eventhough the framework is correctly embedded in the bundle by the Embed Frameworks phase.
DYLD_PRINT_SEARCHING=1confirms it:...and then output stops. A
sampleof the hung process shows 100% of samples parked indyld4::JustInTimeLoader::loadDependents→SyscallDelegate::mapFileReadOnly→dyld3::open→open_with_subsystem. The process is blocked in a file open, not spinning.Why this matters even though it usually "works"
Loading the framework from the build tree normally succeeds, so this is invisible most of the
time. But it makes a launched app depend on its build directory remaining readable at runtime,
which is not a property an installed app should have. When that open blocks for any reason, the
app hangs before
mainwith no diagnostic whatsoever — the hardest possible failure to debug.In our case the trigger is location: the project lives under
~/Documents, a macOSprivacy-protected directory. We confirmed this by experiment:
~/Documents/Development/...~/Development/...(same scaffold, unmodified)~/Documents/Development/..., rpath stripped post-linkWe did not prove the macOS mechanism behind the blocking open (TCC is the strong suspect;
tccdlogged nothing andTCC.dbwas unreadable). Ruled out: iCloud Drive sync (not enabled),endpoint-security extensions (none installed), code-signature differences between the two
framework copies (both ad-hoc, identical), architecture mismatch, and stale DerivedData.
Reproduction
skip createa Fuse app in a project directory under~/Documents.skip app launch.otool -lthe app binary and observe thePackageFrameworksrpath ahead of@executable_path/Frameworks.Where the rpath comes from
Not from the Skip scaffold's own settings.
Darwin/CarpoolApp.xcconfigand the pbxproj setonly:
and
xcodebuild -showBuildSettingsconfirms that is the effective value. Xcode's SwiftPMintegration injects
-rpath $BUILT_PRODUCTS_DIR/PackageFrameworksat link time for thetype: .dynamiclibrary product declared inPackage.swift, outside of any visible buildsetting.
So this may be Xcode behaviour rather than a Skip bug per se — but the scaffold's
.library(name:, type: .dynamic, ...)product is what triggers it, and the resulting failuremode lands squarely on Skip users. Worth either documenting or defending against in the
scaffold.
Workaround we're using
A post-link build phase in the app target, after Embed Frameworks and before Xcode's CodeSign
step:
With the rpath removed the app resolves only its embedded framework and launches correctly,
with the build tree left entirely in place.
Question for maintainers
Is the
PackageFrameworksrpath needed at runtime for Skip Fuse apps, or is embedding theframeworks sufficient? If the latter, stripping it in the scaffold would make Skip apps
independent of their build directory and remove a very hard-to-diagnose class of launch hang.