Conversation
Add a pure Objective-C SDK wrapper that mirrors the Sentry public API so it can be used from ObjC++ without modules (e.g. for React Native, Haxe, custom build systems). Includes: - SentryObjC product and target in Package.swift - Re-declared headers for all public types in Sources/SentryObjC/Public/ - Wrapper implementations for Swift-only types (Unit, Metric, MetricValue, AttributeContent, RedactRegionType) - iOS-ObjectiveCpp-NoModules sample using SentryObjC - sdk_objc_api.json generation via extract-objc-api.py for API stability CI - Makefile targets: build-spm-objc, verify-objc, generate-objc-api Refs GH-6342
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Documentation 📚
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7598 +/- ##
=============================================
- Coverage 86.439% 86.432% -0.007%
=============================================
Files 487 487
Lines 40012 40015 +3
Branches 18011 18007 -4
=============================================
Hits 34586 34586
- Misses 5375 5379 +4
+ Partials 51 50 -1
... and 6 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
Add detailed documentation to core SentryObjC public APIs following Objective-C documentation best practices: - Document all properties with their purpose and behavior - Document all methods with parameters and return values - Add class-level documentation explaining usage context - Include notes about automatic behavior and warnings where applicable Files documented: - SentryObjCSDK: Main SDK entry point - SentryObjCOptions: Configuration options - SentryObjCEvent: Event data structure - SentryObjCScope: Contextual data container - SentryObjCBreadcrumb: Breadcrumb trail - SentryObjCUser: User identification - SentryObjCSpanProtocol: Performance tracing protocol Also adds changelog entry explaining the purpose of the SentryObjC wrapper SDK.
Add comprehensive documentation to exception, attachment, message, and tracing-related classes: - SentryObjCException: Exception information - SentryObjCAttachment: File attachments - SentryObjCMessage: Log messages - SentryObjCSpanContext: Span trace context - SentryObjCTransactionContext: Transaction context All classes now include detailed property and method documentation following Objective-C best practices.
Add comprehensive documentation to: - SentryObjCFrame: Stack frame with source location and context - SentryObjCStacktrace: Stack trace with frames and registers - SentryObjCThread: Thread information and crash state - SentryObjCMechanism: Error mechanism and handling context - SentryObjCDebugMeta: Debug symbols and binary metadata - SentryObjCRequest: HTTP request information - SentryObjCGeo: Geographical location data All classes now include detailed property and method documentation.
Add comprehensive documentation to: - SentryObjCReplayOptions: Session replay configuration with privacy controls - SentryObjCReplayApi: Runtime replay control and masking API - SentryObjCSamplingContext: Context for dynamic trace sampling decisions All properties and methods now include detailed documentation explaining their purpose, behavior, and usage.
Remove the conditional check for sdk_objc_api.json existence since an empty baseline file will be added to main in a follow-up PR. This simplifies the workflow logic.
Keep the original problem description explaining why modules don't work in ObjC++ projects, then show how SentryObjC solves this issue. This provides better context for readers to understand why SentryObjC exists. - Restore "Problem" section describing module import failures - Add "Solution: SentryObjC" section explaining how it solves the issue - List key differences from the main Sentry framework - Reference original issue #4543 and solution PR #6342
Sync with main to include latest changes and adopt new sample structure using projectReferences instead of SPM packages.
Replace Python-based regex parsing with clang AST dump and jq processing. The new approach: - Uses xcrun clang -ast-dump=json for reliable parsing - Extracts declarations via jq queries on intermediate files - Outputs structured JSON objects instead of string signatures - Migrates from Python to pure bash/jq pipeline Also standardize filename to sdk_api_objc.json to match the sdk_api.json and sdk_api_sentryswiftui.json naming convention. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Include the iOS-ObjectiveCpp-NoModules sample in CI build verification to ensure it continues to build successfully with the SentryObjC wrapper. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add the SentryObjC product and target to Package@swift-6.1.swift so the sample can reference it. Also revert iOS-ObjectiveCpp-NoModules sample to use SPM packages instead of projectReferences since SentryObjC is an SPM product, not an Xcode project target. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete documentation for: - SentryObjCAppStartMeasurement (app start types and timestamps) - SentryObjCAttributeContent (typed attribute values) - SentryObjCBaggage (distributed tracing propagation) - SentryObjCError (SDK error codes and helpers) - SentryObjCLogger (structured logging interface) - SentryObjCMechanismContext (crash metadata) - SentryObjCMetric (custom performance metrics) - SentryObjCMetricValue (metric value types) - SentryObjCNSError (serializable error representation) All public types, properties, methods, and enum values now have comprehensive documentation following Objective-C best practices. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
@sentry review |
📲 Install BuildsiOS
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 87074ac | 1217.81 ms | 1255.55 ms | 37.74 ms |
| 0ae2f57 | 1221.43 ms | 1252.13 ms | 30.70 ms |
| aa58669 | 1218.51 ms | 1258.55 ms | 40.04 ms |
| 24f94fe | 1230.71 ms | 1265.21 ms | 34.49 ms |
| d69379c | 1211.30 ms | 1234.77 ms | 23.47 ms |
| dbfeb41 | 1215.17 ms | 1237.41 ms | 22.23 ms |
| 301d380 | 1218.78 ms | 1245.53 ms | 26.75 ms |
| 59981b9 | 1207.25 ms | 1240.71 ms | 33.46 ms |
| bbee1ba | 1197.79 ms | 1215.42 ms | 17.63 ms |
| 0de46cb | 1227.60 ms | 1257.60 ms | 30.00 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 87074ac | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 0ae2f57 | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| aa58669 | 24.14 KiB | 1.10 MiB | 1.07 MiB |
| 24f94fe | 24.15 KiB | 1.10 MiB | 1.07 MiB |
| d69379c | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| dbfeb41 | 24.14 KiB | 1.04 MiB | 1.02 MiB |
| 301d380 | 24.14 KiB | 1.13 MiB | 1.11 MiB |
| 59981b9 | 24.14 KiB | 1.09 MiB | 1.06 MiB |
| bbee1ba | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| 0de46cb | 24.14 KiB | 1.12 MiB | 1.10 MiB |
Previous results on branch: philprime/objc-wrapper-sdk-6342
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| a1f5242 | 1222.28 ms | 1245.53 ms | 23.25 ms |
| a1c7a37 | 1218.20 ms | 1247.48 ms | 29.28 ms |
| 2105a77 | 1213.57 ms | 1225.68 ms | 12.12 ms |
| f042c2d | 1217.49 ms | 1232.57 ms | 15.08 ms |
| 1db3ef0 | 3762.07 ms | 3780.62 ms | 18.56 ms |
| 664430d | 1225.83 ms | 1256.84 ms | 31.01 ms |
| 4bff642 | 1239.29 ms | 1268.56 ms | 29.28 ms |
| 197530f | 1234.94 ms | 1261.72 ms | 26.79 ms |
| bb809e8 | 1229.33 ms | 1262.54 ms | 33.21 ms |
| bfbc9c7 | 1231.69 ms | 1262.49 ms | 30.79 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| a1f5242 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| a1c7a37 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 2105a77 | 24.14 KiB | 1.13 MiB | 1.10 MiB |
| f042c2d | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 1db3ef0 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 664430d | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 4bff642 | 24.14 KiB | 1.13 MiB | 1.11 MiB |
| 197530f | 24.14 KiB | 1.13 MiB | 1.10 MiB |
| bb809e8 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| bfbc9c7 | 24.14 KiB | 1.13 MiB | 1.11 MiB |
The SentryObjC product depends on SentryCppHelper, but it was only defined in the binary targets section. Add it to the compile-from-source targets array so it's available when building SentryObjC from source. Fixes SPM package resolution error: target 'SentryCppHelper' referenced in product 'SentryObjC' could not be found Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add SentryObjCSDK.m implementation that forwards all method calls to SentrySDKInternal. This enables SentryObjC to work in projects with CLANG_ENABLE_MODULES=NO, where Swift's @objc bridging is unavailable. The wrapper provides a pure Objective-C implementation of the SentrySDK class, making all SDK methods accessible via #import <SentryObjC.h> without requiring modules or Swift bridging. Update iOS-ObjectiveCpp-NoModules sample to use the corrected import style (#import <SentryObjC.h> instead of <SentryObjC/SentryObjC.h>) and add module.modulemap for SPM module resolution. Refs #6342
Sentry Build Distribution
|
SentryCppHelper must remain in the initial targets array because the binary distribution products (Sentry, SentrySwiftUI, etc.) depend on it. Moving it to the compile-from-source section broke those binary products. The target can be safely referenced by both binary and source products.
7abca39 to
913cdfd
Compare
Sentry Build Distribution
|
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use typealias SDKAttributeContent to explicitly reference Sentry module's SentryAttributeContent, avoiding ambiguity with SentryObjC wrapper type.
Move SentryObjCBridge.swift from SentryObjC target to Sentry target to avoid module conflicts. When Swift code was in SentryObjC, it could see both the SentryObjC module and Sentry module definitions of the same types (SentryUser, SentryOptions, etc.), causing compilation errors. Architecture is now: - Sentry.framework: SDK + SentrySDKInternal + SentryObjCBridge - SentryObjC.framework: pure ObjC with forward declarations At link time, SentryObjC calls resolve to implementations in Sentry. Also: - Fix bridge imports (can't import Sentry when inside Sentry module) - Add SentryObjC.xcframework to gitignore - Update Ruby script to not include bridge in SentryObjC target
Use swiftc -emit-library to link all three static archives (Sentry, SentryObjCBridge, SentryObjC) into a single standalone dynamic binary. swiftc handles Swift runtime linking automatically, eliminating the need for manual Swift compatibility library paths. Build flow: 1. xcodebuild each target as staticlib 2. libtool -static to merge into one archive 3. swiftc -emit-library -force_load to create dynamic framework 4. Copy SentryObjC public headers and module map Also update develop-docs/SENTRY-OBJC.md with xcframework build documentation. Refs #6342
This commit introduces several new public headers to enhance the SentryObjC SDK, including: - `PrivateSentrySDKOnly.h`: SPI for hybrid SDK wrappers. - `SentryAttribute.h`: Represents typed attribute values for structured logging. - `SentryEnvelope.h`, `SentryEnvelopeHeader.h`, `SentryEnvelopeItem.h`: Define the structure of Sentry envelopes. - `SentryFeedback.h` and `SentryFeedbackSource.h`: Facilitate user feedback submissions. - `SentryLog.h` and `SentryLogLevel.h`: Introduce structured logging capabilities. Additionally, the `project.pbxproj` file has been updated to include these new headers in the public headers section.
Refactor the xcframework build script to define platform-specific framework lists for required and weak frameworks. This change enhances the script's flexibility by allowing different frameworks to be linked based on the target SDK, improving compatibility across platforms.
Add SentryObjCBridge and SentryObjC static slice entries to the release matrix, reusing existing sentry-static slices for Sentry. Create a custom assembly workflow that downloads all three sets of slices, links them via libtool+swiftc, and produces a standalone SentryObjC.xcframework. Remove -ForEmbedding suffix — the static builds are identical to the normal ones. Replace hardcoded per-platform framework lists with dynamic SDK detection to avoid link failures on platforms that lack certain frameworks (e.g. WebKit on watchOS).
CI: SentryObjC XCFramework PipelineAdded CI support for building the standalone What changed
CI flow |
…ework watchOS SDK does not ship SystemConfiguration.framework, so the hardcoded required framework list broke the standalone SentryObjC assembly. Move SystemConfiguration to the candidate weak framework list so it is linked only on platforms where it exists (matching the Xcode project's platform filter: ios, maccatalyst, macos, tvos).
Header files were renamed (dropping the ObjC prefix) without regenerating the tracked public-API JSON, so the API stability check flagged a drift. Regenerated via make generate-public-api.
Replace `id experimental` on the SentryObjC wrapper's SentryOptions with a typed `SentryExperimentalOptions *`, backed by a new public header that mirrors the ObjC-exposable properties of the Swift `SentryExperimentalOptions` class: - enableUnhandledCPPExceptionsV2 - enableSessionReplayInUnreliableEnvironment - enableMetrics - enableWatchdogTerminationsV2 `beforeSendMetric` is intentionally omitted: the Swift signature uses the `SentryMetric` struct, which `@objcMembers` cannot expose to ObjC, so the member is not reachable from ObjC regardless of the header declaration. The header is manually authored so the ObjC-visible surface stays stable when internals of the Swift class evolve.
The previous commit added the header on disk and updated the umbrella import but forgot to list it in the SentryObjC target's publicHeaders exception set. Without this entry, Xcode's file-system-synchronized group includes the header in compilation but does not copy it into the framework's public Headers/ directory, so downstream consumers of the xcframework hit "SentryExperimentalOptions.h file not found".
The SentryObjC framework shipped a plain `module SentryObjC { ... }`
declaration, which trips consumers that enable
`-Werror,-Wincomplete-framework-module-declaration` (Unreal Engine's iOS
toolchain, Xcode's default warning set) with:
error: skipping 'SentryObjC.h' because module declaration of
'SentryObjC' lacks the 'framework' qualifier
Add the `framework` qualifier and the `module * { export *; }` submodule
clause so the declaration matches the convention used by the `Sentry`
target. The source-of-truth is Sources/SentryObjC/Public/module.modulemap,
which is wired into the SentryObjC target via `MODULEMAP_FILE` and copied
into every slice at build time.
Clear MODULEMAP_FILE for the six SentryObjC build configurations and remove the source modulemap. Xcode now generates the framework modulemap automatically, matching how the Sentry target does it, and SPM generates its own library-style modulemap from `publicHeadersPath: "Public"`. The previous override was unnecessary — the only thing it contributed was a shipped declaration missing the `framework` qualifier. Verified by rebuilding the framework and inspecting the generated module.modulemap, and running `swift build --target SentryObjC`.
Split the shipped SentryObjC artifact into two explicit linkage variants,
matching the naming convention already used by the core module:
SentryObjC-Static.xcframework.zip - libtool-merged static archive
packaged as the framework binary.
SentryObjC-Dynamic.xcframework.zip - merged archive re-linked as a dylib
via swiftc (which embeds the Swift
runtime).
Changes:
- Rewrite build-xcframework-sentryobjc-standalone.sh to build both slice
sets from the same merged archive and assemble two xcframeworks.
- Update build-xcframework-local.sh and assemble-xcframework-sentryobjc.yml
to validate, zip, and upload both artifacts.
- Data-drive update-package-sha.sh so new binary targets can be registered
by adding one entry, and so missing zips are skipped with a warning
instead of breaking the release.
- Update SENTRY-OBJC.md and CHANGELOG.md to describe both variants.
The matching Package.swift library products + binary targets will land in a
follow-up PR once the first release that ships these zips has been cut;
adding them now would break `swift package resolve` because SPM fetches
binary targets eagerly during resolution and the 9.10.0 release does not
contain these URLs.
Swift-defined @objcMembers classes are emitted under mangled ObjC runtime names (e.g. _TtC6Sentry14SentryFeedback). Pure-ObjC consumers of SentryObjC referenced the plain _OBJC_CLASS_$_Sentry* symbols and failed to link on iOS; macOS had masked this via NSClassFromString fallbacks in downstream code. Add @compatibility_alias in each affected public header so consumer TUs compile-time-substitute the plain spelling to the mangled class that Sentry.framework exports. Dispatched on SWIFT_PACKAGE since SPM compiles under SentrySwift (prefix 11) while Xcode uses Sentry (prefix 6). Replace plain @Class forward declarations of these classes with full #imports. @compatibility_alias refuses to alias a name that is already forward-declared in the same TU, so every site that used to @Class one of these now imports the full header. Covers SentryAttribute, SentryEnvelope, SentryEnvelopeHeader, SentryEnvelopeItem, SentryExperimentalOptions, SentryFeedback, SentryLog, SentryLogger, SentryReplayOptions. SentryMessage is pure ObjC and SentryOptions already carries @objc(SentryOptions), so both are out of scope. Refs GH-6342
🚨 Detected changes in high risk code 🚨High-risk code can easily blow up and is hard to test. We had severe bugs in the past. Be extra careful when changing these files, and have an extra careful look at these:
|
|
Fixed pure-ObjC link failure for Swift-defined public classes. The Swift compiler emits Added Covered: Out of scope:
Verified:
|
📜 Description
Adds a pure Objective-C SDK wrapper (
SentryObjC) that enables Sentry usage in Objective-C++ projects withCLANG_ENABLE_MODULES=NO. This solves the long-standing issue where Swift SDK APIs are inaccessible without Clang modules, affecting React Native, Haxe, and other custom build systems.What's Included
sdk_objc_api.json💡 Motivation and Context
Closes #6342
The Problem
Since SDK 8.54.0, the Swift SDK's public API requires Clang modules to be imported. When
CLANG_ENABLE_MODULES=NO:#import <Sentry/Sentry.h>only exposes Objective-C APIsSentrySDK,SentryOptions.sessionReplay, and the Metrics API are unavailableuse of undeclared identifier 'SentrySDK'This breaks projects that cannot enable modules, particularly:
Implementation Approach & Thought Process
Initial Considerations
Option 1: Make Swift SDK importable without modules
Option 2: Pure ObjC wrapper (chosen approach)
Architecture Decision
The wrapper uses a three-layer architecture:
Why this design?
SentryObjCSDK.mforward-declaresSentrySDKInternalwithout importing headers, letting the linker resolve at build timeKey Implementation Decisions
1. Metrics API Type Conversion
Challenge: Swift's
SentryAttributeContentenum with associated values cannot be directly bridged to ObjC.Solution: Created
SentryObjCAttributeContentas a class with factory methods:Bridge uses KVC to extract values and convert to Swift enums. This avoids circular imports while maintaining type safety.
2. Session Replay API
Initial approach: Tried forward-declaring
SentrySDKInternal.replaypropertyFinal solution: Exposed via bridge
This pattern proved more reliable and consistent with the metrics/logger APIs.
3. Lazy Initialization Pattern
Original: Metrics API initialized in
+initializeProblem: Runs before SDK initialization, could cause issues if bridge calls Swift before SDK is ready.
Fixed: Switched to
dispatch_oncelazy initialization:Guarantees thread-safe initialization only when first accessed.
4. Package Manifest Consistency
Bug: Swift 6.1 manifest (
Package@swift-6.1.swift) was missingSentryObjCBridgein product targets:This would cause linker errors on Swift 6.1 since the bridge symbols wouldn't be included.
5. Package Resolution for Compile-from-Source Builds
Problem:
SentryObjCproduct includedSentryCppHelper, which isn't available in SPM compile-from-source context, causing package resolution failures in CI.Solution: Removed
SentryCppHelperfromSentryObjCproduct to matchSentrySPMpattern:SentryCppHelperis only needed for binary distribution products, not compile-from-source products.Validation Strategy
Robust validation without logging overhead:
Invalid attributes are silently skipped - this is intentional since:
🧪 Testing Strategy
Unit Tests
Integration Tests
SentryObjCSDKBuild Verification
API Stability
extract-objc-api.sh(uses clang AST parsing)sdk_objc_api.json🔖 Breaking Changes & Compatibility
No Breaking Changes
Hybrid SDK Impact
📝 Checklist
🔗 Related Issues & Context
📦 XCFramework CI Pipeline
The standalone
SentryObjC.xcframeworkbuild is integrated into the release CI workflow:sentry-staticslices — no redundant Sentry static buildsSentryObjCBridgeandSentryObjC(staticlib), built in parallel across all platform SDKsassemble-xcframework-sentryobjc.yml): downloads all 3 static slice sets, merges vialibtool, links viaswiftcinto a dynamic framework per SDK, assembles into xcframework🚀 Future Improvements
Potential follow-ups (not blocking this PR):