Skip to content

Commit c13a030

Browse files
chrfalchclaude
andcommitted
fix(iOS): opt spm_dependency pod targets out of Swift explicit modules
A pod target consuming an SPM binary target via spm_dependency sees the binary target's module.modulemap twice: Xcode processes the xcframework into both the platform-wide products dir and the pod's TARGET_BUILD_DIR, and the existing SWIFT_INCLUDE_PATHS workaround makes the former visible. Implicit modules tolerate the duplicate definition; the explicit-modules dependency scanner (the default starting with Xcode 26) fails with "redefinition of module". Before the VFS-overlay removal these targets carried -ivfsoverlay, which disqualified them from explicit modules and masked the problem; the overlay's removal made them eligible and the nightly-tests react-native-enriched-markdown (RaTeX) job went red. Set SWIFT_ENABLE_EXPLICIT_MODULES=NO on exactly the targets spm_dependency touches, restoring the previous compile mode for the CocoaPods+SPM interop path only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b33de75 commit c13a030

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

packages/react-native/scripts/cocoapods/__tests__/spm-test.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,37 @@ def test_injected_uuids_are_unique_across_all_objects
9797
uuids = reopened.objects.map(&:uuid)
9898
assert_equal(uuids.length, uuids.uniq.length, "all object UUIDs must be unique")
9999
end
100+
101+
# A pod target consuming an SPM binary xcframework sees the product's
102+
# module.modulemap twice (once in the global products dir — reachable via the
103+
# SWIFT_INCLUDE_PATHS workaround — and once in its own TARGET_BUILD_DIR).
104+
# Implicit modules tolerate the duplicate; the explicit-modules dependency
105+
# scanner (Xcode 26 default) hard-errors with "redefinition of module".
106+
# spm_dependency targets must therefore opt out of explicit modules.
107+
def test_spm_targets_opt_out_of_swift_explicit_modules
108+
project = build_project(3)
109+
inject_spm(project)
110+
target = project.targets.find { |t| t.name == POD_NAME }
111+
target.build_configurations.each do |config|
112+
assert_equal(
113+
"NO",
114+
target.build_settings(config.name)["SWIFT_ENABLE_EXPLICIT_MODULES"],
115+
"SPM-consuming pod targets must disable Swift explicit modules " \
116+
"(#{config.name}) — the duplicate binary-target modulemap trips the " \
117+
"explicit-modules dependency scanner"
118+
)
119+
end
120+
end
121+
122+
def test_non_spm_targets_keep_explicit_modules_untouched
123+
project = build_project(3)
124+
inject_spm(project)
125+
other = project.targets.find { |t| t.name == "Pod0" }
126+
other.build_configurations.each do |config|
127+
assert_nil(
128+
other.build_settings(config.name)["SWIFT_ENABLE_EXPLICIT_MODULES"],
129+
"targets without SPM dependencies must not be touched (#{config.name})"
130+
)
131+
end
132+
end
100133
end

packages/react-native/scripts/cocoapods/spm.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ def apply_on_post_install(installer)
3737
unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
3838
target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
3939
end
40+
# The search path above makes the platform-wide products dir visible
41+
# to this target, so an SPM binary target's module.modulemap is seen
42+
# twice: once there and once in the target's own build dir (Xcode
43+
# processes the xcframework into both). Implicit modules tolerate the
44+
# duplicate, but the explicit-modules dependency scanner (the default
45+
# starting with Xcode 26) fails with "redefinition of module". Opt
46+
# SPM-consuming pod targets out of explicit modules.
47+
target.build_settings(config.name)['SWIFT_ENABLE_EXPLICIT_MODULES'] = 'NO'
4048
end
4149
end
4250
end

0 commit comments

Comments
 (0)