Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/file-filters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ios_native:
- 'packages/core/ios/**'
- 'packages/core/RNSentryCocoaTester/**'
- 'packages/core/RNSentry.podspec'
- 'packages/core/Package.swift'

# Changes to the SDK's Android native code only (packages/core).
# Does NOT include sample apps — sample changes have their own filters below.
Expand All @@ -40,6 +41,7 @@ js_source:
- 'packages/core/plugin/**'
- 'packages/core/scripts/**'
- 'packages/core/package.json'
- 'packages/core/react-native.config.js'
- 'packages/core/tsconfig.json'
- 'yarn.lock'
- 'package.json'
Expand Down
196 changes: 196 additions & 0 deletions .github/workflows/spm-application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: SwiftPM Application

# Builds a fresh React Native app that consumes the SDK through React Native's
# Swift Package Manager autolinking instead of CocoaPods. The app is generated
# per run rather than committed: the SwiftPM path only works on a plain
# template, because `react-native spm` stops on any autolinked dependency that
# ships no `Package.swift`, and most community libraries still don't.

on:
push:
branches:
- main
- release/**
pull_request:
types: [opened, synchronize, reopened, labeled]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

env:
# The first React Native version with the SwiftPM integration. Bump it
# together with the `Package.swift` platform requirement.
RN_VERSION: "0.87.1"
APP_NAME: SpmTestApp
APP_PATH: spm-app

jobs:
ready-to-merge-gate:
name: Ready-to-merge gate
uses: ./.github/workflows/ready-to-merge-workflow.yml
with:
is-pr: ${{ github.event_name == 'pull_request' }}
labels: ${{ toJson(github.event.pull_request.labels) }}

diff_check:
needs: [ready-to-merge-gate]
uses: ./.github/workflows/skip-ci.yml

detect-changes:
needs: [ready-to-merge-gate]
uses: ./.github/workflows/detect-changes.yml
with:
caller_event_name: ${{ github.event_name }}
caller_ref: ${{ github.ref }}

build-ios:
name: swiftpm ios ${{ matrix.build-type }}
runs-on: macos-26-xlarge
needs: [diff_check, detect-changes]
if: ${{ needs.diff_check.outputs.skip_ci != 'true' && needs.detect-changes.outputs.needs_ios == 'true' }}
strategy:
fail-fast: false
matrix:
build-type: ["dev", "production"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Enable Corepack
run: corepack enable

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6
with:
package-manager-cache: false
node-version: 22
cache: "yarn"
cache-dependency-path: yarn.lock

- name: Install SDK Dependencies
run: yarn install

- name: Build SDK
run: yarn build

- name: Pack SDK
# Packs both workspaces with `yarn pack`, which replaces the
# `workspace:` ranges with concrete versions. `npm pack` leaves them in
# place and the install then fails.
run: yarn build:tarball

- name: Create React Native App
working-directory: ${{ runner.temp }}
run: |
npx --yes @react-native-community/cli@latest init "$APP_NAME" \
--directory "$APP_PATH" \
--version "$RN_VERSION" \
--skip-git-init \
--install-pods false

- name: Install SDK in App
working-directory: ${{ runner.temp }}/${{ env.APP_PATH }}
# Install both tarballs together so the `@sentry/expo-upload-sourcemaps`
# cross-dependency resolves locally. On a release branch the bumped
# version is not on the registry yet, and a fetch would fail with
# ETARGET — the same reason `buildandtest.yml` installs both.
run: |
set -e
CORE_TARBALL=$(find "$GITHUB_WORKSPACE/packages/core" -name 'sentry-react-native-*.tgz' -print -quit)
EXPO_TARBALL=$(find "$GITHUB_WORKSPACE/packages/expo-upload-sourcemaps" -name 'sentry-expo-upload-sourcemaps-*.tgz' -print -quit)
Comment thread
sentry-warden[bot] marked this conversation as resolved.
[ -n "$CORE_TARBALL" ] || { echo "::error::core tarball not found"; exit 1; }
[ -n "$EXPO_TARBALL" ] || { echo "::error::expo-upload-sourcemaps tarball not found"; exit 1; }
npm install "$EXPO_TARBALL" "$CORE_TARBALL"

- name: Set Up SwiftPM
working-directory: ${{ runner.temp }}/${{ env.APP_PATH }}/ios
# `--deintegrate` removes the template's CocoaPods integration first.
# A missing `Package.swift` in any autolinked dependency exits 2 here,
# which is the regression this job is meant to catch.
run: npx react-native spm add --deintegrate

- name: Build iOS App
working-directory: ${{ runner.temp }}/${{ env.APP_PATH }}/ios
env:
# Fix for Xcode 26+ with MetalToolchain: use default Xcode toolchain to avoid
# missing Swift compatibility libraries (swiftCompatibility56, etc.)
# See: https://github.com/actions/runner-images/issues/13135
TOOLCHAINS: com.apple.dt.toolchain.XcodeDefault
run: |
[[ "${{ matrix.build-type }}" == "production" ]] && export CONFIG='Release' || export CONFIG='Debug'

xcodebuild \
-project "$APP_NAME.xcodeproj" \
-scheme "$APP_NAME" \
-configuration "$CONFIG" \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build \
CODE_SIGNING_ALLOWED=NO \
build

- name: Verify SDK Is Linked
working-directory: ${{ runner.temp }}/${{ env.APP_PATH }}/ios
# A silent autolinking regression still produces a green build, so
# assert that a class from each layer reached the app. Both markers are
# Objective-C class names, which the linker keeps in the binary for
# every class it links — unlike, say, the sentry-cocoa package name,
# which appears only incidentally.
run: |
[[ "${{ matrix.build-type }}" == "production" ]] && export CONFIG='Release' || export CONFIG='Debug'
app="build/Build/Products/$CONFIG-iphonesimulator/$APP_NAME.app"

# Debug builds put the code in a separate dylib, Release builds do not.
binary="$app/$APP_NAME"
[[ -f "$app/$APP_NAME.debug.dylib" ]] && binary="$app/$APP_NAME.debug.dylib"
echo "Inspecting $binary"

if ! strings -a "$binary" | grep -q 'RNSentryReplayMask'; then
echo "RNSentry is missing from the app binary."
exit 1
fi
if ! strings -a "$binary" | grep -q 'SentryOptions'; then
echo "sentry-cocoa is missing from the app binary."
exit 1
fi
echo "The app binary contains both RNSentry and sentry-cocoa."

- name: Verify Sentry Categories Survived The Link
working-directory: ${{ runner.temp }}/${{ env.APP_PATH }}/ios
# A class symbol reaching the binary does not prove its categories did.
# sentry-cocoa ships object files that hold nothing but a category
# (`SentryReplayNetworkDetails+Capture`, `Options+Dictionary`,
# `SentryNSNotificationCenterWrapper`), so nothing references them and
# the linker drops them from the static archive unless it is
# force-loaded — the selectors then fail at runtime with "unrecognized
# selector" (#6609, Apple QA1490). `RNSentry.podspec` solves this with
# `-force_load`; SwiftPM has no equivalent, so assert the outcome here.
run: |
[[ "${{ matrix.build-type }}" == "production" ]] && export CONFIG='Release' || export CONFIG='Debug'
app="build/Build/Products/$CONFIG-iphonesimulator/$APP_NAME.app"
binary="$app/$APP_NAME"
[[ -f "$app/$APP_NAME.debug.dylib" ]] && binary="$app/$APP_NAME.debug.dylib"

# Selector defined only inside a category-only object file.
canary='setRequestWithSize:bodyData:contentType:allHeaders:configuredHeaders:'

# Not finding the archive means this check can no longer see what it
# is asserting about, so fail rather than pass silently.
archive=$(find build -type f -path '*Sentry.framework/Sentry' | head -1)
if [ -z "$archive" ]; then
echo "::error::Sentry.framework not found under build/; this check can no longer verify anything."
exit 1
fi

# A renamed selector is different: the canary is stale, not the link.
# Warn to update it rather than report a false dead-strip.
if ! strings -a "$archive" | grep -qF "$canary"; then
echo "::warning::Canary selector not found in sentry-cocoa; update it for this version."
exit 0
fi

if strings -a "$binary" | grep -qF "$canary"; then
echo "Category-only objects survived the link."
else
echo "::error::Sentry ObjC categories were dead-stripped from the app binary."
exit 1
fi
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
### Features

- Populate `trace_ids` in mobile replays so replays can be searched by trace ID ([#6786](https://github.com/getsentry/sentry-react-native/pull/6786))
- Support React Native's Swift Package Manager integration ([#6784](https://github.com/getsentry/sentry-react-native/pull/6784))
- The SDK now ships a `Package.swift`, so `npx react-native spm` picks it up through SwiftPM autolinking instead of failing with `Package.swift is missing for library "@sentry/react-native"`.
- Requires React Native 0.87 or newer, where SwiftPM support is an opt-in preview. CocoaPods remains the default and is unaffected.
- iOS only. macOS, tvOS and visionOS keep building through CocoaPods.

### Dependencies

Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ Sentry.addBreadcrumb({ message: "test" });
Sentry.captureMessage("Hello Sentry!");
```

### Swift Package Manager (iOS)

React Native 0.87 and newer can autolink iOS dependencies through SwiftPM instead of CocoaPods. The SDK ships a `Package.swift`, so no extra configuration is needed:

```sh
npm install @sentry/react-native
cd ios && npx react-native spm add --deintegrate
```

`--deintegrate` removes an existing CocoaPods setup; drop it on a project that never had one. Then build as usual — `npx react-native run-ios`, or open the `.xcodeproj` (SwiftPM projects have no `.xcworkspace`).

Two limitations to know about:

- **Every autolinked dependency must ship a `Package.swift`.** `react-native spm add` stops if one does not, and many community libraries still do not support SwiftPM. This is a React Native constraint, not a Sentry one.
- **iOS only.** macOS, tvOS and visionOS keep building through CocoaPods.

CocoaPods remains the default and is unaffected.

## Upgrade

If you are coming from `react-native-sentry` which was our SDK `< 1.0` you should follow the [upgrade guide](https://docs.sentry.io/platforms/react-native/#upgrading-from-react-native-sentry) and then follow the [install steps](https://docs.sentry.io/platforms/react-native/#integrating-the-sdk).
Expand Down
1 change: 1 addition & 0 deletions packages/core/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
!/dist/**/*
!ts3.8/**/*
!RNSentry.podspec
!/Package.swift
!sentry.gradle
!sentry.gradle.kts
!react-native.config.js
Expand Down
Loading
Loading