diff --git a/.gitignore b/.gitignore
index 9c81b2ea..aa6d8bd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,10 @@ Pods/
.DS_Store
tmp
+# Samples
+iosApp.xcconfig
+sentry.properties
+release/
# Xcode
diff --git a/.run/androidApp-cocoapods.run.xml b/.run/androidApp-cocoapods.run.xml
new file mode 100644
index 00000000..5ca5075e
--- /dev/null
+++ b/.run/androidApp-cocoapods.run.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/androidApp-mvvm-di-spm.run.xml b/.run/androidApp-mvvm-di-spm.run.xml
new file mode 100644
index 00000000..d93e4b81
--- /dev/null
+++ b/.run/androidApp-mvvm-di-spm.run.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/androidApp-spm.run.xml b/.run/androidApp-spm.run.xml
new file mode 100644
index 00000000..b0221433
--- /dev/null
+++ b/.run/androidApp-spm.run.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/iosApp-cocoapods.run.xml b/.run/iosApp-cocoapods.run.xml
new file mode 100644
index 00000000..8ce66786
--- /dev/null
+++ b/.run/iosApp-cocoapods.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/iosApp-mvvm-di-spm.run.xml b/.run/iosApp-mvvm-di-spm.run.xml
new file mode 100644
index 00000000..c449975c
--- /dev/null
+++ b/.run/iosApp-mvvm-di-spm.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/iosApp-spm.run.xml b/.run/iosApp-spm.run.xml
new file mode 100644
index 00000000..55a25237
--- /dev/null
+++ b/.run/iosApp-spm.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04137e9d..8819e947 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## Unreleased
+
+### Improvements
+
+- ref: improve samples & add SPM docs ([#82](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/82))
+
## 0.1.1
### Fixes
diff --git a/Makefile b/Makefile
index 81cf1e51..2e78c223 100644
--- a/Makefile
+++ b/Makefile
@@ -22,9 +22,17 @@ format:
# build and run tests
compile:
./gradlew build
+ make buildAppleSamples
+
+buildAppleSamples:
+ cd ./sentry-samples/kmp-app-cocoapods/iosApp/iosApp && touch iosApp.xcconfig
+ cd ./sentry-samples/kmp-app-spm/iosApp && touch iosApp.xcconfig
+ cd ./sentry-samples/kmp-app-mvvm-di/iosApp && touch iosApp.xcconfig
sudo xcode-select --switch /Applications/Xcode.app && /usr/bin/xcodebuild -version
- cd ./sentry-samples/kmp-app/iosApp; pod install
- xcodebuild -workspace ./sentry-samples/kmp-app/iosApp/iosApp.xcworkspace -scheme iosApp -configuration Debug -sdk iphonesimulator -arch arm64
+ cd ./sentry-samples/kmp-app-cocoapods/iosApp; pod install
+ xcodebuild -workspace ./sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcworkspace -scheme iosApp -configuration Debug -sdk iphonesimulator -arch arm64
+ xcodebuild -project ./sentry-samples/kmp-app-spm/iosApp.xcodeproj -scheme iosApp -configuration Debug -sdk iphonesimulator -arch arm64
+ xcodebuild -project ./sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj -scheme iosApp -configuration Debug -sdk iphonesimulator -arch arm64
# We stop gradle at the end to make sure the cache folders
# don't contain any lock files and are free to be cached.
diff --git a/README.md b/README.md
index 54873013..2e9d99e8 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,31 @@ cocoapods {
}
```
+### Swift Package Manager
+
+Alternatively you can use the Swift Package Manager to include the Sentry Cocoa SDK into this SDK.
+Open your iOS app in Xcode and open File > Add Packages. Then add the SDK by entering the git repo url in the top right search field:
+`https://github.com/getsentry/sentry-cocoa.git`
+
+After adding the package, you need to add the following to your shared `build.gradle.kts`:
+
+```gradle
+listOf(
+ iosX64(),
+ iosArm64(),
+ iosSimulatorArm64(),
+ // ... other Apple targets
+).forEach {
+ it.binaries.framework {
+ baseName = "shared"
+ isStatic = true
+
+ // Export the SDK in order to be able to access it directly in the iOS project
+ export("io.sentry:sentry-kotlin-multiplatform:")
+ }
+}
+```
+
## Initialization
There are two main strategies for initializing the SDK:
diff --git a/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.kt b/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.kt
index 9c6d0cf8..e6257944 100644
--- a/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.kt
+++ b/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.kt
@@ -79,10 +79,12 @@ internal actual object SentryBridge {
}
}
-public fun captureError(error: NSError) {
+@Suppress("unused")
+public fun Sentry.captureError(error: NSError) {
SentrySDK.captureError(error)
}
-public fun captureException(exception: NSException) {
+@Suppress("unused")
+public fun Sentry.captureException(exception: NSException) {
SentrySDK.captureException(exception)
}
diff --git a/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeSendIntegrationTest.kt b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeSendIntegrationTest.kt
index b9e71e3d..b143d8df 100644
--- a/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeSendIntegrationTest.kt
+++ b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeSendIntegrationTest.kt
@@ -2,6 +2,7 @@ package io.sentry.kotlin.multiplatform
import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
import io.sentry.kotlin.multiplatform.protocol.Message
+import io.sentry.kotlin.multiplatform.utils.fakeDsn
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@@ -12,13 +13,16 @@ class BeforeSendIntegrationTest {
@Test
fun `event is not null if KMP beforeSend option is null`() {
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
}
@Test
fun `event is null if KMP beforeSend callback config returns null`() {
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = {
null
}
@@ -29,6 +33,7 @@ class BeforeSendIntegrationTest {
@Test
fun `event is not null if KMP beforeSend callback config returns not null`() {
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event
}
@@ -40,6 +45,7 @@ class BeforeSendIntegrationTest {
fun `event logger is modified if KMP beforeSend callback config modifies it`() {
val expected = "test"
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.logger = expected
event
@@ -53,6 +59,7 @@ class BeforeSendIntegrationTest {
fun `event level is modified if KMP beforeSend callback config modifies it`() {
val expected = SentryLevel.DEBUG
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.level = expected
event
@@ -66,6 +73,7 @@ class BeforeSendIntegrationTest {
fun `event message is modified if KMP beforeSend callback config modifies it`() {
val expected = Message("test")
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.message = expected
event
@@ -79,6 +87,7 @@ class BeforeSendIntegrationTest {
fun `event release is modified if KMP beforeSend callback config modifies it`() {
val expected = "test"
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.release = expected
event
@@ -92,6 +101,7 @@ class BeforeSendIntegrationTest {
fun `event environment is modified if KMP beforeSend callback config modifies it`() {
val expected = "test"
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.environment = expected
event
@@ -105,6 +115,7 @@ class BeforeSendIntegrationTest {
fun `event serverName is modified if KMP beforeSend callback config modifies it`() {
val expected = "test"
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.serverName = expected
event
@@ -118,6 +129,7 @@ class BeforeSendIntegrationTest {
fun `event dist is modified if KMP beforeSend callback config modifies it`() {
val expected = "test"
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.dist = expected
event
@@ -131,6 +143,7 @@ class BeforeSendIntegrationTest {
fun `event fingerprint is modified if KMP beforeSend callback config modifies it`() {
val expected = mutableListOf("test")
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.fingerprint = expected
event
@@ -144,6 +157,7 @@ class BeforeSendIntegrationTest {
fun `event tags are modified if KMP beforeSend callback config modifies it`() {
val expected = mutableMapOf("test" to "test")
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.tags = expected
event
@@ -157,6 +171,7 @@ class BeforeSendIntegrationTest {
fun `event breadcrumbs are modified if KMP beforeSend callback config modifies it`() {
val expected = mutableListOf(Breadcrumb.debug("test"))
val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
it.beforeSend = { event ->
event.breadcrumbs = expected
event
@@ -171,7 +186,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event logger is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.logger, event.logger)
}
@@ -187,7 +204,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event message is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.message, event.message)
}
@@ -195,7 +214,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event release is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.release, event.release)
}
@@ -203,7 +224,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event environment is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.environment, event.environment)
}
@@ -211,7 +234,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event serverName is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.serverName, event.serverName)
}
@@ -219,7 +244,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event dist is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.dist, event.dist)
}
@@ -227,7 +254,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event fingerprint is not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.fingerprint, event.fingerprint)
}
@@ -235,7 +264,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event tags are not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.tags, event.tags)
}
@@ -243,7 +274,9 @@ class BeforeSendIntegrationTest {
@Test
fun `event breadcrumbs are not modified if KMP beforeSend callback config is not modified`() {
val originalEvent = sentryEventConfigurator.originalEvent
- val event = sentryEventConfigurator.applyOptions()
+ val event = sentryEventConfigurator.applyOptions {
+ it.dsn = fakeDsn
+ }
assertNotNull(event)
assertEquals(originalEvent.breadcrumbs, event.breadcrumbs)
}
diff --git a/sentry-samples/README.md b/sentry-samples/README.md
new file mode 100644
index 00000000..8b3a547d
--- /dev/null
+++ b/sentry-samples/README.md
@@ -0,0 +1,53 @@
+
+# Sentry Kotlin Multiplatform Samples
+
+This contains three samples of Kotlin Multiplatform projects showcasing the Sentry Kotlin Multiplatform SDK usage.
+- Sample 1: Android, iOS with Cocoapods, Desktop with Jetpack Compose
+- Sample 2: Android, iOS with Swift Package Manager, Desktop with Jetpack Compose
+- Sample 3: Android with Jetpack Compose, iOS with Jetpack Compose, MVVM and Dependency Injection with [Koin](https://insert-koin.io/)
+
+## Getting Started
+> All samples are configured as sub-projects. You need to open the root project in Android Studio and sync the gradle files.
+
+### Requirements
+- Xcode (for iOS)
+- Android Studio [KMM plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile)
+- [sentry-cli](https://docs.sentry.io/product/cli/installation/)
+- Cocoapods
+
+### Configuration
+Modify the `common-sentry.properties` file according to your needs.
+
+```bash
+# common-sentry.properties
+org.slug=YOUR_ORG_SLUG
+project.slug=YOUR_PROJECT_SLUG
+auth.token=YOUR_AUTH_TOKEN
+```
+#### Android
+Before running the Android app, execute the `./prepare-android-build.sh` script in the `.../sentry-samples/scripts` directory.
+You might need to re-sync your gradle files afterwards.
+By default the Android application is using the `release` build and will automatically upload proguard mapping files for deobfuscation after the setup.
+
+#### iOS
+Before running the iOS app, execute the `./prepare-apple-build.sh` script in the `.../sentry-samples/scripts` directory.
+By default the iOS application will automatically upload debug symbol files for deobfuscation after the setup.
+
+### SDK configuration
+In the shared code of each sample you will find a `SentrySetup.kt` file where you can configure the SDK to your needs.
+
+## Running the Samples
+Run configurations are automatically set through Android Studio. Further configurations are generally not needed.
+
+## Troubleshooting
+> iOS app run configuration in Android Studio is not working / shows an error
+
+Reimporting the `.xcodeproj` or `.xcworkspace` in run configurations depending on whether you run the SPM or Cocoapods sample usually fixes the problem.
+It should automatically reload the project scheme and configuration.
+
+> WARNING: CocoaPods requires your terminal to be using UTF-8 encoding. Consider adding the following to ~/.profile:
+
+Run `export LANG=en_US.UTF-8` to avoid encoding issues.
+
+## Further information
+For more information on the Sentry Kotlin Multiplatform, please refer to the [official Sentry documentation](https://docs.sentry.io/platforms/kotlin-multiplatform/).
diff --git a/sentry-samples/common-sentry.properties b/sentry-samples/common-sentry.properties
new file mode 100644
index 00000000..eb942b7f
--- /dev/null
+++ b/sentry-samples/common-sentry.properties
@@ -0,0 +1,3 @@
+org.slug=YOUR_ORG_SLUG
+project.slug=YOUR_PROJECT_SLUG
+auth.token=YOUR_AUTH_TOKEN
diff --git a/sentry-samples/kmp-app-cocoapods/androidApp/build.gradle.kts b/sentry-samples/kmp-app-cocoapods/androidApp/build.gradle.kts
new file mode 100644
index 00000000..3e70dcaf
--- /dev/null
+++ b/sentry-samples/kmp-app-cocoapods/androidApp/build.gradle.kts
@@ -0,0 +1,51 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("io.sentry.android.gradle") version "3.5.0"
+}
+
+android {
+ compileSdk = Config.Android.compileSdkVersion
+ defaultConfig {
+ applicationId = "sample.kmp.app.android"
+ minSdk = Config.Android.minSdkVersion
+ targetSdk = Config.Android.targetSdkVersion
+ versionCode = 1
+ versionName = "1.0"
+ }
+ signingConfigs {
+ create("release") {
+ storeFile = file("sentry.keystore")
+ storePassword = "sentry"
+ keyAlias = "Sentry Android Key"
+ keyPassword = "sentry"
+ }
+ }
+ buildTypes {
+ getByName("release") {
+ isDefault = true
+ isMinifyEnabled = true
+ proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
+ signingConfig = signingConfigs.getByName("release")
+ }
+ }
+}
+
+dependencies {
+ implementation(rootProject.project(":sentry-samples:kmp-app-spm:shared"))
+ implementation("com.google.android.material:material:1.6.1")
+ implementation("androidx.appcompat:appcompat:1.4.2")
+ implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+}
+
+// Prevent Sentry from being included in the Android app through the AGP.
+configurations {
+ compileOnly {
+ exclude(group = "io.sentry", module = "sentry")
+ exclude(group = "io.sentry", module = "sentry-android")
+ }
+}
+
+sentry {
+ autoUploadProguardMapping.set(false)
+}
diff --git a/sentry-samples/kmp-app-cocoapods/androidApp/sentry.keystore b/sentry-samples/kmp-app-cocoapods/androidApp/sentry.keystore
new file mode 100644
index 00000000..3d6ddc7c
Binary files /dev/null and b/sentry-samples/kmp-app-cocoapods/androidApp/sentry.keystore differ
diff --git a/sentry-samples/kmp-app/androidApp/src/main/AndroidManifest.xml b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/AndroidManifest.xml
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/AndroidManifest.xml
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/AndroidManifest.xml
diff --git a/sentry-samples/kmp-app/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
diff --git a/sentry-samples/kmp-app/androidApp/src/main/res/layout/activity_main.xml b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/layout/activity_main.xml
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/res/layout/activity_main.xml
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/layout/activity_main.xml
diff --git a/sentry-samples/kmp-app/androidApp/src/main/res/raw/sentry.png b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/raw/sentry.png
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/res/raw/sentry.png
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/raw/sentry.png
diff --git a/sentry-samples/kmp-app/androidApp/src/main/res/values/colors.xml b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/values/colors.xml
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/res/values/colors.xml
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/values/colors.xml
diff --git a/sentry-samples/kmp-app/androidApp/src/main/res/values/styles.xml b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/values/styles.xml
similarity index 100%
rename from sentry-samples/kmp-app/androidApp/src/main/res/values/styles.xml
rename to sentry-samples/kmp-app-cocoapods/androidApp/src/main/res/values/styles.xml
diff --git a/sentry-samples/kmp-app/desktopApp/build.gradle.kts b/sentry-samples/kmp-app-cocoapods/desktopApp/build.gradle.kts
similarity index 94%
rename from sentry-samples/kmp-app/desktopApp/build.gradle.kts
rename to sentry-samples/kmp-app-cocoapods/desktopApp/build.gradle.kts
index 95fe47b1..0e52becb 100644
--- a/sentry-samples/kmp-app/desktopApp/build.gradle.kts
+++ b/sentry-samples/kmp-app-cocoapods/desktopApp/build.gradle.kts
@@ -1,4 +1,3 @@
-import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
@@ -25,7 +24,7 @@ kotlin {
sourceSets {
val jvmMain by getting {
dependencies {
- implementation(rootProject.project(":sentry-samples:kmp-app:shared"))
+ implementation(rootProject.project(":sentry-samples:kmp-app-cocoapods:shared"))
implementation(compose.desktop.currentOs)
}
}
diff --git a/sentry-samples/kmp-app-cocoapods/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt b/sentry-samples/kmp-app-cocoapods/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
new file mode 100644
index 00000000..0f82c667
--- /dev/null
+++ b/sentry-samples/kmp-app-cocoapods/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
@@ -0,0 +1,67 @@
+// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package sample.kmp.app.desktop
+
+import androidx.compose.desktop.ui.tooling.preview.Preview
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.application
+import io.sentry.kotlin.multiplatform.Sentry
+import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
+import sample.kmp.app.LoginImpl
+import sample.kmp.app.Platform
+import sample.kmp.app.configureSentryScope
+import sample.kmp.app.initializeSentry
+
+@Composable
+@Preview
+fun App() {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ val btnBackgroundColor = Color(56, 31, 67)
+ Button({
+ Sentry.captureMessage("From KMP Sample App: " + Platform().platform)
+ }, colors = ButtonDefaults.buttonColors(backgroundColor = btnBackgroundColor)) {
+ Text("Capture Message", color = Color.White)
+ }
+ Button({
+ LoginImpl.login("MyUsername")
+ }, colors = ButtonDefaults.buttonColors(backgroundColor = btnBackgroundColor)) {
+ Text("Capture Exception", color = Color.White)
+ }
+ Button({
+ LoginImpl.login()
+ }, colors = ButtonDefaults.buttonColors(backgroundColor = btnBackgroundColor)) {
+ Text("Crash", color = Color.White)
+ }
+ }
+}
+
+fun main() = application {
+ Window(onCloseRequest = ::exitApplication, title = "Jetpack Compose Desktop App (Cocoapods Sample Version)") {
+ // Initialize Sentry using shared code
+ initializeSentry()
+
+ // Shared scope across all platforms
+ configureSentryScope()
+
+ // Add platform specific scope in addition to the shared scope
+ Sentry.configureScope {
+ it.setContext("JVM Desktop Context", mapOf("context1" to 12, "context2" to false))
+ it.addBreadcrumb(Breadcrumb.debug("initialized Sentry on JVM Desktop"))
+ }
+
+ App()
+ }
+}
diff --git a/sentry-samples/kmp-app/iosApp/Podfile b/sentry-samples/kmp-app-cocoapods/iosApp/Podfile
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/Podfile
rename to sentry-samples/kmp-app-cocoapods/iosApp/Podfile
diff --git a/sentry-samples/kmp-app/iosApp/Podfile.lock b/sentry-samples/kmp-app-cocoapods/iosApp/Podfile.lock
similarity index 91%
rename from sentry-samples/kmp-app/iosApp/Podfile.lock
rename to sentry-samples/kmp-app-cocoapods/iosApp/Podfile.lock
index bfe0d33e..c32432f8 100644
--- a/sentry-samples/kmp-app/iosApp/Podfile.lock
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/Podfile.lock
@@ -23,7 +23,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Sentry: 16d46dd5ca10e7f4469a2611805a3de123188add
SentryPrivate: 2bb4f8d9ff558b25ac70b66c1dedc58a7c43630b
- shared: 0dca2e824df822c0e0186869c320e4ba675f23ed
+ shared: d37be71558369434a00872610da90714f79a1fb3
PODFILE CHECKSUM: f282da88f39e69507b0a255187c8a6b644477756
diff --git a/sentry-samples/kmp-app/iosApp/iosApp.xcodeproj/project.pbxproj b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcodeproj/project.pbxproj
similarity index 83%
rename from sentry-samples/kmp-app/iosApp/iosApp.xcodeproj/project.pbxproj
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcodeproj/project.pbxproj
index ca21bc09..8c4ca0d0 100644
--- a/sentry-samples/kmp-app/iosApp/iosApp.xcodeproj/project.pbxproj
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -9,31 +9,32 @@
/* Begin PBXBuildFile section */
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
- 0E5B2A1A28A27E06000489BA /* sentry.png in Resources */ = {isa = PBXBuildFile; fileRef = 0E5B2A1928A27E06000489BA /* sentry.png */; };
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
+ 243652F129F3500B00FD902A /* sentry.png in Resources */ = {isa = PBXBuildFile; fileRef = 243652F029F3500B00FD902A /* sentry.png */; };
+ 3D1664DC8EBDF25BA5271797 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9EEA8758FDDDA81262859B40 /* Pods_iosApp.framework */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
- D5B08EC7C23D3FC698ACBEB5 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD4480B062D7800C5B353B6F /* Pods_iosApp.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
- 0E5B2A1928A27E06000489BA /* sentry.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sentry.png; sourceTree = ""; };
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
- 25CA49B0A5ED1BE92174FF45 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
+ 243652F029F3500B00FD902A /* sentry.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sentry.png; sourceTree = ""; };
+ 24B0FC5D29F8273700434F1C /* iosApp.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iosApp.xcconfig; sourceTree = ""; };
+ 27764284A12D67D77B0D8962 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 85E7ED6A895F840ADEBA6EA9 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
- FD4480B062D7800C5B353B6F /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9EEA8758FDDDA81262859B40 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ D2E6F99EDC5C3622F933AA2F /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- 870B838CCCF9D96E0B41F9F4 /* Frameworks */ = {
+ 15ED548C5D994C7F345A5039 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- D5B08EC7C23D3FC698ACBEB5 /* Pods_iosApp.framework in Frameworks */,
+ 3D1664DC8EBDF25BA5271797 /* Pods_iosApp.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -48,11 +49,11 @@
path = "Preview Content";
sourceTree = "";
};
- 12A4D8E5051784B06944ACD4 /* Pods */ = {
+ 1E09B7524956916C5D9D7D40 /* Pods */ = {
isa = PBXGroup;
children = (
- 25CA49B0A5ED1BE92174FF45 /* Pods-iosApp.debug.xcconfig */,
- 85E7ED6A895F840ADEBA6EA9 /* Pods-iosApp.release.xcconfig */,
+ D2E6F99EDC5C3622F933AA2F /* Pods-iosApp.debug.xcconfig */,
+ 27764284A12D67D77B0D8962 /* Pods-iosApp.release.xcconfig */,
);
path = Pods;
sourceTree = "";
@@ -62,8 +63,8 @@
children = (
7555FF7D242A565900829871 /* iosApp */,
7555FF7C242A565900829871 /* Products */,
- 12A4D8E5051784B06944ACD4 /* Pods */,
- E43E59E03F8D83D9E75F05C0 /* Frameworks */,
+ 1E09B7524956916C5D9D7D40 /* Pods */,
+ C3B0B54580AA0124B27DF2B7 /* Frameworks */,
);
sourceTree = "";
};
@@ -78,20 +79,21 @@
7555FF7D242A565900829871 /* iosApp */ = {
isa = PBXGroup;
children = (
- 0E5B2A1928A27E06000489BA /* sentry.png */,
+ 243652F029F3500B00FD902A /* sentry.png */,
058557BA273AAA24004C7B11 /* Assets.xcassets */,
7555FF82242A565900829871 /* ContentView.swift */,
7555FF8C242A565B00829871 /* Info.plist */,
2152FB032600AC8F00CF470E /* iOSApp.swift */,
058557D7273AAEEB004C7B11 /* Preview Content */,
+ 24B0FC5D29F8273700434F1C /* iosApp.xcconfig */,
);
path = iosApp;
sourceTree = "";
};
- E43E59E03F8D83D9E75F05C0 /* Frameworks */ = {
+ C3B0B54580AA0124B27DF2B7 /* Frameworks */ = {
isa = PBXGroup;
children = (
- FD4480B062D7800C5B353B6F /* Pods_iosApp.framework */,
+ 9EEA8758FDDDA81262859B40 /* Pods_iosApp.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -103,11 +105,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
buildPhases = (
- E80F4795196947BF23867272 /* [CP] Check Pods Manifest.lock */,
+ 0BFD73D9FC258ED49378CBD3 /* [CP] Check Pods Manifest.lock */,
7555FF77242A565900829871 /* Sources */,
7555FF79242A565900829871 /* Resources */,
- 870B838CCCF9D96E0B41F9F4 /* Frameworks */,
- 3620F85FA2EFF718C6F7366B /* [CP] Embed Pods Frameworks */,
+ 15ED548C5D994C7F345A5039 /* Frameworks */,
+ B9409BDB3577907681ECC50B /* [CP] Embed Pods Frameworks */,
+ 24B0FC5C29F8270F00434F1C /* Upload Debug Symbols to Sentry */,
);
buildRules = (
);
@@ -157,7 +160,7 @@
buildActionMask = 2147483647;
files = (
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
- 0E5B2A1A28A27E06000489BA /* sentry.png in Resources */,
+ 243652F129F3500B00FD902A /* sentry.png in Resources */,
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -165,24 +168,29 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 3620F85FA2EFF718C6F7366B /* [CP] Embed Pods Frameworks */ = {
+ 0BFD73D9FC258ED49378CBD3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
- name = "[CP] Embed Pods Frameworks";
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- E80F4795196947BF23867272 /* [CP] Check Pods Manifest.lock */ = {
+ 24B0FC5C29F8270F00434F1C /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -190,18 +198,32 @@
inputFileListPaths = (
);
inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
+ "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
);
- name = "[CP] Check Pods Manifest.lock";
+ name = "Upload Debug Symbols to Sentry";
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ shellScript = "if which sentry-cli >/dev/null; then\nERROR=$(sentry-cli upload-dif \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"warning: sentry-cli - $ERROR\"\nfi\nelse\necho \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n";
+ };
+ B9409BDB3577907681ECC50B /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -221,6 +243,7 @@
/* Begin XCBuildConfiguration section */
7555FFA3242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5D29F8273700434F1C /* iosApp.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -282,6 +305,7 @@
};
7555FFA4242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5D29F8273700434F1C /* iosApp.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@@ -337,7 +361,7 @@
};
7555FFA6242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 25CA49B0A5ED1BE92174FF45 /* Pods-iosApp.debug.xcconfig */;
+ baseConfigurationReference = D2E6F99EDC5C3622F933AA2F /* Pods-iosApp.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
@@ -357,7 +381,7 @@
};
7555FFA7242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 85E7ED6A895F840ADEBA6EA9 /* Pods-iosApp.release.xcconfig */;
+ baseConfigurationReference = 27764284A12D67D77B0D8962 /* Pods-iosApp.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
diff --git a/sentry-samples/kmp-app/iosApp/iosApp.xcworkspace/contents.xcworkspacedata b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcworkspace/contents.xcworkspacedata
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp.xcworkspace/contents.xcworkspacedata
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcworkspace/contents.xcworkspacedata
diff --git a/sentry-samples/kmp-app/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/Contents.json
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Assets.xcassets/Contents.json
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/ContentView.swift b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/ContentView.swift
similarity index 86%
rename from sentry-samples/kmp-app/iosApp/iosApp/ContentView.swift
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/ContentView.swift
index ec6ad541..7e383a7a 100644
--- a/sentry-samples/kmp-app/iosApp/iosApp/ContentView.swift
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/ContentView.swift
@@ -6,7 +6,7 @@ struct ContentView: View {
Text("KMP Sample App " + Platform().platform)
VStack() {
Button("Capture Message") {
- Sentry().captureMessage(message: "From KMP Sample App " + Platform().platform)
+ Sentry.shared.captureMessage(message: "From KMP Sample App " + Platform().platform)
}
Button("Capture Exception") {
LoginImpl().login(username: "MyUsername")
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/Info.plist b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Info.plist
similarity index 98%
rename from sentry-samples/kmp-app/iosApp/iosApp/Info.plist
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Info.plist
index 40fa6dc1..8044709c 100644
--- a/sentry-samples/kmp-app/iosApp/iosApp/Info.plist
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Info.plist
@@ -43,6 +43,6 @@
UIInterfaceOrientationLandscapeRight
UILaunchScreen
-
+
-
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/Contents.json b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
similarity index 96%
rename from sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/Contents.json
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
index 73c00596..4aa7c535 100644
--- a/sentry-samples/kmp-app/iosApp/iosApp/Assets.xcassets/Contents.json
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -3,4 +3,4 @@
"author" : "xcode",
"version" : 1
}
-}
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/iOSApp.swift b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/iOSApp.swift
new file mode 100644
index 00000000..3c03f0d4
--- /dev/null
+++ b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/iOSApp.swift
@@ -0,0 +1,35 @@
+import SwiftUI
+import shared
+
+@main
+struct iOSApp: App {
+ let sentry = Sentry.shared
+
+ init() {
+ // Initialize Sentry using shared code
+ SentrySetupKt.initializeSentry()
+
+ // Shared scope across all platforms
+ SentrySetupKt.configureSentryScope()
+
+ // Add platform specific scope in addition to the shared scope
+ sentry.configureScope { scope in
+ scope.setContext(key: "iOS Context", value: [
+ "context1": 20,
+ "context2": true
+ ])
+ let breadcrumb = Breadcrumb.companion.debug(message: "initialized Sentry on iOS")
+ scope.addBreadcrumb(breadcrumb: breadcrumb)
+ if let path = Bundle.main.path(forResource: "sentry", ofType: "png") {
+ let imageAttachment = Attachment(pathname: path, filename: "sentry.png")
+ scope.addAttachment(attachment: imageAttachment)
+ }
+ }
+ }
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/sentry.png b/sentry-samples/kmp-app-cocoapods/iosApp/iosApp/sentry.png
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp/sentry.png
rename to sentry-samples/kmp-app-cocoapods/iosApp/iosApp/sentry.png
diff --git a/sentry-samples/kmp-app/shared/build.gradle.kts b/sentry-samples/kmp-app-cocoapods/shared/build.gradle.kts
similarity index 82%
rename from sentry-samples/kmp-app/shared/build.gradle.kts
rename to sentry-samples/kmp-app-cocoapods/shared/build.gradle.kts
index 32f9922a..3f4394e1 100644
--- a/sentry-samples/kmp-app/shared/build.gradle.kts
+++ b/sentry-samples/kmp-app-cocoapods/shared/build.gradle.kts
@@ -4,10 +4,14 @@ plugins {
id("com.android.library")
}
-version = "1.0"
-
kotlin {
- android()
+ android {
+ compilations.all {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ }
+ }
jvm()
iosX64()
iosArm64()
@@ -16,10 +20,11 @@ kotlin {
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
+ version = "1.0"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosApp/Podfile")
- pod(Config.Libs.sentryCocoa, "~> ${Config.Libs.sentryCocoaVersion}")
+ pod("Sentry", "~> 8.4.0")
framework {
baseName = "shared"
@@ -38,16 +43,9 @@ kotlin {
implementation(kotlin("test"))
}
}
-
- val jvmMain by getting {
- dependsOn(commonMain)
- }
-
- val androidMain by getting {
- dependsOn(commonMain)
- }
+ val jvmMain by getting
+ val androidMain by getting
val androidUnitTest by getting
-
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
@@ -70,9 +68,10 @@ kotlin {
}
android {
- compileSdk = Config.Android.compileSdkVersion
+ compileSdk = 32
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
- minSdk = Config.Android.minSdkVersion
+ minSdk = 27
+ targetSdk = 32
}
}
diff --git a/sentry-samples/kmp-app/shared/shared.podspec b/sentry-samples/kmp-app-cocoapods/shared/shared.podspec
similarity index 95%
rename from sentry-samples/kmp-app/shared/shared.podspec
rename to sentry-samples/kmp-app-cocoapods/shared/shared.podspec
index 14bc67a4..1fa7c843 100644
--- a/sentry-samples/kmp-app/shared/shared.podspec
+++ b/sentry-samples/kmp-app-cocoapods/shared/shared.podspec
@@ -12,7 +12,7 @@ Pod::Spec.new do |spec|
spec.dependency 'Sentry', '~> 8.4.0'
spec.pod_target_xcconfig = {
- 'KOTLIN_PROJECT_PATH' => ':sentry-samples:kmp-app:shared',
+ 'KOTLIN_PROJECT_PATH' => ':sentry-samples:kmp-app-cocoapods:shared',
'PRODUCT_MODULE_NAME' => 'shared',
}
diff --git a/sentry-samples/kmp-app/shared/src/androidMain/AndroidManifest.xml b/sentry-samples/kmp-app-cocoapods/shared/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/androidMain/AndroidManifest.xml
rename to sentry-samples/kmp-app-cocoapods/shared/src/androidMain/AndroidManifest.xml
diff --git a/sentry-samples/kmp-app/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt b/sentry-samples/kmp-app-cocoapods/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt
diff --git a/sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt b/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt
diff --git a/sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt b/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt
diff --git a/sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/AppSetup.kt b/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/commonMain/kotlin/sample.kmp.app/AppSetup.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
diff --git a/sentry-samples/kmp-app/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt b/sentry-samples/kmp-app-cocoapods/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt
diff --git a/sentry-samples/kmp-app/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt b/sentry-samples/kmp-app-cocoapods/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt
diff --git a/sentry-samples/kmp-app/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt b/sentry-samples/kmp-app-cocoapods/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt
similarity index 100%
rename from sentry-samples/kmp-app/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt
rename to sentry-samples/kmp-app-cocoapods/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt
diff --git a/sentry-samples/kmp-app-mvvm-di/README.md b/sentry-samples/kmp-app-mvvm-di/README.md
new file mode 100644
index 00000000..36969957
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/README.md
@@ -0,0 +1,57 @@
+# Sentry KMP Demo App
+
+This is a demo app for the Sentry Kotlin Multiplatform SDK that includes a native iOS app and a native Android app with shared code.
+
+## Shared Features
+ - Dependency Injection with Koin
+ - ViewModels
+ - Sentry Setup
+
+## Getting Started
+
+### IDE
+
+Install the [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile) for Android Studio.
+
+You can use Android Studio to run both the Android and iOS sample apps.
+
+The android target is available as `sentry-samples.kmp-app-mvvm-di.androidApp` automatically.
+The iOS target needs to be configured: add a new run configuration and select an iOS application as a new target.
+You can then select the `iosApp.xcworkspace` file for the required `Xcode project file`.
+
+### Android
+- Export your `ANDROID_HOME` environment variable if you haven't done already.
+- You can run `./gradlew :sentry-samples:kmp-app:androidApp:assembleDebug` to compile the Android app.
+
+### iOS
+
+#### Cocoapods
+You need Cocoapods installed on your machine.
+
+Run `export LANG=en_US.UTF-8` to avoid encoding issues.
+
+`pod install` will automatically run through gradle if you run the iOS app through Android Studio.
+However, you can still run `pod install` on the iOS folder manually if you want to make sure the pods are up to date.
+
+#### DSYMS
+First you need to have `sentry-cli` installed.
+
+Then add the following script to your `Build Phases` in Xcode and change the `org`, `project`, `auth_token` slug placeholders accordingly:
+Make sure to change the placeholders correctly, otherwise the iOS app will not run.
+
+```shell
+if which sentry-cli >/dev/null; then
+export SENTRY_ORG=
+export SENTRY_PROJECT=
+export SENTRY_AUTH_TOKEN=
+ERROR=$(sentry-cli upload-dif "$DWARF_DSYM_FOLDER_PATH" 2>&1 >/dev/null)
+if [ ! $? -eq 0 ]; then
+echo "warning: sentry-cli - $ERROR"
+fi
+else
+echo "warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases"
+fi
+```
+
+### Sentry Setup
+If you need to change the `DSN` or any options you can do so in the `SentrySetup.kt` file in the `shared` module.
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/build.gradle.kts b/sentry-samples/kmp-app-mvvm-di/androidApp/build.gradle.kts
new file mode 100644
index 00000000..7e62c3c7
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/build.gradle.kts
@@ -0,0 +1,77 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("io.sentry.android.gradle") version "3.5.0"
+}
+
+android {
+ namespace = "sentry.kmp.demo.android"
+ compileSdk = 33
+ defaultConfig {
+ applicationId = "sentry.kmp.demo"
+ minSdk = 21
+ targetSdk = 33
+ versionCode = 1
+ versionName = "1.0"
+ }
+ buildFeatures {
+ compose = true
+ }
+ signingConfigs {
+ create("release") {
+ storeFile = file("sentry.keystore")
+ storePassword = "sentry"
+ keyAlias = "Sentry Android Key"
+ keyPassword = "sentry"
+ }
+ }
+ buildTypes {
+ getByName("release") {
+ isDefault = true
+ isMinifyEnabled = true
+ proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
+ signingConfig = signingConfigs.getByName("release")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8.toString()
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion = "1.4.0-dev-k1.8.0-33c0ad36f83"
+ }
+}
+
+dependencies {
+ implementation(rootProject.project(":sentry-samples:kmp-app-mvvm-di:shared"))
+ implementation("androidx.core:core-ktx:1.10.0")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
+ implementation("androidx.appcompat:appcompat:1.6.1")
+ implementation("com.google.android.material:material:1.8.0")
+ implementation("androidx.activity:activity-compose:1.7.0")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.6.1")
+ implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
+ implementation("androidx.navigation:navigation-compose:2.5.3")
+ implementation("androidx.navigation:navigation-runtime:2.5.3")
+ implementation("io.insert-koin:koin-android:3.4.0")
+ implementation("androidx.compose.compiler:compiler:1.4.0-dev-k1.8.0-33c0ad36f83")
+ implementation("androidx.compose.ui:ui:1.5.0-alpha02")
+ implementation("androidx.compose.ui:ui-tooling:1.5.0-alpha02")
+ implementation("androidx.compose.foundation:foundation:1.5.0-alpha02")
+ implementation("androidx.compose.material:material:1.5.0-alpha02")
+}
+
+// Prevent Sentry from being included in the Android app through the AGP.
+configurations {
+ compileOnly {
+ exclude(group = "io.sentry", module = "sentry")
+ exclude(group = "io.sentry", module = "sentry-android")
+ }
+}
+
+sentry {
+ autoUploadProguardMapping.set(false)
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/sentry.keystore b/sentry-samples/kmp-app-mvvm-di/androidApp/sentry.keystore
new file mode 100644
index 00000000..3d6ddc7c
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/sentry.keystore differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/AndroidManifest.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..f6802f44
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainActivity.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainActivity.kt
new file mode 100644
index 00000000..d66aaab1
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainActivity.kt
@@ -0,0 +1,26 @@
+package sentry.kmp.demo.android
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.koin.core.component.KoinComponent
+import sentry.kmp.demo.android.theme.Theme
+import sentry.kmp.demo.android.ui.MyApp
+import sentry.kmp.demo.models.AuthenticationViewModel
+import sentry.kmp.demo.models.HomeViewModel
+
+class MainActivity : ComponentActivity(), KoinComponent {
+
+ private val authenticationViewModel: AuthenticationViewModel by viewModel()
+ private val homeViewModel: HomeViewModel by viewModel()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Theme {
+ MyApp(authenticationViewModel, homeViewModel)
+ }
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt
new file mode 100644
index 00000000..1d60baaa
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt
@@ -0,0 +1,22 @@
+package sentry.kmp.demo.android
+
+import android.app.Application
+import android.content.Context
+import org.koin.dsl.module
+import sentry.kmp.demo.initKoin
+import sentry.kmp.demo.sentry.initSentry
+
+class MainApp : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+
+ initSentry(this)
+
+ initKoin(
+ module {
+ single { this@MainApp }
+ }
+ )
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Color.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Color.kt
new file mode 100644
index 00000000..81b515ff
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Color.kt
@@ -0,0 +1,8 @@
+package sentry.kmp.demo.android.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Shapes.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Shapes.kt
new file mode 100644
index 00000000..ee7ee816
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Shapes.kt
@@ -0,0 +1,11 @@
+package sentry.kmp.demo.android.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Theme.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Theme.kt
new file mode 100644
index 00000000..a1c77b63
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Theme.kt
@@ -0,0 +1,38 @@
+package sentry.kmp.demo.android.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+@Composable
+fun Theme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Typography.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Typography.kt
new file mode 100644
index 00000000..327d3dde
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/theme/Typography.kt
@@ -0,0 +1,15 @@
+package sentry.kmp.demo.android.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+)
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/Composables.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/Composables.kt
new file mode 100644
index 00000000..810ac516
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/Composables.kt
@@ -0,0 +1,25 @@
+package sentry.kmp.demo.android.ui
+
+import androidx.compose.runtime.Composable
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import sentry.kmp.demo.models.AuthenticationViewModel
+import sentry.kmp.demo.models.HomeViewModel
+
+@Composable
+fun MyApp(authenticationViewModel: AuthenticationViewModel, homeViewModel: HomeViewModel) {
+ val navController = rememberNavController()
+
+ NavHost(
+ navController = navController,
+ startDestination = "login"
+ ) {
+ composable("login") {
+ LoginScreen(navController, authenticationViewModel)
+ }
+ composable("home") {
+ HomeScreen(navController, homeViewModel)
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/HomeScreen.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/HomeScreen.kt
new file mode 100644
index 00000000..dd20aafb
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/HomeScreen.kt
@@ -0,0 +1,83 @@
+package sentry.kmp.demo.android.ui
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import sentry.kmp.demo.models.HomeViewModel
+
+@Composable
+fun HomeScreen(navController: NavController, homeViewModel: HomeViewModel) {
+ var showDialog by remember { mutableStateOf(false) }
+ var dialogMessage by remember { mutableStateOf("") }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = "Welcome!",
+ style = MaterialTheme.typography.h4,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ Text(
+ text = homeViewModel.homeText,
+ style = MaterialTheme.typography.body1,
+ fontWeight = FontWeight.Normal,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ Button(
+ onClick = {
+ homeViewModel.updateProfileWithErr()
+ dialogMessage = "An error occurred during profile update"
+ showDialog = true
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ ) {
+ Text("Update Profile (error)")
+ }
+ Button(
+ onClick = { navController.popBackStack() },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ ) {
+ Text("Log Out")
+ }
+ if (showDialog) {
+ AlertDialog(
+ onDismissRequest = { showDialog = false },
+ title = { Text("Error") },
+ text = { Text(dialogMessage) },
+ confirmButton = {
+ Button(
+ onClick = { showDialog = false }
+ ) {
+ Text("OK")
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/LoginScreen.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/LoginScreen.kt
new file mode 100644
index 00000000..c7c668c8
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/ui/LoginScreen.kt
@@ -0,0 +1,125 @@
+package sentry.kmp.demo.android.ui
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.Checkbox
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import sentry.kmp.demo.models.AuthenticationViewModel
+
+@Composable
+fun LoginScreen(navController: NavController, authenticationViewModel: AuthenticationViewModel) {
+ val email = remember { mutableStateOf("user@sentrydemo.com") }
+ val password = remember { mutableStateOf("randompassword") }
+ val enableLoginError = remember { mutableStateOf(true) }
+ var showDialog by remember { mutableStateOf(false) }
+ var dialogMessage by remember { mutableStateOf("") }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = "Sentry Demo",
+ style = MaterialTheme.typography.h4,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ OutlinedTextField(
+ value = email.value,
+ onValueChange = { email.value = it },
+ label = { Text("Email") },
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Email
+ ),
+ modifier = Modifier.fillMaxWidth()
+ )
+ OutlinedTextField(
+ value = password.value,
+ onValueChange = { password.value = it },
+ label = { Text("Password") },
+ visualTransformation = PasswordVisualTransformation(),
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Password
+ ),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ )
+ Button(
+ onClick = {
+ val succeeded = authenticationViewModel.login(enableLoginError.value)
+ if (succeeded) {
+ navController.navigate("home")
+ } else {
+ dialogMessage = "An error occurred during login"
+ showDialog = true
+ }
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ ) {
+ Text("Log In ${if (enableLoginError.value) "(error)" else ""}")
+ }
+ Button(
+ onClick = {
+ authenticationViewModel.signUp()
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ ) {
+ Text("Sign up (crash)")
+ }
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text("Enable login error")
+ Checkbox(
+ checked = enableLoginError.value,
+ onCheckedChange = { enableLoginError.value = it },
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ }
+ if (showDialog) {
+ AlertDialog(
+ onDismissRequest = { showDialog = false },
+ title = { Text("Error") },
+ text = { Text(dialogMessage) },
+ confirmButton = {
+ Button(
+ onClick = { showDialog = false }
+ ) {
+ Text("OK")
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..1f6bb290
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_24px.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_24px.xml
new file mode 100644
index 00000000..ce351f43
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_border_24px.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_border_24px.xml
new file mode 100644
index 00000000..e6646709
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_favorite_border_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_launcher_background.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..0d025f9b
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..6f3b755b
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..6f3b755b
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..898f3ed5
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dffca360
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..64ba76f7
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dae5e082
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..e5ed4659
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..14ed0af3
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b0907cac
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..d8ae0315
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..2c18de9e
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..beed3cdd
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/colors.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/colors.xml
new file mode 100644
index 00000000..69b22338
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/strings.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/strings.xml
new file mode 100644
index 00000000..f83b2779
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Sentry KMP Demo
+
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/styles.xml b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/styles.xml
new file mode 100644
index 00000000..5885930d
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.pbxproj b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..38876913
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.pbxproj
@@ -0,0 +1,447 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 52;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
+ 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
+ 1DBB948028897D4700E79663 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 1DBB947F28897D4700E79663 /* Sentry */; };
+ 243652ED29F34FBF00FD902A /* sentry.png in Resources */ = {isa = PBXBuildFile; fileRef = 243652EC29F34FBF00FD902A /* sentry.png */; };
+ 243652F629F3516400FD902A /* Koin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243652F229F3516400FD902A /* Koin.swift */; };
+ 243652F729F3516400FD902A /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243652F329F3516400FD902A /* LoginScreen.swift */; };
+ 243652F829F3516400FD902A /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243652F429F3516400FD902A /* HomeScreen.swift */; };
+ 243652F929F3516400FD902A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243652F529F3516400FD902A /* AppDelegate.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 7555FFB4242A642300829871 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 243652EC29F34FBF00FD902A /* sentry.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sentry.png; sourceTree = ""; };
+ 243652F229F3516400FD902A /* Koin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Koin.swift; sourceTree = ""; };
+ 243652F329F3516400FD902A /* LoginScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = ""; };
+ 243652F429F3516400FD902A /* HomeScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; };
+ 243652F529F3516400FD902A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 24B0FC5A29F826B300434F1C /* iosApp.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iosApp.xcconfig; sourceTree = ""; };
+ 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 7555FF78242A565900829871 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1DBB948028897D4700E79663 /* Sentry in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 058557D7273AAEEB004C7B11 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 7555FF72242A565900829871 = {
+ isa = PBXGroup;
+ children = (
+ 7555FF7D242A565900829871 /* iosApp */,
+ 7555FF7C242A565900829871 /* Products */,
+ 7555FFB0242A642200829871 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 7555FF7C242A565900829871 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7555FF7B242A565900829871 /* iosApp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7555FF7D242A565900829871 /* iosApp */ = {
+ isa = PBXGroup;
+ children = (
+ 243652EC29F34FBF00FD902A /* sentry.png */,
+ 058557BA273AAA24004C7B11 /* Assets.xcassets */,
+ 243652F529F3516400FD902A /* AppDelegate.swift */,
+ 243652F429F3516400FD902A /* HomeScreen.swift */,
+ 243652F229F3516400FD902A /* Koin.swift */,
+ 243652F329F3516400FD902A /* LoginScreen.swift */,
+ 7555FF8C242A565B00829871 /* Info.plist */,
+ 058557D7273AAEEB004C7B11 /* Preview Content */,
+ 24B0FC5A29F826B300434F1C /* iosApp.xcconfig */,
+ );
+ path = iosApp;
+ sourceTree = "";
+ };
+ 7555FFB0242A642200829871 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 7555FF7A242A565900829871 /* iosApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
+ buildPhases = (
+ 7555FFB5242A651A00829871 /* Run Script */,
+ 7555FF77242A565900829871 /* Sources */,
+ 7555FF78242A565900829871 /* Frameworks */,
+ 7555FF79242A565900829871 /* Resources */,
+ 7555FFB4242A642300829871 /* Embed Frameworks */,
+ 24B0FC5B29F826D700434F1C /* Upload Debug Symbols to Sentry */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = iosApp;
+ packageProductDependencies = (
+ 1DBB947F28897D4700E79663 /* Sentry */,
+ );
+ productName = NSExceptionKtSample;
+ productReference = 7555FF7B242A565900829871 /* iosApp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 7555FF73242A565900829871 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1130;
+ LastUpgradeCheck = 1130;
+ ORGANIZATIONNAME = orgName;
+ TargetAttributes = {
+ 7555FF7A242A565900829871 = {
+ CreatedOnToolsVersion = 11.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 7555FF72242A565900829871;
+ packageReferences = (
+ 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
+ );
+ productRefGroup = 7555FF7C242A565900829871 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 7555FF7A242A565900829871 /* iosApp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 7555FF79242A565900829871 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
+ 243652ED29F34FBF00FD902A /* sentry.png in Resources */,
+ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 24B0FC5B29F826D700434F1C /* Upload Debug Symbols to Sentry */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
+ );
+ name = "Upload Debug Symbols to Sentry";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if which sentry-cli >/dev/null; then\nERROR=$(sentry-cli upload-dif \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"warning: sentry-cli - $ERROR\"\nfi\nelse\necho \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n";
+ };
+ 7555FFB5242A651A00829871 /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "../../gradlew :sentry-samples:kmp-app-mvvm-di:shared:embedAndSignAppleFrameworkForXcode\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 7555FF77242A565900829871 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 243652F929F3516400FD902A /* AppDelegate.swift in Sources */,
+ 243652F629F3516400FD902A /* Koin.swift in Sources */,
+ 243652F829F3516400FD902A /* HomeScreen.swift in Sources */,
+ 243652F729F3516400FD902A /* LoginScreen.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 7555FFA3242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5A29F826B300434F1C /* iosApp.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 7555FFA4242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5A29F826B300434F1C /* iosApp.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 7555FFA6242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ INFOPLIST_FILE = iosApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ shared,
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.kotlin.multiplatform.samples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7555FFA7242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ INFOPLIST_FILE = iosApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ shared,
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.kotlin.multiplatform.samples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA3242A565B00829871 /* Debug */,
+ 7555FFA4242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA6242A565B00829871 /* Debug */,
+ 7555FFA7242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git";
+ requirement = {
+ kind = exactVersion;
+ version = 8.4.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 1DBB947F28897D4700E79663 /* Sentry */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
+ productName = Sentry;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 7555FF73242A565900829871 /* Project object */;
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/sentry-samples/kmp-app/iosApp/iosApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
similarity index 100%
rename from sentry-samples/kmp-app/iosApp/iosApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
rename to sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 00000000..11d4b194
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,14 @@
+{
+ "pins" : [
+ {
+ "identity" : "sentry-cocoa",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/getsentry/sentry-cocoa.git",
+ "state" : {
+ "revision" : "92a6472efc750a4e18bdee21c204942ab0bc4dcd",
+ "version" : "8.4.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme
new file mode 100644
index 00000000..cfbcecae
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift b/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift
new file mode 100644
index 00000000..f863295a
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift
@@ -0,0 +1,24 @@
+import SwiftUI
+import shared
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+ var window: UIWindow?
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions
+ launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+
+ startKoin()
+
+ SentrySetupKt.start()
+
+ let viewController = UIHostingController(rootView: HomeScreen())
+
+ self.window = UIWindow(frame: UIScreen.main.bounds)
+ self.window?.rootViewController = viewController
+ self.window?.makeKeyAndVisible()
+
+ return true
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..ee7e3ca0
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..fb88a396
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/Contents.json b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..4aa7c535
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/HomeScreen.swift b/sentry-samples/kmp-app-mvvm-di/iosApp/HomeScreen.swift
new file mode 100644
index 00000000..aa71784f
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/HomeScreen.swift
@@ -0,0 +1,50 @@
+import SwiftUI
+import shared
+
+struct HomeScreen: View {
+ @State private var presentLoginScreen = true
+ @State private var showDialog = false
+ @State private var dialogMessage = ""
+
+ @Environment(\.presentationMode) var presentationMode
+ private var viewModel = KotlinDependencies.shared.getHomeViewModel()
+
+ var body: some View {
+ VStack(alignment: .center, spacing: 16) {
+ Text("Welcome!")
+ .font(.title)
+ .fontWeight(.bold)
+ .padding(.bottom, 16)
+ Text(viewModel.homeText)
+ .font(.body)
+ .fontWeight(.regular)
+ .padding(.bottom, 16)
+ Button(action: {
+ viewModel.updateProfileWithErr()
+ showDialog = true
+ dialogMessage = "An error occurred during profile update"
+ }) {
+ Text("Update Profile (error)")
+ }
+ Button(action: { presentLoginScreen = true }) {
+ Text("Log Out")
+ }
+ .sheet(isPresented: $presentLoginScreen) {
+ LoginScreen()
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.top, 16)
+ }
+ .padding(16)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .alert(isPresented: $showDialog) {
+ Alert(
+ title: Text("Error"),
+ message: Text(dialogMessage),
+ dismissButton: .default(Text("OK")) {
+ showDialog = false
+ }
+ )
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Info.plist b/sentry-samples/kmp-app-mvvm-di/iosApp/Info.plist
new file mode 100644
index 00000000..b9760e8d
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Info.plist
@@ -0,0 +1,55 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ SENTRY_DSN
+ $(SENTRY_DSN)
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+
+ UILaunchScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ bugsnag
+
+ apiKey
+ $(BUGSNAG_API_KEY)
+
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Koin.swift b/sentry-samples/kmp-app-mvvm-di/iosApp/Koin.swift
new file mode 100644
index 00000000..5c01cc17
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Koin.swift
@@ -0,0 +1,12 @@
+import Foundation
+import shared
+
+func startKoin() {
+ let koinApplication = KoinIOSKt.doInitKoinIos()
+ _koin = koinApplication.koin
+}
+
+private var _koin: Koin_coreKoin?
+var koin: Koin_coreKoin {
+ return _koin!
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/LoginScreen.swift b/sentry-samples/kmp-app-mvvm-di/iosApp/LoginScreen.swift
new file mode 100644
index 00000000..f2699fe7
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/LoginScreen.swift
@@ -0,0 +1,70 @@
+import SwiftUI
+import shared
+
+struct LoginScreen: View {
+ @State private var email = "user@sentrydemo.com"
+ @State private var password = "randompassword"
+ @State private var enableLoginError = true
+ @State private var showDialog = false
+ @State private var dialogMessage = ""
+ @State private var isLoggedIn = false
+ @Environment(\.presentationMode) var presentationMode
+
+ private var viewModel = KotlinDependencies.shared.getAuthenticationViewModel()
+
+ var body: some View {
+ VStack(alignment: .center, spacing: 16) {
+ Text("Sentry Demo")
+ .font(.title)
+ .fontWeight(.bold)
+ .padding(.bottom, 16)
+ TextField("Email", text: $email)
+ .keyboardType(.emailAddress)
+ .textFieldStyle(RoundedBorderTextFieldStyle())
+ SecureField("Password", text: $password)
+ .textFieldStyle(RoundedBorderTextFieldStyle())
+ .padding(.top, 16)
+ Button(action: {
+ let succeded = viewModel.login(withError: enableLoginError)
+ if succeded {
+ // present home screen
+ presentationMode.wrappedValue.dismiss()
+ } else {
+ dialogMessage = "An error occurred during login"
+ showDialog = true
+ }
+ }) {
+ if (enableLoginError) {
+ Text("Log In (error)")
+ } else {
+ Text("Log In")
+ }
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.top, 16)
+ Button(action: {
+ viewModel.signUp()
+ }) {
+ Text("Sign up (crash)")
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.top, 16)
+ HStack {
+ Text("Enable login error")
+ Toggle("", isOn: $enableLoginError)
+ .padding(.trailing, 8)
+ }
+ }
+ .padding(16)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .alert(isPresented: $showDialog) {
+ Alert(
+ title: Text("Error"),
+ message: Text(dialogMessage),
+ dismissButton: .default(Text("OK")) {
+ showDialog = false
+ }
+ )
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/sentry-samples/kmp-app-mvvm-di/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 00000000..4aa7c535
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/sentry.png b/sentry-samples/kmp-app-mvvm-di/iosApp/sentry.png
new file mode 100644
index 00000000..8595392b
Binary files /dev/null and b/sentry-samples/kmp-app-mvvm-di/iosApp/sentry.png differ
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/build.gradle.kts b/sentry-samples/kmp-app-mvvm-di/shared/build.gradle.kts
new file mode 100644
index 00000000..f906eba3
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/build.gradle.kts
@@ -0,0 +1,80 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.android.library")
+}
+
+kotlin {
+ android()
+ listOf(
+ iosX64(),
+ iosArm64(),
+ iosSimulatorArm64()
+ ).forEach {
+ it.binaries.framework {
+ baseName = "shared"
+ isStatic = true
+ export(project(":sentry-kotlin-multiplatform"))
+ }
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ api(project(":sentry-kotlin-multiplatform"))
+ implementation("io.insert-koin:koin-core:3.4.0")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
+ }
+ }
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test"))
+ }
+ }
+ val androidMain by getting {
+ dependencies {
+ implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
+ }
+ }
+ val iosX64Main by getting
+ val iosArm64Main by getting
+ val iosSimulatorArm64Main by getting
+ val iosMain by creating {
+ dependsOn(commonMain)
+ iosX64Main.dependsOn(this)
+ iosArm64Main.dependsOn(this)
+ iosSimulatorArm64Main.dependsOn(this)
+ }
+ val iosX64Test by getting
+ val iosArm64Test by getting
+ val iosSimulatorArm64Test by getting
+ val iosTest by creating {
+ dependsOn(commonTest)
+ iosX64Test.dependsOn(this)
+ iosArm64Test.dependsOn(this)
+ iosSimulatorArm64Test.dependsOn(this)
+ }
+ }
+}
+
+android {
+ compileSdk = Config.Android.compileSdkVersion
+ sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ defaultConfig {
+ minSdk = Config.Android.minSdkVersion
+ }
+}
+
+// Workaround for KotlinMetadata tasks failing when using ./gradlew build.
+// However, running this sample on iOS and Android simulators remains unaffected.
+afterEvaluate {
+ afterEvaluate {
+ tasks.configureEach {
+ if (
+ name.startsWith("compile") &&
+ name.endsWith("KotlinMetadata")
+ ) {
+ enabled = false
+ }
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/shared.podspec b/sentry-samples/kmp-app-mvvm-di/shared/shared.podspec
new file mode 100644
index 00000000..97306a57
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/shared.podspec
@@ -0,0 +1,39 @@
+Pod::Spec.new do |spec|
+ spec.name = 'shared'
+ spec.version = '0.1.0'
+ spec.homepage = ''
+ spec.source = { :http=> ''}
+ spec.authors = ''
+ spec.license = ''
+ spec.summary = ''
+ spec.vendored_frameworks = 'build/cocoapods/framework/shared.framework'
+ spec.libraries = 'c++'
+
+
+
+ spec.pod_target_xcconfig = {
+ 'KOTLIN_PROJECT_PATH' => ':sentry-samples:kmp-app-mvvm-di:shared',
+ 'PRODUCT_MODULE_NAME' => 'shared',
+ }
+
+ spec.script_phases = [
+ {
+ :name => 'Build shared',
+ :execution_position => :before_compile,
+ :shell_path => '/bin/sh',
+ :script => <<-SCRIPT
+ if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then
+ echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\""
+ exit 0
+ fi
+ set -ev
+ REPO_ROOT="$PODS_TARGET_SRCROOT"
+ "$REPO_ROOT/../../../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \
+ -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \
+ -Pkotlin.native.cocoapods.archs="$ARCHS" \
+ -Pkotlin.native.cocoapods.configuration="$CONFIGURATION"
+ SCRIPT
+ }
+ ]
+
+end
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/AndroidManifest.xml b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/AndroidManifest.xml
new file mode 100644
index 00000000..a723fcb0
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/KoinAndroid.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/KoinAndroid.kt
new file mode 100644
index 00000000..d91c9842
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/KoinAndroid.kt
@@ -0,0 +1,10 @@
+package sentry.kmp.demo
+
+import org.koin.dsl.module
+import sentry.kmp.demo.models.AuthenticationViewModel
+import sentry.kmp.demo.models.HomeViewModel
+
+actual val platformModule = module {
+ single { AuthenticationViewModel() }
+ single { HomeViewModel() }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/models/ViewModel.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/models/ViewModel.kt
new file mode 100644
index 00000000..bbb35589
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/androidMain/kotlin/sentry/kmp/demo/models/ViewModel.kt
@@ -0,0 +1,13 @@
+package sentry.kmp.demo.models
+
+import kotlinx.coroutines.CoroutineScope
+import androidx.lifecycle.ViewModel as AndroidXViewModel
+import androidx.lifecycle.viewModelScope as androidXViewModelScope
+
+actual abstract class ViewModel actual constructor() : AndroidXViewModel() {
+ actual val viewModelScope: CoroutineScope = androidXViewModelScope
+
+ actual override fun onCleared() {
+ super.onCleared()
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/Koin.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/Koin.kt
new file mode 100644
index 00000000..4b30583d
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/Koin.kt
@@ -0,0 +1,17 @@
+package sentry.kmp.demo
+
+import org.koin.core.KoinApplication
+import org.koin.core.context.startKoin
+import org.koin.core.module.Module
+
+fun initKoin(appModule: Module): KoinApplication {
+ val koinApplication = startKoin {
+ modules(
+ appModule,
+ platformModule
+ )
+ }
+ return koinApplication
+}
+
+expect val platformModule: Module
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/AuthenticationViewModel.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/AuthenticationViewModel.kt
new file mode 100644
index 00000000..b0d3f153
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/AuthenticationViewModel.kt
@@ -0,0 +1,35 @@
+package sentry.kmp.demo.models
+
+import io.sentry.kotlin.multiplatform.Sentry
+import io.sentry.kotlin.multiplatform.SentryLevel
+import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
+
+class LoginException(message: String) : Exception(message)
+
+class AuthenticationViewModel : ViewModel() {
+
+ fun login(withError: Boolean): Boolean {
+ return if (withError) {
+ try {
+ throw LoginException("Error logging in")
+ } catch (exception: Exception) {
+ Sentry.captureException(exception) {
+ val breadcrumb = Breadcrumb.error("Error during login").apply {
+ setData("touch event", "on login")
+ }
+ it.addBreadcrumb(breadcrumb)
+ it.setContext("Login", "Failed due to ...")
+ it.setTag("login", "failed")
+ it.level = SentryLevel.ERROR
+ }
+ false
+ }
+ } else {
+ true
+ }
+ }
+
+ fun signUp() {
+ Sentry.crash()
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/HomeViewModel.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/HomeViewModel.kt
new file mode 100644
index 00000000..712a4a9d
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/HomeViewModel.kt
@@ -0,0 +1,20 @@
+package sentry.kmp.demo.models
+
+import io.sentry.kotlin.multiplatform.ScopeCallback
+import io.sentry.kotlin.multiplatform.Sentry
+
+class ProfileUpdateException(message: String) : Exception(message)
+
+class HomeViewModel : ViewModel() {
+
+ val homeText =
+ "This screen will show you how we can change the scope of each Sentry event via captureException!"
+
+ private val scopeConfig: ScopeCallback = {
+ it.setContext("home", "logged in")
+ }
+
+ fun updateProfileWithErr() {
+ Sentry.captureException(ProfileUpdateException("Error updating profile"), scopeConfig)
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/ViewModel.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/ViewModel.kt
new file mode 100644
index 00000000..82b5aa83
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/models/ViewModel.kt
@@ -0,0 +1,8 @@
+package sentry.kmp.demo.models
+
+import kotlinx.coroutines.CoroutineScope
+
+expect abstract class ViewModel() {
+ val viewModelScope: CoroutineScope
+ protected open fun onCleared()
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt
new file mode 100644
index 00000000..920aa3a2
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt
@@ -0,0 +1,62 @@
+package sentry.kmp.demo.sentry
+
+import io.sentry.kotlin.multiplatform.Attachment
+import io.sentry.kotlin.multiplatform.Context
+import io.sentry.kotlin.multiplatform.OptionsConfiguration
+import io.sentry.kotlin.multiplatform.Sentry
+import kotlin.experimental.ExperimentalObjCRefinement
+import kotlin.native.HiddenFromObjC
+
+/** Shared options configuration */
+private val optionsConfiguration: OptionsConfiguration = {
+ it.dsn = "https://83f281ded2844eda83a8a413b080dbb9@o447951.ingest.sentry.io/5903800"
+ it.attachStackTrace = true
+ it.attachThreads = true
+ it.attachScreenshot = true
+ it.release = "kmp-release@0.0.1"
+ it.beforeSend = { event ->
+ if (event.environment == "test") {
+ null
+ } else {
+ event
+ }
+ }
+ it.beforeBreadcrumb = { breadcrumb ->
+ breadcrumb.message = "Add message before every breadcrumb"
+ breadcrumb
+ }
+}
+
+/**
+ * Initializes Sentry with given options.
+ * Make sure to hook this into your native platforms as early as possible
+ */
+@OptIn(ExperimentalObjCRefinement::class)
+@HiddenFromObjC
+fun initSentry(context: Context) {
+ Sentry.init(context, optionsConfiguration)
+ configureSentryScope()
+}
+
+/**
+ * Convenience initializer for Cocoa targets.
+ * Kotlin -> ObjC doesn't support default parameters (yet).
+ */
+fun start() {
+ Sentry.init(optionsConfiguration)
+ configureSentryScope()
+}
+
+/** Configure scope applicable to all platforms */
+private fun configureSentryScope() {
+ Sentry.configureScope {
+ it.setContext("Custom Context", "Shared Context")
+ it.setTag("custom-tag", "from shared code")
+ it.addAttachment(
+ Attachment(
+ "This is a shared text attachment".encodeToByteArray(),
+ "shared.log"
+ )
+ )
+ }
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/KoinIOS.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/KoinIOS.kt
new file mode 100644
index 00000000..7cd50c18
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/KoinIOS.kt
@@ -0,0 +1,22 @@
+package sentry.kmp.demo
+
+import org.koin.core.KoinApplication
+import org.koin.core.component.KoinComponent
+import org.koin.dsl.module
+import sentry.kmp.demo.models.AuthenticationViewModel
+import sentry.kmp.demo.models.HomeViewModel
+
+fun initKoinIos(): KoinApplication = initKoin(
+ module { }
+)
+
+actual val platformModule = module {
+ single { AuthenticationViewModel() }
+ single { HomeViewModel() }
+}
+
+@Suppress("unused") // Called from Swift
+object KotlinDependencies : KoinComponent {
+ fun getAuthenticationViewModel() = getKoin().get()
+ fun getHomeViewModel() = getKoin().get()
+}
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/models/ViewModel.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/models/ViewModel.kt
new file mode 100644
index 00000000..e157e877
--- /dev/null
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/iosMain/kotlin/sentry.kmp.demo/models/ViewModel.kt
@@ -0,0 +1,30 @@
+package sentry.kmp.demo.models
+
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+/**
+ * Base class that provides a Kotlin/Native equivalent to the AndroidX `ViewModel`. In particular, this provides
+ * a [CoroutineScope][kotlinx.coroutines.CoroutineScope] which uses [Dispatchers.Main][kotlinx.coroutines.Dispatchers.Main]
+ * and can be tied into an arbitrary lifecycle by calling [clear] at the appropriate time.
+ */
+actual abstract class ViewModel {
+
+ actual val viewModelScope = MainScope()
+
+ /**
+ * Override this to do any cleanup immediately before the internal [CoroutineScope][kotlinx.coroutines.CoroutineScope]
+ * is cancelled in [clear]
+ */
+ protected actual open fun onCleared() {
+ }
+
+ /**
+ * Cancels the internal [CoroutineScope][kotlinx.coroutines.CoroutineScope]. After this is called, the ViewModel should
+ * no longer be used.
+ */
+ fun clear() {
+ onCleared()
+ viewModelScope.cancel()
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/androidApp/build.gradle.kts b/sentry-samples/kmp-app-spm/androidApp/build.gradle.kts
new file mode 100644
index 00000000..3e70dcaf
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/build.gradle.kts
@@ -0,0 +1,51 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("io.sentry.android.gradle") version "3.5.0"
+}
+
+android {
+ compileSdk = Config.Android.compileSdkVersion
+ defaultConfig {
+ applicationId = "sample.kmp.app.android"
+ minSdk = Config.Android.minSdkVersion
+ targetSdk = Config.Android.targetSdkVersion
+ versionCode = 1
+ versionName = "1.0"
+ }
+ signingConfigs {
+ create("release") {
+ storeFile = file("sentry.keystore")
+ storePassword = "sentry"
+ keyAlias = "Sentry Android Key"
+ keyPassword = "sentry"
+ }
+ }
+ buildTypes {
+ getByName("release") {
+ isDefault = true
+ isMinifyEnabled = true
+ proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
+ signingConfig = signingConfigs.getByName("release")
+ }
+ }
+}
+
+dependencies {
+ implementation(rootProject.project(":sentry-samples:kmp-app-spm:shared"))
+ implementation("com.google.android.material:material:1.6.1")
+ implementation("androidx.appcompat:appcompat:1.4.2")
+ implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+}
+
+// Prevent Sentry from being included in the Android app through the AGP.
+configurations {
+ compileOnly {
+ exclude(group = "io.sentry", module = "sentry")
+ exclude(group = "io.sentry", module = "sentry-android")
+ }
+}
+
+sentry {
+ autoUploadProguardMapping.set(false)
+}
diff --git a/sentry-samples/kmp-app-spm/androidApp/sentry.keystore b/sentry-samples/kmp-app-spm/androidApp/sentry.keystore
new file mode 100644
index 00000000..3d6ddc7c
Binary files /dev/null and b/sentry-samples/kmp-app-spm/androidApp/sentry.keystore differ
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/AndroidManifest.xml b/sentry-samples/kmp-app-spm/androidApp/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..bdfc10f4
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt b/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
new file mode 100644
index 00000000..1b693381
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
@@ -0,0 +1,76 @@
+package sample.kmp.app.android
+
+import android.app.Application
+import android.os.Bundle
+import android.widget.Button
+import androidx.appcompat.app.AppCompatActivity
+import io.sentry.kotlin.multiplatform.Attachment
+import io.sentry.kotlin.multiplatform.Sentry
+import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
+import sample.kmp.app.LoginImpl
+import sample.kmp.app.Platform
+import sample.kmp.app.configureSentryScope
+import sample.kmp.app.initializeSentry
+import java.io.FileOutputStream
+import java.io.IOException
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ val captureMessageBtn: Button = findViewById(R.id.captureMessageBtn)
+ val captureExceptionBtn: Button = findViewById(R.id.captureExceptionBtn)
+ val captureHardCrashBtn: Button = findViewById(R.id.captureHardCrash)
+
+ captureMessageBtn.setOnClickListener {
+ Sentry.captureMessage("From KMP Sample App: " + Platform().platform)
+ }
+
+ captureExceptionBtn.setOnClickListener {
+ LoginImpl.login("MyUsername")
+ }
+
+ captureHardCrashBtn.setOnClickListener {
+ LoginImpl.login()
+ }
+ }
+}
+
+class SentryApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+
+ // Initialize Sentry using shared code
+ initializeSentry(this)
+
+ // Shared scope across all platforms
+ configureSentryScope()
+
+ val imageFile = applicationContext.getFileStreamPath("sentry.png")
+ try {
+ applicationContext.resources.openRawResource(R.raw.sentry).use { inputStream ->
+ FileOutputStream(imageFile).use { outputStream ->
+ val bytes = ByteArray(1024)
+ while (inputStream.read(bytes) !== -1) {
+ // To keep the sample code simple this happens on the main thread. Don't do this in a
+ // real app.
+ outputStream.write(bytes)
+ }
+ outputStream.flush()
+ }
+ }
+ } catch (e: IOException) {
+ Sentry.captureException(e)
+ }
+
+ val imageAttachment = Attachment(imageFile.getAbsolutePath(), "sentry.png", "image/png")
+
+ // Add platform specific scope in addition to the shared scope
+ Sentry.configureScope {
+ it.setContext("Android Context", mapOf("context1" to 12, "context2" to false))
+ it.addBreadcrumb(Breadcrumb.debug("initialized Sentry on Android"))
+ it.addAttachment(imageAttachment)
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/res/layout/activity_main.xml b/sentry-samples/kmp-app-spm/androidApp/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..024e7343
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/res/raw/sentry.png b/sentry-samples/kmp-app-spm/androidApp/src/main/res/raw/sentry.png
new file mode 100644
index 00000000..2225be47
Binary files /dev/null and b/sentry-samples/kmp-app-spm/androidApp/src/main/res/raw/sentry.png differ
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/colors.xml b/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/colors.xml
new file mode 100644
index 00000000..4faecfa8
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/styles.xml b/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/styles.xml
new file mode 100644
index 00000000..1971a0a0
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/res/values/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/desktopApp/build.gradle.kts b/sentry-samples/kmp-app-spm/desktopApp/build.gradle.kts
new file mode 100644
index 00000000..0e52becb
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/desktopApp/build.gradle.kts
@@ -0,0 +1,44 @@
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+
+plugins {
+ kotlin("multiplatform")
+ id("org.jetbrains.compose")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ google()
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+}
+
+kotlin {
+ jvm {
+ compilations.all {
+ kotlinOptions.jvmTarget = "11"
+ }
+ withJava()
+ }
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ implementation(rootProject.project(":sentry-samples:kmp-app-cocoapods:shared"))
+ implementation(compose.desktop.currentOs)
+ }
+ }
+ val jvmTest by getting
+ }
+}
+
+compose.desktop {
+ application {
+ mainClass = "MainKt"
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "demo"
+ packageVersion = "1.0.0"
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt b/sentry-samples/kmp-app-spm/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
similarity index 95%
rename from sentry-samples/kmp-app/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
rename to sentry-samples/kmp-app-spm/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
index cb574189..987c29d5 100644
--- a/sentry-samples/kmp-app/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
+++ b/sentry-samples/kmp-app-spm/desktopApp/src/jvmMain/kotlin/sample.kmp.app.desktop/Main.kt
@@ -49,7 +49,7 @@ fun App() {
}
fun main() = application {
- Window(onCloseRequest = ::exitApplication) {
+ Window(onCloseRequest = ::exitApplication, title = "Jetpack Compose Desktop App (SPM Sample Version)") {
// Initialize Sentry using shared code
initializeSentry()
diff --git a/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.pbxproj b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..bbcac866
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.pbxproj
@@ -0,0 +1,438 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 52;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
+ 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
+ 1DBB948028897D4700E79663 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 1DBB947F28897D4700E79663 /* Sentry */; };
+ 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
+ 243652EF29F34FF500FD902A /* sentry.png in Resources */ = {isa = PBXBuildFile; fileRef = 243652EE29F34FF500FD902A /* sentry.png */; };
+ 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 7555FFB4242A642300829871 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
+ 243652EE29F34FF500FD902A /* sentry.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sentry.png; sourceTree = ""; };
+ 24B0FC5729F821E500434F1C /* iosApp.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = iosApp.xcconfig; sourceTree = ""; };
+ 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 7555FF78242A565900829871 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1DBB948028897D4700E79663 /* Sentry in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 058557D7273AAEEB004C7B11 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 7555FF72242A565900829871 = {
+ isa = PBXGroup;
+ children = (
+ 7555FF7D242A565900829871 /* iosApp */,
+ 7555FF7C242A565900829871 /* Products */,
+ 7555FFB0242A642200829871 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 7555FF7C242A565900829871 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7555FF7B242A565900829871 /* iosApp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7555FF7D242A565900829871 /* iosApp */ = {
+ isa = PBXGroup;
+ children = (
+ 243652EE29F34FF500FD902A /* sentry.png */,
+ 058557BA273AAA24004C7B11 /* Assets.xcassets */,
+ 7555FF82242A565900829871 /* ContentView.swift */,
+ 7555FF8C242A565B00829871 /* Info.plist */,
+ 2152FB032600AC8F00CF470E /* iOSApp.swift */,
+ 058557D7273AAEEB004C7B11 /* Preview Content */,
+ 24B0FC5729F821E500434F1C /* iosApp.xcconfig */,
+ );
+ path = iosApp;
+ sourceTree = "";
+ };
+ 7555FFB0242A642200829871 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 7555FF7A242A565900829871 /* iosApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
+ buildPhases = (
+ 7555FFB5242A651A00829871 /* ShellScript */,
+ 7555FF77242A565900829871 /* Sources */,
+ 7555FF78242A565900829871 /* Frameworks */,
+ 7555FF79242A565900829871 /* Resources */,
+ 7555FFB4242A642300829871 /* Embed Frameworks */,
+ 24B0FC5429F8202E00434F1C /* Upload Debug Symbols to Sentry */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = iosApp;
+ packageProductDependencies = (
+ 1DBB947F28897D4700E79663 /* Sentry */,
+ );
+ productName = NSExceptionKtSample;
+ productReference = 7555FF7B242A565900829871 /* iosApp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 7555FF73242A565900829871 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1130;
+ LastUpgradeCheck = 1130;
+ ORGANIZATIONNAME = orgName;
+ TargetAttributes = {
+ 7555FF7A242A565900829871 = {
+ CreatedOnToolsVersion = 11.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 7555FF72242A565900829871;
+ packageReferences = (
+ 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
+ );
+ productRefGroup = 7555FF7C242A565900829871 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 7555FF7A242A565900829871 /* iosApp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 7555FF79242A565900829871 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
+ 243652EF29F34FF500FD902A /* sentry.png in Resources */,
+ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 24B0FC5429F8202E00434F1C /* Upload Debug Symbols to Sentry */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
+ );
+ name = "Upload Debug Symbols to Sentry";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if which sentry-cli >/dev/null; then\nERROR=$(sentry-cli upload-dif \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\nif [ ! $? -eq 0 ]; then\necho \"warning: sentry-cli - $ERROR\"\nfi\nelse\necho \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n";
+ };
+ 7555FFB5242A651A00829871 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "../../gradlew :sentry-samples:kmp-app-spm:shared:embedAndSignAppleFrameworkForXcode\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 7555FF77242A565900829871 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
+ 7555FF83242A565900829871 /* ContentView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 7555FFA3242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5729F821E500434F1C /* iosApp.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 7555FFA4242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 24B0FC5729F821E500434F1C /* iosApp.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 7555FFA6242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ INFOPLIST_FILE = iosApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ shared,
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.kotlin.multiplatform.samples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7555FFA7242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ INFOPLIST_FILE = iosApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ shared,
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.kotlin.multiplatform.samples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA3242A565B00829871 /* Debug */,
+ 7555FFA4242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA6242A565B00829871 /* Debug */,
+ 7555FFA7242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git";
+ requirement = {
+ kind = exactVersion;
+ version = 8.4.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 1DBB947F28897D4700E79663 /* Sentry */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 1DBB947E28897D4700E79663 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
+ productName = Sentry;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 7555FF73242A565900829871 /* Project object */;
+}
diff --git a/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 00000000..11d4b194
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,14 @@
+{
+ "pins" : [
+ {
+ "identity" : "sentry-cocoa",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/getsentry/sentry-cocoa.git",
+ "state" : {
+ "revision" : "92a6472efc750a4e18bdee21c204942ab0bc4dcd",
+ "version" : "8.4.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/sentry-samples/kmp-app-spm/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme
new file mode 100644
index 00000000..cfbcecae
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..ee7e3ca0
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..fb88a396
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/Contents.json b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..4aa7c535
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app-spm/iosApp/ContentView.swift b/sentry-samples/kmp-app-spm/iosApp/ContentView.swift
new file mode 100644
index 00000000..7e383a7a
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/ContentView.swift
@@ -0,0 +1,28 @@
+import SwiftUI
+import shared
+
+struct ContentView: View {
+ var body: some View {
+ Text("KMP Sample App " + Platform().platform)
+ VStack() {
+ Button("Capture Message") {
+ Sentry.shared.captureMessage(message: "From KMP Sample App " + Platform().platform)
+ }
+ Button("Capture Exception") {
+ LoginImpl().login(username: "MyUsername")
+ }
+ Button("Capture Http Client Error") {
+ HttpClientKt.captureHttpClientError()
+ }
+ Button("Hard Crash") {
+ LoginImpl().login(username: nil)
+ }
+ }
+ }
+}
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/iosApp/Info.plist b/sentry-samples/kmp-app-spm/iosApp/Info.plist
new file mode 100644
index 00000000..b9760e8d
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/Info.plist
@@ -0,0 +1,55 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ SENTRY_DSN
+ $(SENTRY_DSN)
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+
+ UILaunchScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ bugsnag
+
+ apiKey
+ $(BUGSNAG_API_KEY)
+
+
+
diff --git a/sentry-samples/kmp-app-spm/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/sentry-samples/kmp-app-spm/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 00000000..4aa7c535
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
\ No newline at end of file
diff --git a/sentry-samples/kmp-app/iosApp/iosApp/iOSApp.swift b/sentry-samples/kmp-app-spm/iosApp/iOSApp.swift
similarity index 91%
rename from sentry-samples/kmp-app/iosApp/iosApp/iOSApp.swift
rename to sentry-samples/kmp-app-spm/iosApp/iOSApp.swift
index 87fa7450..a16a2a13 100644
--- a/sentry-samples/kmp-app/iosApp/iosApp/iOSApp.swift
+++ b/sentry-samples/kmp-app-spm/iosApp/iOSApp.swift
@@ -7,10 +7,10 @@ struct iOSApp: App {
init() {
// Initialize Sentry using shared code
- AppSetupKt.initializeSentry()
+ SentrySetupKt.initializeSentry()
// Shared scope across all platforms
- AppSetupKt.configureSentryScope()
+ SentrySetupKt.configureSentryScope()
// Add platform specific scope in addition to the shared scope
sentry.configureScope { scope in
diff --git a/sentry-samples/kmp-app-spm/iosApp/sentry.png b/sentry-samples/kmp-app-spm/iosApp/sentry.png
new file mode 100644
index 00000000..8595392b
Binary files /dev/null and b/sentry-samples/kmp-app-spm/iosApp/sentry.png differ
diff --git a/sentry-samples/kmp-app-spm/shared/build.gradle.kts b/sentry-samples/kmp-app-spm/shared/build.gradle.kts
new file mode 100644
index 00000000..ccfd58e5
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/build.gradle.kts
@@ -0,0 +1,61 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.android.library")
+}
+
+kotlin {
+ android()
+ jvm()
+ listOf(
+ iosX64(),
+ iosArm64(),
+ iosSimulatorArm64()
+ ).forEach {
+ it.binaries.framework {
+ baseName = "shared"
+ isStatic = true
+ export(project(":sentry-kotlin-multiplatform"))
+ }
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ api(project(":sentry-kotlin-multiplatform"))
+ }
+ }
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test"))
+ }
+ }
+ val jvmMain by getting
+ val androidMain by getting
+ val iosX64Main by getting
+ val iosArm64Main by getting
+ val iosSimulatorArm64Main by getting
+ val iosMain by creating {
+ dependsOn(commonMain)
+ iosX64Main.dependsOn(this)
+ iosArm64Main.dependsOn(this)
+ iosSimulatorArm64Main.dependsOn(this)
+ }
+ val iosX64Test by getting
+ val iosArm64Test by getting
+ val iosSimulatorArm64Test by getting
+ val iosTest by creating {
+ dependsOn(commonTest)
+ iosX64Test.dependsOn(this)
+ iosArm64Test.dependsOn(this)
+ iosSimulatorArm64Test.dependsOn(this)
+ }
+ }
+}
+
+android {
+ compileSdk = Config.Android.compileSdkVersion
+ sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ defaultConfig {
+ minSdk = Config.Android.minSdkVersion
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/androidMain/AndroidManifest.xml b/sentry-samples/kmp-app-spm/shared/src/androidMain/AndroidManifest.xml
new file mode 100644
index 00000000..8bca5cc4
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/sentry-samples/kmp-app-spm/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt b/sentry-samples/kmp-app-spm/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt
new file mode 100644
index 00000000..c371e4ef
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/androidMain/kotlin/sample/kmp/app/Platform.kt
@@ -0,0 +1,5 @@
+package sample.kmp.app
+
+actual class Platform actual constructor() {
+ actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt
new file mode 100644
index 00000000..fddd8726
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/LoginImpl.kt
@@ -0,0 +1,51 @@
+package sample.kmp.app
+
+import io.sentry.kotlin.multiplatform.Sentry
+import io.sentry.kotlin.multiplatform.SentryLevel
+import io.sentry.kotlin.multiplatform.protocol.Breadcrumb
+import io.sentry.kotlin.multiplatform.protocol.User
+import io.sentry.kotlin.multiplatform.protocol.UserFeedback
+
+class InvalidUsernameException(message: String) : Exception(message)
+
+object LoginImpl {
+ /**
+ * login() throws a either checked InvalidUsernameException
+ * or an IllegalArgumentException that crashes the app.
+ *
+ */
+ fun login(username: String? = null) {
+ try {
+ validateUsername(username)
+ } catch (exception: InvalidUsernameException) {
+ val sentryId = Sentry.captureException(exception) {
+ val breadcrumb = Breadcrumb.debug("this is a test breadcrumb")
+ breadcrumb.setData("touch event", "on login")
+ it.addBreadcrumb(breadcrumb)
+ it.setContext("Login", "Failed with Invalid Username")
+ it.setTag("login", "failed auth")
+ it.level = SentryLevel.WARNING
+ val user = User().apply {
+ this.username = "John Doe"
+ this.email = "john@doe.com"
+ }
+ it.user = user
+ }
+ val userFeedback = UserFeedback(sentryId).apply {
+ name = "John Doe"
+ email = "john@doe.com"
+ comments = "I had an error during login on ${Platform().platform}"
+ }
+ Sentry.captureUserFeedback(userFeedback)
+ } catch (exception: IllegalArgumentException) {
+ throw exception
+ }
+ }
+
+ private fun validateUsername(username: String?) {
+ if (username == null) {
+ throw IllegalArgumentException("Username cannot be null")
+ }
+ throw InvalidUsernameException("Username does not exist")
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt
new file mode 100644
index 00000000..9f081fce
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/Platform.kt
@@ -0,0 +1,5 @@
+package sample.kmp.app
+
+expect class Platform() {
+ val platform: String
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
new file mode 100644
index 00000000..c094d11d
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
@@ -0,0 +1,63 @@
+package sample.kmp.app
+
+import io.sentry.kotlin.multiplatform.Attachment
+import io.sentry.kotlin.multiplatform.Context
+import io.sentry.kotlin.multiplatform.HttpStatusCodeRange
+import io.sentry.kotlin.multiplatform.OptionsConfiguration
+import io.sentry.kotlin.multiplatform.Sentry
+
+/** Configure scope applicable to all platforms */
+fun configureSentryScope() {
+ Sentry.configureScope {
+ it.setContext("Custom Context", "Shared Context")
+ it.setTag("custom-tag", "from shared code")
+ it.addAttachment(
+ Attachment(
+ "This is a shared text attachment".encodeToByteArray(),
+ "shared.log"
+ )
+ )
+ }
+}
+
+/**
+ * Initializes Sentry with given options.
+ * Make sure to hook this into your native platforms as early as possible
+ */
+fun initializeSentry(context: Context) {
+ Sentry.init(context, optionsConfiguration())
+}
+
+/**
+ * Convenience initializer for Cocoa targets.
+ * Kotlin -> ObjC doesn't support default parameters (yet).
+ * Otherwise, you would need to do this: AppSetupKt.initializeSentry(context: nil) in Swift.
+ */
+fun initializeSentry() {
+ Sentry.init(optionsConfiguration())
+}
+
+/** Returns a shared options configuration */
+private fun optionsConfiguration(): OptionsConfiguration {
+ return {
+ it.dsn = "https://83f281ded2844eda83a8a413b080dbb9@o447951.ingest.sentry.io/5903800"
+ it.attachStackTrace = true
+ it.attachThreads = true
+ it.attachScreenshot = true
+ it.attachViewHierarchy = true
+ it.release = "kmp-release@0.0.1"
+ it.failedRequestStatusCodes = listOf(HttpStatusCodeRange(400, 599))
+ it.failedRequestTargets = listOf("httpbin.org")
+ it.beforeBreadcrumb = { breadcrumb ->
+ breadcrumb.message = "Add message before every breadcrumb"
+ breadcrumb
+ }
+ it.beforeSend = { event ->
+ if (event.environment == "test") {
+ null
+ } else {
+ event
+ }
+ }
+ }
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt b/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt
new file mode 100644
index 00000000..b63d03af
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/HttpClient.kt
@@ -0,0 +1,21 @@
+package sample.kmp.app
+
+import platform.Foundation.NSURL
+import platform.Foundation.NSURLRequest
+import platform.Foundation.NSURLSession
+import platform.Foundation.dataTaskWithRequest
+
+@Suppress("Unused") // Called from Swift
+fun captureHttpClientError() {
+ val url = NSURL(string = "https://httpbin.org/status/404")
+ val request = NSURLRequest(uRL = url)
+ NSURLSession.sharedSession.dataTaskWithRequest(request) { data, response, error ->
+ if (error != null) {
+ // handle error
+ println("error: $error")
+ } else {
+ // handle successful response
+ println("response: $response")
+ }
+ }.resume()
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt b/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt
new file mode 100644
index 00000000..d9b1e6c2
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/iosMain/kotlin/sample.kmp.app/Platform.kt
@@ -0,0 +1,7 @@
+package sample.kmp.app
+
+import platform.UIKit.UIDevice
+
+actual class Platform actual constructor() {
+ actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
+}
diff --git a/sentry-samples/kmp-app-spm/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt b/sentry-samples/kmp-app-spm/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt
new file mode 100644
index 00000000..a755bffa
--- /dev/null
+++ b/sentry-samples/kmp-app-spm/shared/src/jvmMain/kotlin/sample/kmp/app/Platform.kt
@@ -0,0 +1,5 @@
+package sample.kmp.app
+
+actual class Platform actual constructor() {
+ actual val platform: String = "JVM Desktop"
+}
diff --git a/sentry-samples/kmp-app/androidApp/build.gradle.kts b/sentry-samples/kmp-app/androidApp/build.gradle.kts
deleted file mode 100644
index a1081d5d..00000000
--- a/sentry-samples/kmp-app/androidApp/build.gradle.kts
+++ /dev/null
@@ -1,27 +0,0 @@
-plugins {
- id("com.android.application")
- kotlin("android")
-}
-
-android {
- compileSdk = Config.Android.compileSdkVersion
- defaultConfig {
- applicationId = "sample.kpm_app.android"
- minSdk = Config.Android.minSdkVersion
- targetSdk = Config.Android.targetSdkVersion
- versionCode = 1
- versionName = "1.0"
- }
- buildTypes {
- getByName("release") {
- isMinifyEnabled = false
- }
- }
-}
-
-dependencies {
- implementation(rootProject.project(":sentry-samples:kmp-app:shared"))
- implementation("com.google.android.material:material:1.6.1")
- implementation("androidx.appcompat:appcompat:1.4.2")
- implementation("androidx.constraintlayout:constraintlayout:2.1.4")
-}
diff --git a/sentry-samples/scripts/prepare-android-build.sh b/sentry-samples/scripts/prepare-android-build.sh
new file mode 100755
index 00000000..ad6e8db6
--- /dev/null
+++ b/sentry-samples/scripts/prepare-android-build.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR/../
+
+function prop {
+ grep "${1}" common-sentry.properties|cut -d'=' -f2
+}
+
+org_slug=$(prop 'org.slug')
+project_slug=$(prop 'project.slug')
+auth_token=$(prop 'auth.token')
+
+sentry_props="defaults.project=$project_slug
+defaults.org=$org_slug
+auth.token=$auth_token"
+proj_root="$PWD"
+
+dirs=$(find "$proj_root" -type f -name "sentry.keystore" -exec dirname {} \; | grep -E 'kmp-app-(cocoapods|spm|mvvm-di)/androidApp' | sort | uniq)
+
+for dir in $dirs; do
+ if [ -d "$dir" ]; then
+ # Create the sentry.properties file in the directory and write the contents to it
+ echo "$sentry_props" > "$dir/sentry.properties"
+ echo "Added sentry.properties to $dir"
+ sed -i '' 's/autoUploadProguardMapping.set(false)/autoUploadProguardMapping.set(true)/g' "$dir"/build.gradle.kts
+ echo "Updated autoUploadProguardMapping to true in $dir"
+ else
+ echo "$dir does not exist"
+ fi
+done
diff --git a/sentry-samples/scripts/prepare-apple-build.sh b/sentry-samples/scripts/prepare-apple-build.sh
new file mode 100755
index 00000000..143dd316
--- /dev/null
+++ b/sentry-samples/scripts/prepare-apple-build.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR/../
+
+function prop {
+ grep "${1}" common-sentry.properties|cut -d'=' -f2
+}
+
+org_slug=$(prop 'org.slug')
+project_slug=$(prop 'project.slug')
+auth_token=$(prop 'auth.token')
+
+sentry_props="SENTRY_ORG = $org_slug
+SENTRY_PROJECT = $project_slug
+SENTRY_AUTH_TOKEN = $auth_token"
+proj_root="$PWD"
+
+dirs=$(find "$proj_root" -type f -name "Info.plist" -exec dirname {} \; | grep -E 'kmp-app-(cocoapods|spm|mvvm-di)/iosApp' | sort | uniq)
+
+for dir in $dirs; do
+ if [ -d "$dir" ]; then
+ # Create the sentry.properties file in the directory and write the contents to it
+ echo "$sentry_props" > "$dir/iosApp.xcconfig"
+ echo "Added xcconfig to $dir"
+ else
+ echo "$dir does not exist"
+ fi
+done
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 05b5c78d..68022c8e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -12,6 +12,8 @@ dependencyResolutionManagement {
mavenCentral()
mavenLocal()
google()
+ maven("https://androidx.dev/storage/compose-compiler/repository/")
+ maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
}
}
@@ -20,11 +22,29 @@ rootProject.name = "sentry-kotlin-multiplatform-sdk"
include(":sentry-kotlin-multiplatform")
/*
-KMP App with targets:
+Simple KMP App with targets:
- Android
- - iOS with SwiftUI
+ - iOS with SwiftUI and SPM
- JVM Desktop with Jetpack Compose
*/
-include("sentry-samples:kmp-app:shared")
-include("sentry-samples:kmp-app:androidApp")
-include("sentry-samples:kmp-app:desktopApp")
+include("sentry-samples:kmp-app-spm:shared")
+include("sentry-samples:kmp-app-spm:androidApp")
+include("sentry-samples:kmp-app-spm:desktopApp")
+
+/*
+Simple KMP App with targets:
+ - Android
+ - iOS with SwiftUI and Cocoapods
+ - JVM Desktop with Jetpack Compose
+ */
+include("sentry-samples:kmp-app-cocoapods:shared")
+include("sentry-samples:kmp-app-cocoapods:androidApp")
+include("sentry-samples:kmp-app-cocoapods:desktopApp")
+
+/*
+KMP App with MVVM and Dependency Injection with Koin:
+ - Android with Jetpack Compose
+ - iOS with SwiftUI and SPM
+ */
+include("sentry-samples:kmp-app-mvvm-di:shared")
+include("sentry-samples:kmp-app-mvvm-di:androidApp")