Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Managed layer initializes native SDK on iOS #1915

Merged
merged 45 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5c04e3e
how to get it working
bitsandfoxes Nov 22, 2024
99aaac4
Merge branch 'main' into feat/ios-init-cocoa
bitsandfoxes Nov 25, 2024
913ae70
Added 'native first' options
bitsandfoxes Nov 25, 2024
a05d50e
Updated Xcode modification for new flow
bitsandfoxes Nov 25, 2024
34aeaf1
Updated NativeBridges & removed NoOp
bitsandfoxes Nov 25, 2024
3e43051
Updated setup cocoa target
bitsandfoxes Nov 25, 2024
55ba78e
fixed bridge.meta
bitsandfoxes Nov 25, 2024
03d411f
leave android be
bitsandfoxes Nov 26, 2024
18c5948
2019 meta files
bitsandfoxes Nov 26, 2024
c88e5cd
keep the framework hidden from unity
bitsandfoxes Nov 26, 2024
1c25211
revert post build processing
bitsandfoxes Nov 26, 2024
63f4de2
ignore the bridge as well
bitsandfoxes Nov 26, 2024
480b3b1
added post process to remove deprecated optoin
bitsandfoxes Nov 26, 2024
76d3c63
?
bitsandfoxes Nov 26, 2024
3817247
rev
bitsandfoxes Nov 26, 2024
7d1af1a
only on 2020 and older and only for iOS
bitsandfoxes Nov 27, 2024
fdd1f29
Format code
getsentry-bot Nov 27, 2024
968bb61
exclude editor scripts for samples
bitsandfoxes Nov 27, 2024
2baa488
Merge branch 'chore/sample-no-thumb-update' of https://github.com/get…
bitsandfoxes Nov 27, 2024
7195256
wrong editor
bitsandfoxes Nov 27, 2024
2ceb47a
Merge branch 'chore/sample-no-thumb-update' into feat/ios-init-cocoa
bitsandfoxes Nov 27, 2024
978c6f0
removed option from window
bitsandfoxes Nov 27, 2024
92dc061
setting native first in config
bitsandfoxes Nov 27, 2024
4c4aab9
setting debug settings for sample
bitsandfoxes Nov 27, 2024
550a9c7
fix native init
bitsandfoxes Nov 27, 2024
72b0b79
bring back noop bridge for default setup
bitsandfoxes Nov 27, 2024
dcee725
test
bitsandfoxes Nov 27, 2024
dcff3fd
tests
bitsandfoxes Nov 27, 2024
33451f7
Format code
getsentry-bot Nov 27, 2024
2c1b027
comment
bitsandfoxes Nov 27, 2024
5a8d209
Merge branch 'feat/ios-init-cocoa' of https://github.com/getsentry/se…
bitsandfoxes Nov 27, 2024
b508957
test tweak
bitsandfoxes Nov 27, 2024
c38b8f6
moved fixer from scripts to editor
bitsandfoxes Nov 27, 2024
ff87e96
updated 'pack'
bitsandfoxes Nov 27, 2024
4fcc328
Merge branch 'chore/sample-no-thumb-update' into feat/ios-init-cocoa
bitsandfoxes Nov 27, 2024
26fa897
Updated CHANGELOG.md
bitsandfoxes Nov 27, 2024
66eaf57
Merge branch 'main' into feat/ios-init-cocoa
bitsandfoxes Nov 27, 2024
6800e06
added success log
bitsandfoxes Nov 28, 2024
de577e5
Merge branch 'feat/ios-init-cocoa' of https://github.com/getsentry/se…
bitsandfoxes Nov 28, 2024
7fdd463
Merge branch 'main' into feat/ios-init-cocoa
bitsandfoxes Nov 29, 2024
fc1bb2d
renamed 'native first' to 'standalone'
bitsandfoxes Nov 29, 2024
be50be6
forgot the 'native'
bitsandfoxes Nov 29, 2024
7d4b214
using enum to specify initalization type
bitsandfoxes Dec 2, 2024
6cc5957
revert valid check
bitsandfoxes Dec 2, 2024
7d4756e
fixed fallback check and tests
bitsandfoxes Dec 2, 2024
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### API Changes

