Skip to content

[RUM-18631] Consume dd-sdk-ios through Swift Package Manager - #1455

Open
saraSr5 wants to merge 32 commits into
developfrom
sara/RUM-18631/spm-migration
Open

saraSr5 wants to merge 32 commits into
developfrom
sara/RUM-18631/spm-migration

Conversation

@saraSr5

@saraSr5 saraSr5 commented Sep 29, 2026 •

Copy link
Copy Markdown

What does this PR do?

Builds on @sbarrio's sbarrio/test/spm-dependency-support spike and validates it end to end.

The native iOS SDK is now resolved through Swift Package Manager via React Native's spm_dependency helper, replacing the seven dd-sdk-ios pods. All three sample apps build and run with no Datadog* CocoaPods installed: example (old arch, SDK confirmed live in console), example-new-architecture (Fabric, RN 0.76.9) and benchmarks.

Three fixes on top of the spike:

React-RCTText missing from the session replay podspec. RCTTextExtractor.mm references the RCTText classes directly, but the dependency was only declared on the test spec. Static linking deferred the undefined symbols to the app link and hid it; dynamic linking, which SPM resolution requires, exposes it. Pre-existing bug, surfaced not caused by this change.

Duplicate OpenTelemetryApi module in benchmarks. DatadogTrace resolves opentelemetry-swift-core through SwiftPM while the Podfile pulled OpenTelemetry-Swift-Sdk from CocoaPods, so two copies entered the build and the module scanner warned across every target touching it. Both products now come from the same SwiftPM package.

Native test suites restored. The spike dropped the test_spec blocks, orphaning 18 test files and breaking the three CI jobs that run xcodebuild … test. The stated blockers did not hold: no test uses anything DD_SDK_COMPILED_FOR_TESTING guards (and its injection was broken anyway — targets.detect returns one target, not six), and @testable resolves against SwiftPM modules unchanged. The only real breakage was 16 files using Date/URL/URLRequest without importing Foundation, which used to arrive transitively through the CocoaPods modules. Session replay and webview also link DatadogInternal explicitly, since their tests reference its protocol descriptors and it is not one of the SPM products those pods request. Now at 184 tests in core on both architectures, plus session replay and webview.

Motivation

RUM-18631. The CocoaPods & Carthage deprecation RFC gates stopping publication of dd-sdk-ios to Trunk on React Native having an SPM path, as the last consumer without one. This shows it exists today, with no changes required in dd-sdk-ios.

Only the native SDK changes. React Native and our wrapper pods still install through CocoaPods, resolved from node_modules via autolinking — a path Trunk going read-only does not affect.

Additional Notes

Two calls for a reviewer:

  • No dual path for RN < 0.75. The podspecs raise instead of falling back to s.dependency, but all four packages still declare react-native: ">=0.63.4 <1.0", so a user on RN 0.72 installs cleanly from npm and only fails at pod install. If the raise stays, that range should become >=0.75.0 <1.0 — a breaking change
  • ~25 lines of SPM scaffolding duplicated across the four podspecs. No shared .rb: each package publishes only its own directory to npm, so require_relative across packages breaks once installed. The require.resolve pattern would work, but two satellite packages do not declare @datadog/mobile-react-native in package.json.

Requires dynamic linking, which the sample Podfiles now set unconditionally. DatadogInternal is still reached through an explicit FRAMEWORK_SEARCH_PATHS entry into PackageFrameworks/; a proper @_spi(Internal) surface is separate, larger work.

Review checklist

  • Feature or bugfix MUST have appropriate tests
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
  • If this PR is auto-generated, please make sure also to manually update the code related to the change

@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Pipelines  Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 45da042 | Docs | View more details | Give us feedback!

@saraSr5

saraSr5 commented Sep 29, 2026

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@saraSr5
saraSr5 marked this pull request as ready for review September 29, 2026 14:55
@saraSr5
saraSr5 requested a review from a team as a code owner September 29, 2026 14:55
Copilot AI balanced review requested due to automatic review settings September 29, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Session Replay asset lookup breaks under dynamic linkage, and the advertised React Native support range and internal native test target remain unresolved.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity · 1 Low severity

