Summary
brownfield package:ios --add-spm-package generates a local Swift package whose binary targets are fixed: the app framework, Hermes, ReactBrownfield, Brownie/BrownfieldNavigation, and the Expo support list in emitExpoSupportXcframeworks.ts. There is no way for a project to declare an additional XCFramework that the host must import directly.
Problem
Some Swift modules cannot be fused into the packaged RN framework: the host app has to link and import them itself (the same reason ExpoModulesCore is shipped as a separate XCFramework instead of inside BrownfieldLib). Today the host ends up with two integration paths: the generated SPM package for RN, plus a hand-added local Swift package or a manually dragged XCFramework for the extra module. --add-spm-package promises “one Add Local”; that promise breaks as soon as a project has one such module.
Dropping an extra *.xcframework into the package dir is not a workaround: createLocalSpmPackage.resolveAppFrameworkName treats any non-reserved XCFramework as an app-framework candidate, so without --scheme it either fails with “multiple candidates” or picks the wrong one.
Proposal (opt-in, additive)
// brownfield.config.js
module.exports = {
ios: {
extraSpmXcframeworks: [
{ name: 'MyHostModule', path: './artifacts/MyHostModule.xcframework' },
],
},
};
- Default
[] → byte-identical to current behavior.
- Only consumed when
--add-spm-package is set.
- Each
name is added to RESERVED_FRAMEWORK_NAMES for that run, so it never competes with app-framework resolution.
- Copied into
spm-artifacts/ with normalizeCopiedXcframework, emitted as one more .binaryTarget and listed in the library product.
- Not linked into
BrownfieldLib; no Podfile or Xcode project changes.
- Missing path → hard error naming the entry. No silent skip.
- Schema:
BrownfieldIosConfig.extraSpmXcframeworks (array of { name, path }), same shape family as extraParams.
Why here and not in the consumer
The host has no Node toolchain; the point of package:ios is that the RN side owns packaging. A per-project list keeps that contract instead of pushing a second package onto every host.
Context
Hit this with a C++-free Swift API that must live beside (not inside) the RN framework so the host can provide() native implementations before startReactNative. Any analytics / design-system / internal SDK the host must import has the same shape.
Happy to send a PR against createLocalSpmPackage.ts + schema.json if the direction is acceptable.
Summary
brownfield package:ios --add-spm-packagegenerates a local Swift package whose binary targets are fixed: the app framework, Hermes,ReactBrownfield,Brownie/BrownfieldNavigation, and the Expo support list inemitExpoSupportXcframeworks.ts. There is no way for a project to declare an additional XCFramework that the host mustimportdirectly.Problem
Some Swift modules cannot be fused into the packaged RN framework: the host app has to link and
importthem itself (the same reasonExpoModulesCoreis shipped as a separate XCFramework instead of insideBrownfieldLib). Today the host ends up with two integration paths: the generated SPM package for RN, plus a hand-added local Swift package or a manually dragged XCFramework for the extra module.--add-spm-packagepromises “one Add Local”; that promise breaks as soon as a project has one such module.Dropping an extra
*.xcframeworkinto the package dir is not a workaround:createLocalSpmPackage.resolveAppFrameworkNametreats any non-reserved XCFramework as an app-framework candidate, so without--schemeit either fails with “multiple candidates” or picks the wrong one.Proposal (opt-in, additive)
[]→ byte-identical to current behavior.--add-spm-packageis set.nameis added toRESERVED_FRAMEWORK_NAMESfor that run, so it never competes with app-framework resolution.spm-artifacts/withnormalizeCopiedXcframework, emitted as one more.binaryTargetand listed in the library product.BrownfieldLib; no Podfile or Xcode project changes.BrownfieldIosConfig.extraSpmXcframeworks(array of{ name, path }), same shape family asextraParams.Why here and not in the consumer
The host has no Node toolchain; the point of
package:iosis that the RN side owns packaging. A per-project list keeps that contract instead of pushing a second package onto every host.Context
Hit this with a C++-free Swift API that must live beside (not inside) the RN framework so the host can
provide()native implementations beforestartReactNative. Any analytics / design-system / internal SDK the host mustimporthas the same shape.Happy to send a PR against
createLocalSpmPackage.ts+schema.jsonif the direction is acceptable.