Background
The .NET SDK wraps the native sentry-cocoa SDK via the Sentry.Bindings.Cocoa project, which generates C# bindings from the framework's Objective-C headers. Historically it bound the classic Sentry* ObjC classes that live in Sentry.framework, plus a deprecated grab-bag of hybrid-only hooks (PrivateSentrySDKOnly).
#5331 (PR #5409) migrated the hybrid/private surface to sentry-cocoa's new structured SentryObjCSDK.internal API. But that was only the private hooks. Everything else — options, SDK lifecycle, the whole event model, scope, enums, debug images — still binds the classic Sentry* ObjC types, so we still ship Sentry.framework alongside the new SentryObjC/SentryObjCCompat frameworks.
The sentry-cocoa team is removing the Objective-C surface from the Sentry target in v10 (~Q3/Q4 2026). Once that lands, the classic Sentry* ObjC classes we bind today will be gone, so this migration is needed to support cocoa v10.
Scope
Rebinding
Migrate the remaining bound Sentry* ObjC surface to SentryObjC*. This is a mechanical rebind — the SentryObjC* types already exist with matching shape (options + callbacks, SDK entry point, the event model, enums, and debug images via [SentryObjCSDK internal].debug).
Scope → event enrichment (currently a private-API hack)
CocoaEventProcessor enriches .NET-captured events with the native SDK's contexts (device, OS, app…). It does this by creating a throwaway native event and calling scope.applyToEvent(event, maxBreadcrumb: 0) — a private cocoa API bound via PrivateApiDefinitions.cs. (This is the last remaining instance of the private-API pattern #5331 set out to eliminate; the code has carried a "this is a private API, find a better way" TODO since cocoa 8.0.)
SentryObjCScope exposes serialize but no applyToEvent.
To be decided: Align with the sentry-cocoa team on what shape the new API should have so that we can continue to enrich .NET captured events with Cocoa SDK context (there might be an easier way to do this).
No exported SentrySerializable protocol
When serializing a native object into an envelope, the wrapper type-tests against cocoa's SentrySerializable protocol (obj is ISentrySerializable) and calls serialize() to get the object's Sentry wire-format dictionary before JSON-encoding. SentryObjC* types have serialize methods but expose no common protocol to test against.
This gap may largely dissolve after item 1 (the objects we serialize this way are the profiling payload — already a plain dictionary — and the throwaway event, which disappears if the processor is reworked).
Possible solution (requires changes to the Cocoa SDK)
If we could add a SentryObjCSDK.internal.currentContexts() method to the Cocoa SDK, that returns the scope's contexts as a plain nested NSDictionary, .NET could simply call that method and then add a bit of logic to apply the bits of that context that we care about to the managed .NET events (no serialisation or throw away event required).
[!NOTE]
Depending on what solution we land on for serialisation, maybe also worth addressing some code duplication while we're there. We have both of the following (largely doing the same thing... only inconsistently):
https://github.com/getsentry/sentry-dotnet/blob/ff8e67f45ac2bdbfb1fc60532535bbd2b22bd78f/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs#L29-L53
|
// For types that implement Sentry Cocoa's SentrySerializable protocol (interface), |
|
// We should call that first, and then serialize the result to JSON later. |
|
var obj = _value is CocoaSdk.ISentrySerializable serializable |
|
? serializable.Serialize() |
|
: _value; |
|
|
|
// Now we will use Apple's JSON Serialization functions. |
|
// See https://developer.apple.com/documentation/foundation/nsjsonserialization |
|
// TODO can we pipe NSOutputStream directly? It can be passed as a second argument |
|
// TODO how do we check if the error happened? Is it non-null? Then we can rethrow as NSErrorException? |
|
return NSJsonSerialization.Serialize(obj, 0, out NSError error); |
SentryEvent does not implement ISentrySerializable
We use this to convert Cocoa events to Managed events by serialising to Json and deserialising (in .NET):
|
public static SentryEvent? ToSentryEvent(this CocoaSdk.SentryEvent sentryEvent) |
|
{ |
|
using var stream = sentryEvent.ToJsonStream(); |
|
if (stream == null) |
|
return null; |
|
|
|
using var json = JsonDocument.Parse(stream); |
|
var exception = sentryEvent.Error == null ? null : new NSErrorException(sentryEvent.Error); |
|
var ev = SentryEvent.FromJson(json.RootElement, exception); |
|
return ev; |
|
} |
Ultimately this is so we can run managed hooks for native exceptions:
https://github.com/getsentry/sentry-dotnet/blob/80c75e3a16812cc5708642edf4e1b99226bb0bea/src/Sentry/Platforms/Cocoa/SentrySdk.cs#L101-L105
|
var sentryEvent = evt.ToSentryEvent(); |
|
if (SentryEventHelper.ProcessEvent(sentryEvent, manualProcessors, null, options, DataCategory.Error) |
|
is not { } processedEvent) |
|
{ |
|
return null; |
|
} |
We could potentially work around this by mapping the properties across directly (as is attempted here), but that solution would require more effort to maintain.
Done when
Related: #5331, #5409
Background
The .NET SDK wraps the native sentry-cocoa SDK via the
Sentry.Bindings.Cocoaproject, which generates C# bindings from the framework's Objective-C headers. Historically it bound the classicSentry*ObjC classes that live inSentry.framework, plus a deprecated grab-bag of hybrid-only hooks (PrivateSentrySDKOnly).#5331 (PR #5409) migrated the hybrid/private surface to sentry-cocoa's new structured
SentryObjCSDK.internalAPI. But that was only the private hooks. Everything else — options, SDK lifecycle, the whole event model, scope, enums, debug images — still binds the classicSentry*ObjC types, so we still shipSentry.frameworkalongside the newSentryObjC/SentryObjCCompatframeworks.The sentry-cocoa team is removing the Objective-C surface from the
Sentrytarget in v10 (~Q3/Q4 2026). Once that lands, the classicSentry*ObjC classes we bind today will be gone, so this migration is needed to support cocoa v10.Scope
Rebinding
Migrate the remaining bound
Sentry*ObjC surface toSentryObjC*. This is a mechanical rebind — theSentryObjC*types already exist with matching shape (options + callbacks, SDK entry point, the event model, enums, and debug images via[SentryObjCSDK internal].debug).Scope → event enrichment (currently a private-API hack)
CocoaEventProcessorenriches .NET-captured events with the native SDK's contexts (device, OS, app…). It does this by creating a throwaway native event and callingscope.applyToEvent(event, maxBreadcrumb: 0)— a private cocoa API bound viaPrivateApiDefinitions.cs. (This is the last remaining instance of the private-API pattern #5331 set out to eliminate; the code has carried a "this is a private API, find a better way" TODO since cocoa 8.0.)SentryObjCScopeexposesserializebut noapplyToEvent.To be decided: Align with the sentry-cocoa team on what shape the new API should have so that we can continue to enrich .NET captured events with Cocoa SDK context (there might be an easier way to do this).
No exported
SentrySerializableprotocolWhen serializing a native object into an envelope, the wrapper type-tests against cocoa's
SentrySerializableprotocol (obj is ISentrySerializable) and callsserialize()to get the object's Sentry wire-format dictionary before JSON-encoding.SentryObjC*types haveserializemethods but expose no common protocol to test against.This gap may largely dissolve after item 1 (the objects we serialize this way are the profiling payload — already a plain dictionary — and the throwaway event, which disappears if the processor is reworked).
Possible solution (requires changes to the Cocoa SDK)
If we could add a
SentryObjCSDK.internal.currentContexts()method to the Cocoa SDK, that returns the scope's contexts as a plain nested NSDictionary, .NET could simply call that method and then add a bit of logic to apply the bits of that context that we care about to the managed .NET events (no serialisation or throw away event required).SentryEventdoes not implementISentrySerializableWe use this to convert Cocoa events to Managed events by serialising to Json and deserialising (in .NET):
sentry-dotnet/src/Sentry/Platforms/Cocoa/Extensions/CocoaExtensions.cs
Lines 305 to 315 in ff8e67f
Ultimately this is so we can run managed hooks for native exceptions:
https://github.com/getsentry/sentry-dotnet/blob/80c75e3a16812cc5708642edf4e1b99226bb0bea/src/Sentry/Platforms/Cocoa/SentrySdk.cs#L101-L105
sentry-dotnet/src/Sentry/Platforms/Cocoa/SentrySdk.cs
Lines 294 to 299 in 80c75e3
We could potentially work around this by mapping the properties across directly (as is attempted here), but that solution would require more effort to maintain.
Done when
Sentry*ObjC types;Sentry.xcframeworkis no longer shipped.Sentry.Bindings.Cocoaconsumes a single prebuiltSentryObjCxcframework (from-source build + thin-framework packaging added in ref: migrate Cocoa bindings from PrivateSentrySDKOnly to SentryObjCSDK.internal #5409 removed).Related: #5331, #5409