Open (4)
What changed in this PR

This PR moves the React Native wrapper’s native iOS SDK dependencies from CocoaPods to Swift Package Manager, supporting the planned end of CocoaPods publication for dd-sdk-ios.

Changes:

  • Replace native SDK pod dependencies with Swift Package Manager products and update the three sample apps for dynamic framework linkage.
  • Restore native test targets and add imports and link settings needed by the new dependency layout.
  • Adjust Session Replay resources, WebView headers, and benchmark OpenTelemetry dependencies.
File Description
packages/​react-native-webview/​ios/​Sources/​RCTDatadogWebViewManager.h Supports framework header imports.
packages/​react-native-webview/​ios/​Sources/​RCTDatadogWebView.h Supports framework header imports.
packages/​react-native-webview/​ios/​Sources/​DatadogSDKReactNativeWebView.h Supports framework header imports.
packages/​react-native-webview/​DatadogSDKReactNativeWebView.podspec Requests the native Swift package product.
packages/​react-native-session-replay/​ios/​Tests/​RCTTextViewRecorderTests.swift Imports Foundation explicitly.
packages/​react-native-session-replay/​ios/​Tests/​DdSessionReplayTests.swift Imports Foundation explicitly.
packages/​react-native-session-replay/​ios/​Sources/​Utils/​Bundle+SessionReplay.swift Adapts asset-bundle lookup.
packages/​react-native-session-replay/​DatadogSDKReactNativeSessionReplay.podspec Updates resources and Swift package linkage.
packages/​internal-testing-tools/​DatadogInternalTesting.podspec Requests DatadogCore through SwiftPM.
packages/​core/​ios/​Tests/​RUMMocks.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​RNDdSdkConfigurationTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​ModulesTest.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​MockRUMMonitor.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​MockDatadogCore.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​GlobalStateTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdTraceTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdSdkTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdSdkSessionStartedListenerTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdSdkNativeInitializationTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdRumTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdLogsTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DdFlagsTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​DatadogSdkWrapperTests.swift Imports Foundation explicitly.
packages/​core/​ios/​Tests/​AnyEncodableTests.swift Imports Foundation explicitly.
packages/​core/​DatadogSDKReactNative.podspec Replaces native pods with SwiftPM products.
example/​ios/​Podfile.lock Records the revised pod graph.
example/​ios/​Podfile Enables dynamic frameworks.
example/​ios/​ddSdkReactnativeExample/​PrivacyInfo.xcprivacy Reorders existing privacy entries.
example/​ios/​DdSdkReactNativeExample.xcworkspace/​xcshareddata/​swiftpm/​Package.resolved Pins Swift packages.
example/​ios/​DdSdkReactNativeExample.xcodeproj/​project.pbxproj Updates framework linkage and header paths.
example-new-architecture/​ios/​Podfile.lock Records the revised pod graph.
example-new-architecture/​ios/​Podfile Enables dynamic frameworks.
example-new-architecture/​ios/​DdSdkReactNativeExample/​PrivacyInfo.xcprivacy Reorders existing privacy entries.
example-new-architecture/​ios/​DdSdkReactNativeExample.xcworkspace/​xcshareddata/​swiftpm/​Package.resolved Pins Swift packages.
example-new-architecture/​ios/​DdSdkReactNativeExample.xcodeproj/​project.pbxproj Updates framework linkage and header paths.
benchmarks/​ios/​Podfile.lock Removes native SDK and OpenTelemetry pods.
benchmarks/​ios/​Podfile Enables dynamic frameworks and removes the OpenTelemetry pod.
benchmarks/​ios/​BenchmarkRunner/​PrivacyInfo.xcprivacy Reorders existing privacy entries.
benchmarks/​ios/​BenchmarkRunner.xcworkspace/​xcshareddata/​swiftpm/​Package.resolved Pins Swift packages.
benchmarks/​ios/​BenchmarkRunner.xcodeproj/​project.pbxproj Links OpenTelemetry Swift package products.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@saraSr5
saraSr5 marked this pull request as draft September 29, 2026 15:08
@saraSr5
saraSr5 marked this pull request as ready for review September 29, 2026 15:14
@sbarrio
sbarrio self-requested a review September 30, 2026 07:10
…ls test spec

