Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
5044bb2
Added ability to set ReplayOptions for Cocoa
jamescrosswell Oct 22, 2025
faa8955
Added SentryReplayOptions constructor
jamescrosswell Oct 22, 2025
2b38e74
Simplify replay options setup
jamescrosswell Oct 23, 2025
34308e1
Enable session tracking (required) in Cocoa when session replay is en…
jamescrosswell Oct 28, 2025
fd44770
change log
jamescrosswell Oct 28, 2025
24a1894
Associate replays with exceptions
jamescrosswell Oct 28, 2025
d932572
Fixed compilation error
jamescrosswell Oct 28, 2025
7e1ef56
Merge remote-tracking branch 'origin/version6' into ios-replay
jamescrosswell Oct 28, 2025
241d0d8
Updated bindings
jamescrosswell Oct 28, 2025
6696c12
WIP: Generate bindings from Sentry-Swift.h
jpnurmi Sep 2, 2025
28efafc
Fix Xcode 26
jpnurmi Oct 29, 2025
9a504f3
Support wildcards
jpnurmi Oct 29, 2025
d4be03c
Support negation
jpnurmi Oct 29, 2025
f8cf3a8
Process attributes
jpnurmi Oct 29, 2025
21a86be
Remove unneeded Constants
jpnurmi Oct 29, 2025
15a8b02
SentryOnAppStartMeasurementAvailable
jpnurmi Oct 29, 2025
830be9a
SentryStructuredLogAttribute
jpnurmi Oct 29, 2025
f69b682
ref: SyntaxNode/StringExtensions
jpnurmi Oct 29, 2025
3cb7ff6
Fixed bindable options tests
jamescrosswell Oct 29, 2025
1afac38
Disable macos targets on Windows for Maui sample project
jamescrosswell Oct 30, 2025
f8eef20
Merge remote-tracking branch 'origin/ios-replay' into ios-replay
jamescrosswell Oct 30, 2025
e34869e
Merge branch 'version6' into ios-replay
jamescrosswell Oct 30, 2025
74610f8
Insert namespace
jpnurmi Oct 30, 2025
79a385b
Rename SentryRRWebEvent
jpnurmi Oct 30, 2025
6cc4c84
Models
jpnurmi Oct 30, 2025
8a4057e
INSCopying
jpnurmi Oct 30, 2025
bb48bea
tabs to spaces
jpnurmi Oct 30, 2025
0bdee43
Interfaces
jpnurmi Oct 30, 2025
f6d9119
Argument names
jpnurmi Oct 30, 2025
db67798
partial SentryScope
jpnurmi Oct 30, 2025
e366852
MethodToProperty
jpnurmi Oct 30, 2025
eda4876
Format
jpnurmi Oct 30, 2025
85f6fa5
StronglyTypedNSArray
jpnurmi Oct 30, 2025
49c1000
DEPRECATED_MSG_ATTRIBUTE
jpnurmi Oct 30, 2025
5339c6b
StructsAndEnums
jpnurmi Oct 30, 2025
a46c385
Header
jpnurmi Oct 30, 2025
b6db1e3
Try .NET 10 run script
jpnurmi Oct 30, 2025
d38c428
Minor clean up...
jpnurmi Oct 30, 2025
59f6d78
Fix comment
jpnurmi Oct 30, 2025
63316c8
Fix rename
jpnurmi Oct 30, 2025
18aabbf
Remove SentrySession
jpnurmi Oct 30, 2025
b595c57
Clean up...
jpnurmi Oct 30, 2025
e02c641
Restore trailing line-break
jpnurmi Oct 30, 2025
d168d49
Fix interfaces
jpnurmi Oct 30, 2025
0efe682
Shorten switch-cases
jpnurmi Oct 30, 2025
bd44d54
chore: update modules/sentry-cocoa.properties to 8.57.0 (#4637)
github-actions[bot] Oct 16, 2025
ee4a34b
chore: update modules/sentry-cocoa.properties to 8.57.1 (#4680)
github-actions[bot] Oct 30, 2025
6fc1c5e
Remove orphaned link
jamescrosswell Oct 31, 2025
f93b903
Added support for EnableSessionReplayInUnreliableEnvironment option
jamescrosswell Oct 31, 2025
7e962ef
Merge branch 'version6' into ios-replay
jamescrosswell Oct 31, 2025
c426d61
Refactor
jpnurmi Oct 31, 2025
a1d4b66
Prepare KeepMethod/Properties for V9
jpnurmi Oct 31, 2025
857f793
Fix WildcardExtensions.Matches
jpnurmi Oct 31, 2025
32714c0
$PSNativeCommandUseErrorActionPreference = $true
jpnurmi Oct 31, 2025
947d6e9
Merge remote-tracking branch 'origin/build/swift-gen' into ios-replay
jamescrosswell Nov 3, 2025
25007aa
Merge remote-tracking branch 'origin/version6' into ios-replay
jamescrosswell Nov 3, 2025
10a178c
Fixed changelog
jamescrosswell Nov 3, 2025
f79fc89
Review feedback
jamescrosswell Nov 3, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- The SDK now makes use of the new SessionEndStatus `Unhandled` when capturing an unhandled but non-terminal exception, i.e. through the UnobservedTaskExceptionIntegration ([#4633](https://github.com/getsentry/sentry-dotnet/pull/4633), [#4653](https://github.com/getsentry/sentry-dotnet/pull/4653))
- The SDK now provides a `IsSessionActive` to allow checking the session state ([#4662](https://github.com/getsentry/sentry-dotnet/pull/4662))
- The SDK now makes use of the new SessionEndStatus `Unhandled` when capturing an unhandled but non-terminal exception, i.e. through the UnobservedTaskExceptionIntegration ([#4633](https://github.com/getsentry/sentry-dotnet/pull/4633))
- Added experimental support for Session Replay on iOS ([#4664](https://github.com/getsentry/sentry-dotnet/pull/4664))

## 6.0.0-preview.2

Expand Down
1 change: 0 additions & 1 deletion Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{A8A9
scripts\build-sentry-cocoa.sh = scripts\build-sentry-cocoa.sh
scripts\update-project-xml.ps1 = scripts\update-project-xml.ps1
scripts\build-sentry-native.ps1 = scripts\build-sentry-native.ps1
scripts\ios-simulator-utils.ps1 = scripts\ios-simulator-utils.ps1
scripts\commit-formatted-code.sh = scripts\commit-formatted-code.sh
scripts\accept-verifier-changes.ps1 = scripts\accept-verifier-changes.ps1
scripts\generate-cocoa-bindings.ps1 = scripts\generate-cocoa-bindings.ps1
Expand Down
6 changes: 4 additions & 2 deletions samples/Sentry.Samples.Maui/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,22 @@ public static MauiApp CreateMauiApp()
// Automatically create traces for async relay commands in the MVVM Community Toolkit
options.AddCommunityToolkitIntegration();

#if __ANDROID__
// Currently, experimental support is only available on Android
#if __ANDROID__ || __IOS__ || __MACCATALYST__
// Experimental support for Session Replay is currently available on Android, iOS and Mac Catalyst.
options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate = 1.0;
options.Native.ExperimentalOptions.SessionReplay.SessionSampleRate = 1.0;
// Mask all images and text by default. This can be overridden for individual view elements via the
// sentry:SessionReplay.Mask XML attribute (see MainPage.xaml for an example)
options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = true;
options.Native.ExperimentalOptions.SessionReplay.MaskAllText = true;
#if __ANDROID__
// Alternatively, the masking behaviour for entire classes of VisualElements can be configured here as
// an exception to the default behaviour.
// WARNING: In apps with complex user interfaces, consisting of hundreds of visual controls on a single
// page, this option may cause performance issues. In such cases, consider applying the
// sentry:SessionReplay.Mask="Unmask" attribute to individual controls instead.
options.Native.ExperimentalOptions.SessionReplay.UnmaskControlsOfType<Button>();
#endif
#endif

options.SetBeforeScreenshotCapture((@event, hint) =>
Expand Down
2 changes: 1 addition & 1 deletion samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
On Windows, we'll also build for Windows 10.
-->
<TargetFrameworks>$(TargetFrameworks);net9.0-android</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0;net9.0-ios18.0;net9.0-maccatalyst18.0</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('OSX'))">$(TargetFrameworks);net9.0-ios18.0;net9.0-maccatalyst18.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>Sentry.Samples.Maui</RootNamespace>
Expand Down
31 changes: 30 additions & 1 deletion scripts/patch-cocoa-bindings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"SentryDsn",
"SentryEvent",
"SentryException",
"SentryExperimentalOptions",
"SentryFeedback",
"SentryFeedbackAPI",
"SentryFrame",
Expand Down Expand Up @@ -155,7 +156,10 @@
"SentryUser",
"SentryViewScreenshotOptions",
"SentryViewScreenshotProvider"
);
)
// Rename and retarget the experimental options property
.RenameProperty("SentryOptions", "_swiftExperimentalOptions", "Experimental")
.ChangePropertyType("SentryOptions", "Experimental", "SentryExperimentalOptions");

var formatted = CodeFormatter.Format(nodes, new AdhocWorkspace());
File.WriteAllText(args[0], formatted.ToFullString() + "\n");
Expand Down Expand Up @@ -388,6 +392,31 @@ public static CompilationUnitSyntax VerifyProperty(
.Where(node => node.Identifier.Matches(property) && node.HasParent(type));
return root.ReplaceNodes(nodes, (node, _) => node.WithAttributeLists(node.AttributeLists.RemoveAttribute("Verify", verify)));
}

public static CompilationUnitSyntax RenameProperty(
this CompilationUnitSyntax root,
string type,
string from,
string to)
{
var nodes = root.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(node => node.Identifier.Matches(from) && node.HasParent(type));
return root.ReplaceNodes(nodes, (node, _) => node.WithIdentifier(SyntaxFactory.Identifier(to)));
}

public static CompilationUnitSyntax ChangePropertyType(
this CompilationUnitSyntax root,
string type,
string property,
string newType)
{
var nodes = root.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(node => node.Identifier.Matches(property) && node.HasParent(type));
return root.ReplaceNodes(nodes, (node, _) => node.WithType(SyntaxFactory.ParseTypeName(newType)));
}

}

internal static class SyntaxNodeExtensions
Expand Down
32 changes: 31 additions & 1 deletion src/Sentry.Bindings.Cocoa/ApiDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,7 @@ interface SentryOptions

// @property (readonly, nonatomic) NSObject * _Nonnull _swiftExperimentalOptions;
[Export("_swiftExperimentalOptions")]
NSObject _swiftExperimentalOptions { get; }
SentryExperimentalOptions Experimental { get; }
}

// typedef void (^SentryProfilingConfigurationBlock)(SentryProfileOptions * _Nonnull);
Expand Down Expand Up @@ -2126,6 +2126,36 @@ interface SentryUser : SentrySerializable
nuint Hash { get; }
}

// @interface SentryExperimentalOptions : NSObject
[BaseType(typeof(NSObject), Name = "_TtC6Sentry25SentryExperimentalOptions")]
[Internal]
interface SentryExperimentalOptions
{
// @property (nonatomic) BOOL enableDataSwizzling;
[Export("enableDataSwizzling")]
bool EnableDataSwizzling { get; set; }

// @property (nonatomic) BOOL enableFileManagerSwizzling;
[Export("enableFileManagerSwizzling")]
bool EnableFileManagerSwizzling { get; set; }

// @property (nonatomic) BOOL enableUnhandledCPPExceptionsV2;
[Export("enableUnhandledCPPExceptionsV2")]
bool EnableUnhandledCPPExceptionsV2 { get; set; }

// @property (nonatomic) BOOL enableSessionReplayInUnreliableEnvironment;
[Export("enableSessionReplayInUnreliableEnvironment")]
bool EnableSessionReplayInUnreliableEnvironment { get; set; }

// @property (nonatomic) BOOL enableLogs;
[Export("enableLogs")]
bool EnableLogs { get; set; }

// -(void)validateOptions:(NSDictionary<NSString *,id> * _Nullable)options;
[Export("validateOptions:")]
void ValidateOptions([NullAllowed] NSDictionary<NSString, NSObject> options);
}

// @interface SentryFeedback : NSObject
[BaseType(typeof(NSObject), Name = "_TtC6Sentry14SentryFeedback")]
[DisableDefaultCtor]
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry/Internal/ReplaySession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public SentryId? ActiveReplayId
// Check to see if a Replay ID is available
var replayId = JavaSdk.ScopesAdapter.Instance?.Options?.ReplayController?.ReplayId?.ToSentryId();
return (replayId is { } id && id != SentryId.Empty) ? id : null;
#elif __IOS__
string? nativeId = null;
SentryCocoaSdk.ConfigureScope(scope => nativeId = scope.ReplayId);
return (nativeId is { } id) ? SentryId.Parse(id) : null;
#else
return null;
#endif
Expand Down
98 changes: 98 additions & 0 deletions src/Sentry/Platforms/Cocoa/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,104 @@ public void AddInAppInclude(string prefix)
InAppIncludes ??= new List<string>();
InAppIncludes.Add(prefix);
}

/// <summary>
/// Options for experimental features in the native Sentry Cocoa SDK.
/// </summary>
public class NativeExperimentalOptions
{
/// <summary>
/// Session Replay options.
/// </summary>
public NativeSentryReplayOptions SessionReplay { get; set; } = new();
}

/// <summary>
/// Session Replay options for the native Sentry Cocoa SDK.
/// </summary>
public class NativeSentryReplayOptions
{
/// <summary>
/// <para>
/// Forces enabling of session replay in unreliable environments.
/// </para>
/// <para>
/// Due to internal changes with the release of Liquid Glass on iOS 26.0, the masking of text and images can
/// not be reliably guaranteed. Therefore the SDK uses a defensive programming approach to disable the
/// session replay integration by default, unless the environment is detected as reliable.
/// </para>
/// <para>
/// Indicators for reliable environments include:
/// <list type="bullet">
/// <item>
/// <description>Running on an older version of iOS that doesn't have Liquid Glass (iOS 18 or earlier)</description>
/// </item>
/// <item>
/// <description><c>UIDesignRequiresCompatibility</c> is explicitly set to <c>YES</c> in <c>Info.plist</c></description>
/// </item>
/// <item>
/// <description>The app was built with Xcode &lt; 26.0 (DTXcode &lt; 2600)</description>
/// </item>
/// </list>
/// </para>
/// <para>
/// Important: This flag allows to re-enable the session replay integration on iOS 26.0 and later, but please be aware that text and images may not be masked as expected.
/// </para>
/// </summary>
/// <remarks>
/// See https://github.com/getsentry/sentry-cocoa/issues/6389
/// </remarks>
public bool EnableSessionReplayInUnreliableEnvironment { get; set; } = false;

/// <summary>
/// The sample rate for sessions that had an error or crash.
/// Value must be between 0.0 and 1.0.
/// A value of 0.0 disables session replay for errored sessions.
/// A value of 1.0 captures session replay for all errored sessions.
/// </summary>
public double? OnErrorSampleRate { get; set; }
/// <summary>
/// The sample rate for all sessions.
/// Value must be between 0.0 and 1.0.
/// A value of 0.0 disables session replay for all sessions.
/// A value of 1.0 captures session replay for all sessions.
/// </summary>
public double? SessionSampleRate { get; set; }
/// <summary>
/// Whether to mask all images in the session replay by default.
/// </summary>
public bool MaskAllImages { get; set; } = true;
/// <summary>
/// Whether to mask all text in the session replay by default.
/// </summary>
public bool MaskAllText { get; set; } = true;

/// <summary>
/// When enabled, reduces the impact of Session Replay on the main thread and potential frame drops. This is
/// the default and recommended setting, but if you are experiencing issues then you can opt out by setting
/// to <c>false</c>.
/// </summary>
/// <remarks>Defaults to <c>true</c></remarks>
public bool EnableViewRendererV2 { get; set; } = true;
Copy link
Member

Choose a reason for hiding this comment

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

question: are EnableViewRendererV2 and EnableFastViewRendering mutually exclusive?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

They're separate flags in the underlying Cocoa SDK... we don't actually need to surface these in .NET though. I could just remove them unless/until we have some need to expose them.


/// <summary>
/// <para>
/// Enables faster rendering of views at the cost of some visual fidelity.
/// </para>
/// <para>
/// See: https://blog.sentry.io/boosting-session-replay-performance-on-ios-with-view-renderer-v2/
/// </para>
/// </summary>
/// <remarks>Defaults to <c>false</c></remarks>
public bool EnableFastViewRendering { get; set; } = false;

internal bool IsSessionReplayEnabled => OnErrorSampleRate > 0.0 || SessionSampleRate > 0.0;
}

/// <summary>
/// ExperimentalOptions
/// </summary>
public NativeExperimentalOptions ExperimentalOptions { get; set; } = new();
}

// We actually add the profiling integration automatically in InitSentryCocoaSdk().
Expand Down
23 changes: 23 additions & 0 deletions src/Sentry/Platforms/Cocoa/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,29 @@ private static void InitSentryCocoaSdk(SentryOptions options)
// nativeOptions.DefaultIntegrations
// nativeOptions.EnableProfiling (deprecated)

// Session Replay options for the Cocoa SDK
if (options.Native.ExperimentalOptions.SessionReplay.IsSessionReplayEnabled)
{
// For replay to work on iOS, session tracking must be enabled in the Cocoa SDK
options.AutoSessionTracking = false;
nativeOptions.EnableAutoSessionTracking = true;
Copy link
Collaborator Author

@jamescrosswell jamescrosswell Oct 28, 2025

Choose a reason for hiding this comment

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

I'm not sure how we now want to handle situations like this:

var hasTerminalException = processedEvent.HasTerminalException();
if (hasTerminalException)
{
// Event contains a terminal exception -> end session as crashed
_options.LogDebug("Ending session as Crashed, due to unhandled exception.");
scope.SessionUpdate = _sessionManager.EndSession(SessionEndStatus.Crashed);
}
else if (processedEvent.HasException())
{
// Event contains a non-terminal exception -> report error
// (this might return null if the session has already reported errors before)
scope.SessionUpdate = _sessionManager.ReportError();
}

... or this:

// Start a new session
try
{
var sessionUpdate = _sessionManager.StartSession();
if (sessionUpdate is not null)
{
CaptureSession(sessionUpdate);
}
}

If the CocoaSdk owns the session, do we still need to be able to start/stop sessions from the .NET SDK?

Copy link
Member

Choose a reason for hiding this comment

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

these sessions are not the same as session replay. let's not conflate these APIs


// SDK users must explicitly opt-in to Session Replay in unreliable environments
nativeOptions.Experimental.EnableSessionReplayInUnreliableEnvironment =
options.Native.ExperimentalOptions.SessionReplay.EnableSessionReplayInUnreliableEnvironment;

var sessionSampleRate = (float)(options.Native.ExperimentalOptions.SessionReplay.SessionSampleRate ?? 0f);
var onErrorSampleRate = (float)(options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate ?? 0f);
var cocoaReplayOptions = new Sentry.CocoaSdk.SentryReplayOptions();
cocoaReplayOptions.SessionSampleRate = sessionSampleRate;
cocoaReplayOptions.OnErrorSampleRate = onErrorSampleRate;
cocoaReplayOptions.MaskAllText = options.Native.ExperimentalOptions.SessionReplay.MaskAllText;
cocoaReplayOptions.MaskAllImages = options.Native.ExperimentalOptions.SessionReplay.MaskAllImages;
cocoaReplayOptions.EnableViewRendererV2 = options.Native.ExperimentalOptions.SessionReplay.EnableViewRendererV2;
cocoaReplayOptions.EnableFastViewRendering = options.Native.ExperimentalOptions.SessionReplay.EnableFastViewRendering;
nativeOptions.SessionReplay = cocoaReplayOptions;
}

// Set hybrid SDK name
SentryCocoaHybridSdk.SetSdkName("sentry.cocoa.dotnet");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ namespace Sentry.Tests.Platforms.Cocoa;
public class BindableSentryOptionsTests : BindableTests<SentryOptions.NativeOptions>
{
public BindableSentryOptionsTests()
: base(nameof(SentryOptions.NativeOptions.UrlSessionDelegate))
: base(nameof(SentryOptions.NativeOptions.UrlSessionDelegate),
nameof(SentryOptions.NativeOptions.ExperimentalOptions))
{
}

Expand Down
Loading