Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@
**Warning**

**This may be a breaking change for some setups.** `pod install` now downloads `Sentry.xcframework` from sentry-cocoa's GitHub release (SHA256-verified) and vendors it, instead of building Sentry from source as a CocoaPod. If your iOS build breaks after upgrading (e.g. when another pod also depends on the `Sentry` CocoaPod), or if your `pod install` environment cannot reach `github.com`, set `SENTRY_USE_XCFRAMEWORK=0` before `pod install` to restore the previous source-build behavior.
- **iOS:** On React Native versions where `React-hermes` (or another RN pod) is not modularized by default (e.g. RN 0.71), add `use_modular_headers!` to your `ios/Podfile` above the `target` block. This is required because the `RNSentry` pod now contains Swift code (the new `RNSentryInternal` bridge over `SentrySDK.internal`) and CocoaPods refuses to integrate a Swift pod against non-modular ObjC dependencies. Newer React Native versions require no change ([#6370](https://github.com/getsentry/sentry-react-native/issues/6370)).

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.

Q: Shouldn't we point to the PR ([#6380](https://github.com/getsentry/sentry-react-native/pull/6380)) instead of the issue ([#6370](https://github.com/getsentry/sentry-react-native/issues/6370))?

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.

same for line 23 and 31

- **visionOS:** breadcrumbs no longer include the current screen name. sentry-cocoa's new hybrid-SDK `SentryInternalScreenApi` (which replaces the deprecated `PrivateSentrySDKOnly.setCurrentScreen:`) is gated to iOS/tvOS only, so the bridge stubs the setter on visionOS. Other visionOS behaviour (frames tracking, RNSScreen swizzle) is unchanged ([#6370](https://github.com/getsentry/sentry-react-native/issues/6370)).

### Fixes

- Skip iOS source maps upload on `Debug` builds ([#6405](https://github.com/getsentry/sentry-react-native/pull/6405))

### Internal

- Migrate iOS code from the deprecated `PrivateSentrySDKOnly` SPI (and `SentrySwizzle.h` macro) to the new `SentrySDK.internal` Swift API exposed by sentry-cocoa 9.19.0, via a thin in-pod ObjCโ†”Swift bridge ([#6370](https://github.com/getsentry/sentry-react-native/issues/6370))

## 8.17.2

### Fixes
Expand Down
21 changes: 20 additions & 1 deletion dev-packages/e2e-tests/patch-scripts/rn.patch.podfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,27 @@ if (currentMatch) {
debug.log('Warning: Could not find platform :ios line to patch');
}

// RNSentry now contains Swift code (via the RNSentryInternal bridge over
// SentrySDK.internal). CocoaPods refuses to integrate a Swift pod against
// non-modular ObjC dependencies (e.g. React-hermes on older RN versions),
// so ensure the Podfile requests modular headers globally.
let modularPatched = false;
if (!content.includes('use_modular_headers!')) {
const patched = content.replace(
/prepare_react_native_project!\s*\n/,
"prepare_react_native_project!\nuse_modular_headers!\n",
);
if (patched !== content) {
content = patched;
modularPatched = true;
debug.log('Patching Podfile with use_modular_headers!');
} else {
debug.log('Warning: Could not find prepare_react_native_project! anchor to inject use_modular_headers!');
}
}

// Write the file if any changes were made
if (shouldPatch || currentMatch) {
if (shouldPatch || currentMatch || modularPatched) {
fs.writeFileSync(args['pod-file'], content);
debug.log('Podfile patched successfully!');
} else {
Expand Down
26 changes: 9 additions & 17 deletions packages/core/RNSentry.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,15 @@ Pod::Spec.new do |s|
# in `android/CMakeLists.txt`. The files are guarded with
# `RCT_NEW_ARCH_ENABLED` so they compile to empty TUs on Old Arch.
#
# We include `.swift` (for `RNSentrySwiftLinkStub.swift`) only on RN >=
# 0.75. Adding a Swift file makes CocoaPods treat RNSentry as a Swift
# pod, which then requires modular headers from its ObjC dependencies
# (React-Core, React-hermes) โ€” RN < 0.75 doesn't emit those, so
# `pod install` fails with:
# "The Swift pod `RNSentry` depends upon `React-hermes`, which does
# not define modules."
# The stub is only needed when linking Sentry.xcframework's Swift
# symbols into a dynamic framework anyway (RN 0.86+ `use_frameworks!
# :dynamic`), so gating on RN 0.75 is safe.
supports_swift_stub = rn_version[:major] >= 1 || (rn_version[:major] == 0 && rn_version[:minor] >= 75)
if supports_swift_stub
s.source_files = 'ios/**/*.{h,m,mm,swift}', 'cpp/**/*.{h,cpp}'
s.swift_versions = ['5.5']
else
s.source_files = 'ios/**/*.{h,m,mm}', 'cpp/**/*.{h,cpp}'
end
# Swift is compiled unconditionally because `RNSentryInternal.swift` is
# the sole ObjCโ†”Swift bridge over `SentrySDK.internal.*` โ€” every `.m`/
# `.mm` file in this pod calls into it. That makes RNSentry a Swift pod
# in CocoaPods' eyes, which in turn requires modular headers from its
# ObjC dependencies. Users on React Native < 0.75 (where `React-hermes`
# and friends aren't modularized by default) must add
# `use_modular_headers!` to their Podfile โ€” see CHANGELOG.
s.source_files = 'ios/**/*.{h,m,mm,swift}', 'cpp/**/*.{h,cpp}'
s.swift_versions = ['5.5']
s.public_header_files = 'ios/RNSentry.h', 'ios/RNSentrySDK.h', 'ios/RNSentryStart.h', 'ios/RNSentryVersion.h', 'ios/RNSentryBreadcrumb.h', 'ios/RNSentryReplay.h', 'ios/RNSentryReplayBreadcrumbConverter.h', 'ios/Replay/RNSentryReplayMask.h', 'ios/Replay/RNSentryReplayUnmask.h', 'ios/RNSentryTimeToDisplay.h'

s.compiler_flags = other_cflags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
33F58AD02977037D008F60EA /* RNSentryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33F58ACF2977037D008F60EA /* RNSentryTests.m */; };
2639D71D3BD04F17B0BAC987 /* RNSentryTurboModulePerfControllerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E795057A6D534A80A9D06356 /* RNSentryTurboModulePerfControllerTests.mm */; };
AEFB00422CC90C4B00EC8A9A /* RNSentryBreadcrumbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */; };
AB000123456789ABCDEF0002 /* RNSentryInternalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB000123456789ABCDEF0001 /* RNSentryInternalTests.swift */; };
Comment thread
sentry-warden[bot] marked this conversation as resolved.
B4DEB41739F14AA38202D4D4 /* RNSentryUriValidationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E3742693F7643C2ADE1BDF2 /* RNSentryUriValidationTests.m */; };
B5859A50A3E865EF5E61465A /* libPods-RNSentryCocoaTesterTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */; };
/* End PBXBuildFile section */
Expand All @@ -27,6 +28,7 @@
1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.release.xcconfig"; sourceTree = "<group>"; };
330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryBreadcrumb.h; path = ../ios/RNSentryBreadcrumb.h; sourceTree = "<group>"; };
332D33462CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryReplayOptionsTests.swift; sourceTree = "<group>"; };
AB000123456789ABCDEF0001 /* RNSentryInternalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryInternalTests.swift; sourceTree = "<group>"; };
332D33482CDBDC7300547D76 /* RNSentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentry.h; path = ../ios/RNSentry.h; sourceTree = SOURCE_ROOT; };
332D33492CDCC8E100547D76 /* RNSentryTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryTests.h; sourceTree = "<group>"; };
332D334A2CDCC8EB00547D76 /* RNSentryCocoaTesterTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentryCocoaTesterTests-Bridging-Header.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -119,6 +121,7 @@
3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */,
332D33462CDBDBB600547D76 /* RNSentryReplayOptionsTests.swift */,
3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */,
AB000123456789ABCDEF0001 /* RNSentryInternalTests.swift */,
);
path = RNSentryCocoaTesterTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -273,6 +276,7 @@
33DEDFF02D9185EB006066E4 /* RNSentryTimeToDisplayTests.swift in Sources */,
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */,
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */,
AB000123456789ABCDEF0002 /* RNSentryInternalTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@_spi(Private) import RNSentry
@_spi(Private) import Sentry
import XCTest

/// Smoke coverage for the `RNSentryInternal` ObjCโ†”Swift bridge.
///
/// These are not exhaustive tests of the underlying `SentrySDK.internal.*`
/// surface โ€” sentry-cocoa owns that. We only assert the wrapper does not
/// crash, forwards data correctly, and honours the nil / platform guards
/// documented in `RNSentryInternal.swift`.
final class RNSentryInternalTests: XCTestCase {

override func setUp() {
super.setUp()
// Start the native SDK so `SentrySDK.internal.*` returns real state
// instead of the no-op hub. A minimal, offline-safe DSN is enough.
RNSentrySDK.start { options in
options.dsn = "https://abcd@efgh.ingest.sentry.io/123456"
}
}

override func tearDown() {
SentrySDK.close()
super.tearDown()
}

// MARK: - envelope(fromData:)

func testEnvelopeFromNilDataReturnsNil() {
// Regression guard: the old ObjC `PrivateSentrySDKOnly.envelopeWithData:`
// tolerated nil. `RNSentryInternal.envelope(fromData:)` must too, or
// the ObjC bridge boundary would crash on a nil `NSData*` before the
// Swift body runs. See RNSentry.mm captureEnvelope path.
XCTAssertNil(RNSentryInternal.envelope(fromData: nil))
}

func testEnvelopeFromInvalidDataReturnsNil() {
let junk = Data("this is not an envelope".utf8)
XCTAssertNil(RNSentryInternal.envelope(fromData: junk))
}

// MARK: - SDK metadata

func testSdkMetadataAccessorsAreNonEmpty() {
XCTAssertFalse(RNSentryInternal.sdkName.isEmpty)
XCTAssertFalse(RNSentryInternal.sdkVersionString.isEmpty)
XCTAssertFalse(RNSentryInternal.installationID.isEmpty)
// `extraContext` may be empty on an unstarted SDK; here we only
// assert the accessor does not crash and returns a dictionary.
_ = RNSentryInternal.extraContext
}

func testSetSdkNameAndAddPackageRoundTrip() {
RNSentryInternal.setSdkName("sentry.cocoa.react-native.test", version: "42.42.42")
XCTAssertEqual(RNSentryInternal.sdkName, "sentry.cocoa.react-native.test")
XCTAssertEqual(RNSentryInternal.sdkVersionString, "42.42.42")

// Add-package is void; assert it does not throw. Idempotency across
// sentry-cocoa releases is not part of the bridge's contract.
RNSentryInternal.addSdkPackage("test-package", version: "1.0.0")
}

// MARK: - Options

func testOptionsAccessorReturnsLiveOptions() {
let options = RNSentryInternal.options
XCTAssertNotNil(options.dsn)
}

func testOptionsFromDictionaryValidatesInput() throws {
let dict: [String: Any] = ["dsn": "https://abcd@efgh.ingest.sentry.io/123456"]
let options = try RNSentryInternal.options(fromDictionary: dict)
XCTAssertNotNil(options.dsn)
}

// MARK: - App start / performance hybrid flags

func testAppStartHybridSDKModeIsReadWrite() {
let previous = RNSentryInternal.appStartMeasurementHybridSDKMode
RNSentryInternal.appStartMeasurementHybridSDKMode = !previous
XCTAssertEqual(RNSentryInternal.appStartMeasurementHybridSDKMode, !previous)
RNSentryInternal.appStartMeasurementHybridSDKMode = previous
}

#if os(iOS) || os(tvOS) || os(visionOS)
func testFramesTrackingHybridSDKModeIsReadWrite() {
let previous = RNSentryInternal.framesTrackingMeasurementHybridSDKMode
RNSentryInternal.framesTrackingMeasurementHybridSDKMode = !previous
XCTAssertEqual(RNSentryInternal.framesTrackingMeasurementHybridSDKMode, !previous)
RNSentryInternal.framesTrackingMeasurementHybridSDKMode = previous
}
#endif

// MARK: - Swizzle bridge

func testSwizzleRNSScreenViewDidAppearNoOpWhenClassMissing() {
// `RNSScreen` is not linked in this test target; the bridge should
// early-return without invoking the hook and without touching the
// ObjC runtime.
var hookCalled = false
RNSentryInternal.swizzleRNSScreenViewDidAppear { hookCalled = true }
XCTAssertFalse(hookCalled, "hook must not be called at registration time")
}
}
Loading
Loading