The webview podspec only requested DatadogWebViewTracking via spm_dependency, relying on
FRAMEWORK_SEARCH_PATHS to make DatadogInternal importable. That's enough to compile against it,
but not to link it: unlike DatadogCore and DatadogSessionReplay, DatadogWebViewTracking's own
autolink metadata doesn't pull DatadogInternal onto this target's link line, so
RCTDatadogWebViewTracking.swift's direct references to it were left unresolved at link time.
Verified with a full DerivedData wipe + clean build, not just a retry.

Also documents the missing use_frameworks! :linkage => :dynamic requirement in the core,
session-replay, and webview READMEs, and restores internal-testing-tools' test spec following
the same pattern as the other three packages -- though unlike those, it could not be verified
against a real build: no app in this repo installs this pod, and a local CocoaPods/Xcode
compatibility issue corrupts Pods.xcodeproj when adding it as a fourth SPM-consuming target.
Copilot AI balanced review requested due to automatic review settings September 30, 2026 07:38
@saraSr5
saraSr5 requested a review from a team as a code owner September 30, 2026 07:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The cross-package linkage migration has an unresolved benchmark target dependency issue, and its tvOS path remains unverified.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Declare OpenTelemetry products as BenchmarkRunner dependencies

benchmarks/​ios/​BenchmarkRunner.xcodeproj/​project.pbxproj:21

The two OpenTelemetry products have framework-phase entries and a project-level package reference, but the BenchmarkRunner target has no packageProductDependencies list (its target definition is at lines 164–184). Xcode needs the product IDs on the target to establish its package build dependencies; otherwise clean builds may fail to resolve or link the Swift imports. Add 10520E8B306BAEDB00BBFBFF (OpenTelemetryApi) and 10520E8D306BAEDB00BBFBFF (OpenTelemetrySdk) to BenchmarkRunner's packageProductDependencies, while retaining their framework-phase entries.

…cies

The Frameworks build phase and the project's packageReferences already referenced
OpenTelemetryApi/OpenTelemetrySdk, but the BenchmarkRunner PBXNativeTarget itself never listed
them in packageProductDependencies -- the field Xcode actually uses to resolve and order the
package build. Verified with a full DerivedData wipe.
Copilot AI balanced review requested due to automatic review settings September 30, 2026 08:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The cross-package linking migration leaves tvOS unverified, and the restored internal-testing subspec is not run by an iOS test host.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Update tvOS dependency compatibility test and verify framework resolution

packages/​core/​DatadogSDKReactNative.podspec:39

The core pod still supports tvOS, but this change requests DatadogWebViewTracking on tvOS too. ios/Tests/TvOSCompatibilityTests.swift:10-15 still says that product is unavailable there and cites the removed s.ios.dependency; its source-guard checks do not exercise SwiftPM resolution or linking. Please update the test's stated contract and verify a tvOS install/build (or keep the dependency platform-specific) before relying on that test for tvOS compatibility.

Medium severity Run restored internal testing subspec regression tests in CI

packages/​internal-testing-tools/​DatadogInternalTesting.podspec:45

Restoring this Tests subspec does not yet run its logging regression test. Neither example Podfile selects DatadogInternalTesting/Tests, the generated sample lockfiles omit the pod, and the iOS CI test jobs only run the core and Session Replay schemes. Add the subspec to an iOS test host and run its test scheme in CI so DdInternalTestingTests.swift is compiled and executed.

Copilot AI balanced review requested due to automatic review settings September 30, 2026 09:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The migration relies on internal SwiftPM framework paths, and end-to-end tvOS linkage remains unverified.

Review effort: Balanced
Findings: 1 Low severity

Open (1)

@saraSr5

saraSr5 commented Sep 30, 2026

Copy link
Copy Markdown
Author

Copilot review overview

🔵 Needs a closer look

