diff --git a/CHANGELOG.md b/CHANGELOG.md index 2988586b..eafdab16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Features + +- Improve iOS crash reports by adding scope data ([#491](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/491)) + - ⚠️ This change will most likely affect issue grouping as Sentry now properly symbolicates Kotlin iOS crashes + ### Dependencies - Bump Java SDK from v8.25.0 to v8.27.1 ([#487](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/487)) diff --git a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt index 047ca6b2..1039b2bc 100644 --- a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt +++ b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/SentryUnhandledExceptions.kt @@ -16,6 +16,7 @@ package io.sentry.kotlin.multiplatform.nsexception import Internal.Sentry.NSExceptionKt_SentryCrashStackCursorFromNSException import Internal.Sentry.kSentryLevelFatal +import kotlinx.cinterop.invoke import platform.Foundation.NSException import platform.Foundation.NSNumber @@ -54,12 +55,22 @@ internal fun dropKotlinCrashEvent(event: CocoapodsSentryEvent?): CocoapodsSentry * @see wrapUnhandledExceptionHook */ public fun setSentryUnhandledExceptionHook(): Unit = wrapUnhandledExceptionHook { throwable -> - val envelope = throwable.asSentryEnvelope() - // The envelope will be persisted, so we can safely terminate afterwards. - // https://github.com/getsentry/sentry-cocoa/blob/678172142ac1d10f5ed7978f69d16ab03e801057/Sources/Sentry/SentryClient.m#L409 - InternalSentrySDK.storeEnvelope(envelope as objcnames.classes.SentryEnvelope) - CocoapodsSentrySDK.configureScope { scope -> - scope?.setTagValue(KOTLIN_CRASH_TAG, KOTLIN_CRASH_TAG) + val crashReporter = InternalSentryDependencyContainer.sharedInstance().crashReporter + val handler = crashReporter.uncaughtExceptionHandler + + if (handler != null) { + // This will: + // 1. Write a crash report to disk with ALL synced scope data + // 2. Include tags, user, context, breadcrumbs, etc. + // 3. The crash will be sent on next app launch + handler.invoke(throwable.asNSException(appendCausedBy = true)) + } else { + // Fallback to old approach if handler not available + val envelope = throwable.asSentryEnvelope() + InternalSentrySDK.storeEnvelope(envelope as objcnames.classes.SentryEnvelope) + CocoapodsSentrySDK.configureScope { scope -> + scope?.setTagValue(KOTLIN_CRASH_TAG, KOTLIN_CRASH_TAG) + } } } diff --git a/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/Sentry.Internal.def b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/Sentry.Internal.def index 03d115d4..c48f354e 100644 --- a/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/Sentry.Internal.def +++ b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/Sentry.Internal.def @@ -2,4 +2,4 @@ language = Objective-C headers = SentryClient.h SentryEvent.h SentryDebugImageProvider.h SentryHub.h SentryScope.h \ SentryCrashMonitor_NSException.h SentryCrashMonitor_NSException+NSExceptionKt.h \ SentryCrashStackCursor.h SentryDependencyContainer.h SentryHook.h SentrySDKInternal.h \ - SentryStacktraceBuilder.h SentryThreadInspector.h PrivateSentrySDKOnly.h \ + SentryStacktraceBuilder.h SentryThreadInspector.h PrivateSentrySDKOnly.h SentryCrash.h \ diff --git a/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryCrash.h b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryCrash.h new file mode 100644 index 00000000..e502a4bd --- /dev/null +++ b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryCrash.h @@ -0,0 +1,8 @@ +// SentryCrash interface - expose the uncaughtExceptionHandler +// Based on: https://github.com/getsentry/sentry-cocoa/blob/main/Sources/Sentry/include/SentryCrash.h + +@interface SentryCrash : NSObject + +@property (nonatomic, assign, nullable) NSUncaughtExceptionHandler *uncaughtExceptionHandler; + +@end diff --git a/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryDependencyContainer.h b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryDependencyContainer.h index 0c58c825..ed227812 100644 --- a/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryDependencyContainer.h +++ b/sentry-kotlin-multiplatform/src/nativeInterop/cinterop/SentryInternal/SentryDependencyContainer.h @@ -18,11 +18,13 @@ #import #import +#import @interface SentryDependencyContainer : NSObject + (nonnull instancetype)sharedInstance; @property (nonatomic, strong, nonnull) SentryDebugImageProvider *debugImageProvider; +@property (nonatomic, strong, nonnull) SentryCrash *crashReporter; @end