- The native layer on iOS no longer self-initializes before the Unity game starts. Instead, it accepts the options at the end of the configure call. To restore the old behavior, users can opt-in to initializing native first via `IosInitializeNativeFirst`. Note that using this option comes with the limitation of baking the options into the generated Xcode project at build-time. ([#1915](https://github.com/getsentry/sentry-unity/pull/1915))
Copy link
Member

Choose a reason for hiding this comment

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

Note that using this option comes with the limitation of baking the options into the generated Xcode project at build-time.

Would be nice to link to the docs on this topic since it's quite complex

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will follow!


### Dependencies

- Bump CLI from v2.39.0 to v2.39.1 ([#1922](https://github.com/getsentry/sentry-unity/pull/1922))
Expand Down
40 changes: 35 additions & 5 deletions package-dev/Plugins/iOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,41 @@
NS_ASSUME_NONNULL_BEGIN

// macOS only
int SentryNativeBridgeLoadLibrary() { return 0; }
void *_Nullable SentryNativeBridgeOptionsNew() { return nil; }
void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) { }
void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) { }
void SentryNativeBridgeStartWithOptions(void *options) { }
// On iOS, the SDK is linked statically so we don't need to dlopen() it.
int SentryNativeBridgeLoadLibrary() { return 1; }

int SentryNativeBridgeIsEnabled() { return [SentrySDK isEnabled] ? 1 : 0; }

const void *SentryNativeBridgeOptionsNew()
{
NSMutableDictionary *dictOptions = [[NSMutableDictionary alloc] init];
dictOptions[@"sdk"] = @ { @"name" : @"sentry.cocoa.unity" };
dictOptions[@"enableAutoSessionTracking"] = @NO;
dictOptions[@"enableAppHangTracking"] = @NO;
return CFBridgingRetain(dictOptions);
}

void SentryNativeBridgeOptionsSetString(const void *options, const char *name, const char *value)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
dictOptions[[NSString stringWithUTF8String:name]] = [NSString stringWithUTF8String:value];
}

void SentryNativeBridgeOptionsSetInt(const void *options, const char *name, int32_t value)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithInt:value];
}

void SentryNativeBridgeStartWithOptions(const void *options)
{
NSMutableDictionary *dictOptions = (__bridge_transfer NSMutableDictionary *)options;
id sentryOptions = [[SentryOptions alloc]
performSelector:@selector(initWithDict:didFailWithError:)
withObject:dictOptions withObject:nil];

[SentrySDK performSelector:@selector(startWithOptions:) withObject:sentryOptions];
}

int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; }

Expand Down
1 change: 1 addition & 0 deletions package-dev/Plugins/iOS/SentryNativeBridge.m.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ void SentryNativeBridgeWriteScope( // clang-format off
) // clang-format on
{ }

NS_ASSUME_NONNULL_END
NS_ASSUME_NONNULL_END
2 changes: 1 addition & 1 deletion package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package-dev/Plugins/macOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ int SentryNativeBridgeLoadLibrary()
return loadStatus;
}

int SentryNativeBridgeIsEnabled() { return [SentrySDK isEnabled] ? 1 : 0; }

const void *SentryNativeBridgeOptionsNew()
{
NSMutableDictionary *dictOptions = [[NSMutableDictionary alloc] init];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,5 @@ MonoBehaviour:
<OptionsConfiguration>k__BackingField: {fileID: 11400000, guid: cea63afb7c75f429799422326f926abe,
type: 2}
<Debug>k__BackingField: 1
<DebugOnlyInEditor>k__BackingField: 1
<DiagnosticLevel>k__BackingField: 2
<DebugOnlyInEditor>k__BackingField: 0
<DiagnosticLevel>k__BackingField: 0

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public override void Configure(SentryUnityOptions options)
return sentryEvent;
});

options.IosNativeInitializationType = NativeInitializationType.BuildTime;

Debug.Log("OptionConfigure finished.");
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 30 additions & 10 deletions src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,29 @@ internal static void AddSentryToXcodeProject(SentryUnityOptions? options,
logger.LogWarning("iOS native support disabled because Sentry has not been configured. " +
"You can do that through the editor: {0}", SentryWindow.EditorMenuPath);

// Even with native support disabled the P/Invoke declarations from `SentryCocoaBridgeProxy` must exist.
SetupNoOpBridge(logger, pathToProject);
return;
}

if (!IsNativeSupportEnabled(options, logger))
if (IsNativeSupportEnabled(options, logger))
{
SetupSentry(options, cliOptions, logger, pathToProject);
}
else
{
logger.LogInfo("iOS native support has been disabled through the options. " +
"Native support will not be available at runtime.");

// Even with native support disabled the P/Invoke declarations from `SentryCocoaBridgeProxy` must exist.
SetupNoOpBridge(logger, pathToProject);
}

// We want to avoid users getting stuck on a cached built output.
// This can happen if the user appends builds
// - and toggles the `IosNativeSupportEnabled` from `true` to `false`
// - and toggles the `IosNativeInitializationType` from `Standalone` to `Runtime`
if (options.IosNativeInitializationType is NativeInitializationType.Runtime)
{
var mainPath = Path.Combine(pathToProject, SentryXcodeProject.MainPath);
if (File.Exists(mainPath))
Expand All @@ -69,12 +87,7 @@ internal static void AddSentryToXcodeProject(SentryUnityOptions? options,
"during a previous build. Select 'Replace' when exporting the project to create a clean project.");
}
}

SetupNoOpBridge(logger, pathToProject);
return;
}

SetupSentry(options, cliOptions, logger, pathToProject);
}

internal static void SetupNoOpBridge(IDiagnosticLogger logger, string pathToProject)
Expand All @@ -83,7 +96,6 @@ internal static void SetupNoOpBridge(IDiagnosticLogger logger, string pathToProj
{
logger.LogDebug("Copying the NoOp bride to the output project.");

// The Unity SDK expects the bridge to be there. The Xcode build will break during linking otherwise.
var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.NoOpBridgeName));
CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger);

Expand All @@ -105,7 +117,11 @@ internal static void SetupSentry(SentryUnityOptions options,

try
{
// The Sentry.xcframework ends in '~' to hide it from Unity. Otherwise, Unity tries to export it with the XCode build.
// The Sentry.xcframework ends in '~' to hide it from Unity. This prevents Unity from exporting it with the XCode build.
// Ideally, we would let Unity copy this over but:
// - Detection of `.xcframework` as datatype and non-folder happened in Unity 2021
// - Without a `.meta` file we cannot opt-in embedding the framework
// - Even if Unity copies it, the framework still requires to be 'linked with binary' for it to work
var frameworkPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.FrameworkName + "~"));
CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", SentryXcodeProject.FrameworkName), logger);

Expand All @@ -115,8 +131,12 @@ internal static void SetupSentry(SentryUnityOptions options,
using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject, logger);
sentryXcodeProject.AddSentryFramework();
sentryXcodeProject.AddSentryNativeBridge();
sentryXcodeProject.AddNativeOptions(options, NativeOptions.CreateFile);
sentryXcodeProject.AddSentryToMain(options);

if (options.IosNativeInitializationType is NativeInitializationType.BuildTime)
{
sentryXcodeProject.AddNativeOptions(options, NativeOptions.CreateFile);
sentryXcodeProject.AddSentryToMain(options);
}

if (cliOptions != null && cliOptions.IsValid(logger, EditorUserBuildSettings.development))
{
Expand Down
6 changes: 5 additions & 1 deletion src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ namespace Sentry.Unity.iOS;
/// <see href="https://github.com/getsentry/sentry-cocoa"/>
internal static class SentryCocoaBridgeProxy
{
// Note: used on macOS only
public static bool IsEnabled() => SentryNativeBridgeIsEnabled() == 1;

public static bool Init(SentryUnityOptions options)
{
if (LoadLibrary() != 1)
Expand Down Expand Up @@ -66,6 +67,9 @@ public static bool Init(SentryUnityOptions options)
[DllImport("__Internal", EntryPoint = "SentryNativeBridgeLoadLibrary")]
private static extern int LoadLibrary();

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeIsEnabled")]
private static extern int SentryNativeBridgeIsEnabled();

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsNew")]
private static extern IntPtr OptionsNew();

Expand Down
12 changes: 12 additions & 0 deletions src/Sentry.Unity.iOS/SentryNativeCocoa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent

if (platform == RuntimePlatform.IPhonePlayer)
{
if (SentryCocoaBridgeProxy.IsEnabled())
{
options.DiagnosticLogger?.LogDebug("The native SDK is already initialized");
}
else if (!SentryCocoaBridgeProxy.Init(options))
{
options.DiagnosticLogger?.LogWarning("Failed to initialize the native SDK");
return;
}

options.ScopeObserver = new NativeScopeObserver("iOS", options);
}
else
Expand Down Expand Up @@ -76,6 +86,8 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent
}
}
}

options.DiagnosticLogger?.LogInfo("Successfully initialized the native SDK");
}

/// <summary>
Expand Down
20 changes: 20 additions & 0 deletions src/Sentry.Unity/SentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ public sealed class SentryUnityOptions : SentryOptions
/// </summary>
public bool IosNativeSupportEnabled { get; set; } = true;

/// <summary>
/// Whether the SDK should initialize the native SDK before the Unity game. This bakes the options at build-time into
/// the generated Xcode project
/// </summary>
public NativeInitializationType IosNativeInitializationType { get; set; } = NativeInitializationType.Runtime;

/// <summary>
/// Whether the SDK should add native support for Android
/// </summary>
Expand Down Expand Up @@ -356,3 +362,17 @@ public enum ScreenshotQuality
/// </summary>
Low
}

public enum NativeInitializationType
{
/// <summary>
/// The native SDK will be initialized at runtime through the C# layer. Options that you set programmatically
/// will apply to the native SDK as well.
/// </summary>
Runtime,
/// <summary>
/// The SDK will bake the options available at build time. The native SDK will auto-initialize outside the of the
/// game. Options that you modify programmatically will not apply to the native SDK.
/// </summary>
BuildTime,
}
Loading
Loading