The migration relies on internal SwiftPM framework paths, and end-to-end tvOS linkage remains unverified.

Review effort: Balanced Findings: 1 Low severity

Open (1)

Quick update on this and the tvOS thread from before:

Linkage docs: added. The core, session-replay, and webview READMEs now mention use_frameworks! :linkage => :dynamic. This thread is anchored to the podspec line, so it's probably still showing open even though the docs are there.

tvOS: fixed the stale comment. I also tried checking if DatadogWebViewTracking itself still builds for tvOS and it does. What I couldn't check is whether our pod builds for tvOS end to end, as far as I can tell, mainline React Native 0.76.9 doesn't declare tvOS support at all here, so I'm not sure it's even possible without switching to react-native-tvos.

@sbarrio do you know if there's a way to test this that I'm missing? Happy to run it if so.

@saraSr5
saraSr5 force-pushed the sara/RUM-18631/spm-migration branch from ec62a7c to 7d60c17 Compare November 4, 2026 13:56

@marco-saia-datadog marco-saia-datadog left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @saraSr5 👋 Looks good! We just have an issue to address before merging this.

I ran the E2E tests against this branch on every RN version the SPM path supports. All jobs pass, but RN 0.87 and 0.88 with static libraries (RN's default) only pass with one more change to DatadogSDKReactNative.podspec, below.

Without it, 87-newarch and 88-newarch fail to build (the static / dynamic framework variants pass):

Umbrella for module 'DatadogInternalTesting' already covers this directory
Inferred submodules require a module with an umbrella

The reason is that since 0.87, React Native builds every static pod that uses spm_dependency into one shared products folder, which here means all four Datadog pods. CocoaPods copies each Swift pod's module map and umbrella header into its build folder, so four umbrella headers end up in one folder, and clang only allows one umbrella module per folder.

To fix it, we can add a new datadog_link_static_products_from_core step after datadog_restore_static_pod_build_dirs, which runs before datadog_update_moved_module_map_references. The new step:

  1. puts them back into their own build folders by removing React Native's CONFIGURATION_BUILD_DIR override
  2. adds back the SWIFT_INCLUDE_PATHS entry so the pods still find the SDK's Swift modules;
  3. points the app's -fmodule-map-file references back at <Pod>/<Module>.modulemap.

On RN ≤ 0.86 or with use_frameworks! the step doesn't kick in. I have tested this with a local patch applied through patch-package, and it passes all our tests:

@@ -162,6 +162,56 @@
       Xcodeproj::Project.instance_method(:generate_available_uuid_list).bind(project).call(100)
     end
 
+    # React Native 0.87 and later build every statically linked pod that requests package products straight
+    # into the shared products directory (see datadog_update_moved_module_map_references), and CocoaPods
+    # copies each Swift pod's module map and umbrella header into its build directory. With all the
+    # Datadog pods in one directory, clang lets only one umbrella module cover it: "Umbrella for module
+    # ... already covers this directory". Once datadog_link_static_products_from_core has moved every
+    # package product onto the core pod, the other pods request none, so React Native's reason to move
+    # them is gone: put them back into their own build directories, with the include path React Native
+    # gives the pods it does not move, and point the app's references back at their module maps. Finds
+    # nothing to do on earlier versions or with frameworks.
+    def datadog_restore_static_pod_build_dirs(installer, core)
+      return if core.build_as_dynamic?
+
+      products_dir = '${PODS_CONFIGURATION_BUILD_DIR}'
+      search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
+      restored = installer.pod_targets.select do |pod|
+        next false if pod == core || !pod.pod_name.start_with?('Datadog')
+        target = installer.pods_project.native_targets.find { |t| t.name == pod.label }
+        next false if target.nil? || !target.package_product_dependencies.empty?
+        moved = target.build_configurations.select { |config| config.build_settings['CONFIGURATION_BUILD_DIR'] == products_dir }
+        moved.each do |config|
+          config.build_settings.delete('CONFIGURATION_BUILD_DIR')
+          include_paths = Array(config.build_settings['SWIFT_INCLUDE_PATHS'] || '$(inherited)')
+          include_paths << search_path unless include_paths.include?(search_path)
+          config.build_settings['SWIFT_INCLUDE_PATHS'] = include_paths
+        end
+        !moved.empty?
+      end
+      return if restored.empty?
+
+      # React Native rewrote these in the aggregate targets' in-memory xcconfigs, which later post-install
+      # steps save again, so edit the same objects rather than the files.
+      installer.aggregate_targets.each do |aggregate|
+        aggregate.xcconfigs.each do |config_name, xcconfig|
+          changed = false
+          %w[OTHER_CFLAGS OTHER_SWIFT_FLAGS].each do |key|
+            value = xcconfig.attributes[key]
+            next unless value
+            updated = restored.reduce(value) do |acc, pod|
+              module_name = pod.product_module_name
+              acc.gsub("#{products_dir}/#{module_name}.modulemap", "#{products_dir}/#{pod.label}/#{module_name}.modulemap")
+            end
+            next if updated == value
+            xcconfig.attributes[key] = updated
+            changed = true
+          end
+          xcconfig.save_as(aggregate.xcconfig_path(config_name)) if changed
+        end
+      end
+    end
+
     # React Native 0.87 and later build a statically linked pod that requests package products straight
     # into the shared products directory, which moves its module map from <Pod>/<Module>.modulemap to
     # <Module>.modulemap. React Native updates the app's references to it but not those of other pods,
@@ -209,6 +259,7 @@
       datadog_dedupe_static_archive(installer, core) unless core.nil?
       super(installer)
       datadog_link_static_products_from_core(installer, core) unless core.nil?
+      datadog_restore_static_pod_build_dirs(installer, core) unless core.nil?
       datadog_update_moved_module_map_references(installer)
       embed = <<~'SH'
         # Embeds the Datadog Swift package frameworks that Xcode builds dynamically. Looks where Xcode

Another thing to flag: our archive step passes PROVISIONING_PROFILE_SPECIFIER=… to xcodebuild, and with SPM that fails with: <target> does not support provisioning profiles. Settings passed on the command line apply to every target, and unlike CocoaPods' resource bundles (CODE_SIGNING_ALLOWED = NO), these bundles don't opt out of signing. We fixed it on our side by setting the profile on the app target only. Customers whose CI passes the profile the same way, which is common with fastlane and other scripted archives, will hit this after upgrading.

No changes required here, we just have to remember to add mention this in the changelog and migration notes when we release this, something like:

Important

If your build passes PROVISIONING_PROFILE_SPECIFIER to xcodebuild on the command line (for example from fastlane or a CI script), set it on your app target instead: command-line settings apply to every target, including the resource bundles Xcode generates for the Swift package (Datadog_DatadogCore, KSCrash_*, …), and the archive then fails with "<target> does not support provisioning profiles".

Once the fix is in this PR, we'll remove the temporary patch from the E2E branch and re-run the matrix against the branch as-is.

Copilot AI balanced review requested due to automatic review settings November 6, 2026 11:48
@saraSr5

saraSr5 commented Nov 6, 2026

Copy link
Copy Markdown
Author

Hey @marco-saia-datadog, thanks a lot for this review and for running the full E2E matrix against the branch, it's been really helpful! 🙏 I've just applied your fix for the umbrella collision on RN 0.87+ with static libraries in the latest commit 😄

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed to weigh the quadratic build-time archive I/O against the requirement to preserve distinct same-named objects during deduplication.

3 open issues

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 10, 2026 14:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed to evaluate the rebuild-performance risk from quadratic archive I/O and repeated process launches during SDK archive deduplication.

3 open issues

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 10, 2026 14:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The React Native/CocoaPods build-graph, static-archive, and framework-embedding rewrites require native Release/archive validation across supported React Native, Xcode, and linkage combinations that this environment cannot perform.

3 open issues

Review effort: Balanced

@sbarrio sbarrio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

Comment thread .gitlab-ci.yml
GIT_DEPTH: 5
DEVELOP_BRANCH: 'develop'
DEVELOPER_DIR: '/Applications/Xcode-16.4.0.app/Contents/Developer'
DEVELOPER_DIR: '/Applications/Xcode-26.5.0.app/Contents/Developer'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@marco-saia-datadog marco-saia-datadog left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have ran Expo E2E tests against this branch on every Expo version it supports: Expo 52–58 (RN 0.76–0.88), both architectures where available, JSC on Expo 53, and use_frameworks! static/dynamic on Expo 54, 57 and 58.

All 18 jobs pass, but Expo 57+ with use_frameworks! only passes with a change to DatadogSDKReactNativeWebView.podspec, below.

Tested against 2ff3500:

@@ -22,6 +22,42 @@
 # every Datadog product then (see DatadogSDKReactNative.podspec).
 uses_dynamic_frameworks = ENV['USE_FRAMEWORKS'] == 'dynamic'
 
+# USE_FRAMEWORKS says how the app asked pods to be linked, not how this pod ends up being built.
+# Expo 57 and later, with precompiled modules (the default), build every pod that depends on
+# React-Core as a static library even under `use_frameworks!` -- this one and react-native-webview
+# included -- and decide it in a pre-install step, after this podspec has been read. Expo 58 keeps
+# pods that use `spm_dependency` as dynamic frameworks, but only for dynamic linkage. When this pod
+# ends up a static library, the react-native-webview dependency declared below for frameworks fails
+# `pod install` ("The Swift pod `DatadogSDKReactNativeWebView` depends upon `react-native-webview`,
+# which does not define modules"), and it is not needed: a static library resolves the headers through
+# HEADER_SEARCH_PATHS and leaves linking to the app, as without `use_frameworks!`. So just before
+# CocoaPods validates the targets, when every pod's build type is final, drop that dependency from
+# this pod if it is built as a static library. A framework keeps it.
+unless Pod::Installer.method_defined?(:datadog_drop_static_webview_dependency)
+  Pod::Installer.prepend(Module.new do
+    def datadog_drop_static_webview_dependency
+      pod_targets.each do |target|
+        next unless target.pod_name == 'DatadogSDKReactNativeWebView' && !target.build_as_framework?
+
+        dependencies = target.dependent_targets_by_config
+        next unless dependencies.each_value.any? { |deps| deps.any? { |dep| dep.pod_name == 'react-native-webview' } }
+
+        target.dependent_targets_by_config = dependencies.transform_values do |deps|
+          deps.reject { |dep| dep.pod_name == 'react-native-webview' }
+        end
+        # CocoaPods caches the transitive dependencies, which later steps read.
+        target.remove_instance_variable(:@recursive_dependent_targets) if target.instance_variable_defined?(:@recursive_dependent_targets)
+        Pod::UI.puts "[Datadog] #{target.name} is built as a static library, so it does not depend on react-native-webview"
+      end
+    end
+
+    def validate_targets
+      datadog_drop_static_webview_dependency
+      super
+    end
+  end)
+end
+
 Pod::Spec.new do |s|
   s.name         = "DatadogSDKReactNativeWebView"
   s.version      = package["version"]
@@ -44,7 +80,8 @@
   # declared dependency so that CocoaPods puts its build directory on this target's
   # FRAMEWORK_SEARCH_PATHS. Static libraries find them through HEADER_SEARCH_PATHS below instead,
   # and declaring it there fails `pod install`: react-native-webview does not define modules, which
-  # a Swift static library's dependencies must.
+  # a Swift static library's dependencies must. Under `use_frameworks!` this pod can still end up a
+  # static library (Expo's precompiled modules): see datadog_drop_static_webview_dependency above.
   if uses_frameworks
     s.dependency "react-native-webview"
   end

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Session Replay retains the non-modular React-RCTText dependency when Expo builds it as a static library under USE_FRAMEWORKS, causing CocoaPods validation to reject pod install.

3 open issues

Review effort: Balanced

@datadog-prod-us1-3 datadog-prod-us1-3 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bits Code Review: PASS

More details

The SwiftPM migration’s native linking behavior could not be independently verified without Xcode. The completed static review raised no reportable issue.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit 45da042

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants