mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-30 02:28:26 +01:00
fix(ios): drop SwiftUICore autolink on pods so the app links via SwiftUI re-export
Build #9 proved `-weak_framework SwiftUICore` does NOT bypass the allowed-client check, and applying it to the tvOS app target regressed tvOS — reverted that plugin (withSwiftUICoreWeakLink). Confirmed root cause from build #8/#9 logs: both iOS jobs fail at the app *executable* link (`Ld … Streamyfin`), not at any pod. SwiftUI was split into SwiftUI + SwiftUICore on iOS 26; the SwiftUI pods emit a `-framework SwiftUICore` autolink directive that, under use_frameworks :static, is inherited by the app's static link, and the app isn't an allowed client of the private SwiftUICore.tbd. Fix: in the pod post_install, compile pods with `-Xfrontend -disable-autolink-framework -Xfrontend SwiftUICore` so they stop emitting that direct autolink. SwiftUICore symbols then resolve through SwiftUI's re-export (SwiftUI.tbd re-exports SwiftUICore). Scoped to phone (ENV['EXPO_TV'] != '1') to leave the green tvOS build untouched. Also harden scripts/ios/build-ios.ts: displayBuildError now surfaces the "Undefined symbols for architecture …" linker block, which the error:-only pattern filter was swallowing (so unsigned-build failures show the real symbol).
This commit is contained in:
1
app.json
1
app.json
@@ -133,7 +133,6 @@
|
||||
],
|
||||
"expo-web-browser",
|
||||
["./plugins/with-runtime-framework-headers.js"],
|
||||
["./plugins/withSwiftUICoreWeakLink.js"],
|
||||
["./plugins/withChangeNativeAndroidTextToWhite.js"],
|
||||
["./plugins/withAndroidAlertColors.js"],
|
||||
["./plugins/withAndroidManifest.js"],
|
||||
|
||||
@@ -25,6 +25,17 @@ function buildPatch() {
|
||||
" cfg.build_settings['HEADER_SEARCH_PATHS'] ||= '$(inherited)'",
|
||||
" cfg.build_settings['HEADER_SEARCH_PATHS'] << \" #{extra_hdrs.join(' ')}\"",
|
||||
" cfg.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES'] = 'YES'",
|
||||
" # iOS 26 / Xcode 26: SwiftUI was split into SwiftUI + SwiftUICore. The SwiftUI",
|
||||
" # pods (ExpoUI, glass-effect, glass-poster, …) emit a `-framework SwiftUICore`",
|
||||
" # autolink directive that, under use_frameworks :static, flows into the app",
|
||||
" # executable's link. The app isn't an allowed client of the private",
|
||||
" # SwiftUICore.tbd → `cannot link directly with 'SwiftUICore'`. Dropping that one",
|
||||
" # autolink at the Swift frontend lets the symbols resolve via SwiftUI's",
|
||||
" # re-export instead. Phone-only — tvOS links fine and must stay untouched.",
|
||||
" if ENV['EXPO_TV'] != '1'",
|
||||
" cfg.build_settings['OTHER_SWIFT_FLAGS'] ||= '$(inherited)'",
|
||||
" cfg.build_settings['OTHER_SWIFT_FLAGS'] << ' -Xfrontend -disable-autolink-framework -Xfrontend SwiftUICore'",
|
||||
" end",
|
||||
" end",
|
||||
" end",
|
||||
"",
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
const { withXcodeProject } = require("@expo/config-plugins");
|
||||
|
||||
// Tokens written verbatim as OTHER_LDFLAGS array entries.
|
||||
const LDFLAG_TOKENS = ['"-weak_framework"', '"SwiftUICore"'];
|
||||
|
||||
/**
|
||||
* Xcode 26 + `use_frameworks! :linkage => :static` makes the main app target
|
||||
* auto-link SwiftUICore directly (SwiftUI was split into SwiftUI + SwiftUICore on
|
||||
* recent SDKs, and the SwiftUI pods' object files carry a `-framework SwiftUICore`
|
||||
* autolink directive that flows into the app link). The linker then rejects it:
|
||||
* ld: cannot link directly with 'SwiftUICore' because product being built is
|
||||
* not an allowed client of it
|
||||
* Weakly linking SwiftUICore on the app target bypasses the allowed-client check;
|
||||
* the symbols still resolve at runtime via SwiftUI's re-export.
|
||||
*
|
||||
* Scoped to `com.apple.product-type.application` ONLY — it must not touch the
|
||||
* tvOS TopShelf app-extension (which legitimately links SwiftUI); applying the
|
||||
* flag there breaks that target.
|
||||
*/
|
||||
const withSwiftUICoreWeakLink = (config) =>
|
||||
withXcodeProject(config, (config) => {
|
||||
const project = config.modResults;
|
||||
const nativeTargets = project.pbxNativeTargetSection();
|
||||
const configLists = project.pbxXCConfigurationList();
|
||||
const buildConfigs = project.pbxXCBuildConfigurationSection();
|
||||
|
||||
// Collect build-configuration UUIDs that belong to application targets only.
|
||||
const appConfigIds = new Set();
|
||||
for (const key of Object.keys(nativeTargets)) {
|
||||
const target = nativeTargets[key];
|
||||
if (!target || typeof target !== "object" || !target.productType)
|
||||
continue;
|
||||
const productType = String(target.productType).replace(/"/g, "");
|
||||
if (productType !== "com.apple.product-type.application") continue;
|
||||
const list = configLists[target.buildConfigurationList];
|
||||
if (!list || !list.buildConfigurations) continue;
|
||||
for (const bc of list.buildConfigurations) appConfigIds.add(bc.value);
|
||||
}
|
||||
|
||||
for (const id of appConfigIds) {
|
||||
const entry = buildConfigs[id];
|
||||
if (!entry || typeof entry !== "object" || !entry.buildSettings) continue;
|
||||
const settings = entry.buildSettings;
|
||||
let flags = settings.OTHER_LDFLAGS;
|
||||
if (flags == null || flags === '""' || flags === "") {
|
||||
flags = ['"$(inherited)"'];
|
||||
} else if (typeof flags === "string") {
|
||||
flags = [flags];
|
||||
}
|
||||
const already = flags.some((f) => String(f).includes("SwiftUICore"));
|
||||
if (!already) {
|
||||
flags.push(...LDFLAG_TOKENS);
|
||||
settings.OTHER_LDFLAGS = flags;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
module.exports = withSwiftUICoreWeakLink;
|
||||
@@ -525,10 +525,23 @@ function displayBuildError(
|
||||
console.error(line);
|
||||
}
|
||||
console.error("--- End Build Errors ---\n");
|
||||
} else if (stdout.trim()) {
|
||||
}
|
||||
|
||||
// Linker failures ("Undefined symbols for architecture …", the SwiftUICore
|
||||
// autolink rejection, "ld: …") don't carry an "error:" token, so the pattern
|
||||
// filter above drops the symbol name and "referenced from" context that
|
||||
// actually pinpoints the culprit. Surface that block explicitly.
|
||||
const stdoutLines = stdout.split("\n");
|
||||
const undefIdx = stdoutLines.findIndex((line: string) =>
|
||||
line.includes("Undefined symbols"),
|
||||
);
|
||||
if (undefIdx >= 0) {
|
||||
console.error("\n--- Linker error detail ---");
|
||||
console.error(stdoutLines.slice(undefIdx, undefIdx + 40).join("\n"));
|
||||
console.error("--- End linker error detail ---\n");
|
||||
} else if (errorLines.length === 0 && stdout.trim()) {
|
||||
// No specific error patterns found, show last N lines of stdout
|
||||
const lines = stdout.split("\n");
|
||||
const lastLines = lines.slice(-ERROR_OUTPUT_TAIL_LINES).join("\n");
|
||||
const lastLines = stdoutLines.slice(-ERROR_OUTPUT_TAIL_LINES).join("\n");
|
||||
console.error(
|
||||
`\n--- Last ${ERROR_OUTPUT_TAIL_LINES} lines of build output ---`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user