getting started

Two Gradle lines.
Zero init code.

Sharingan ships as two artifacts with the same public API: the real one for debug builds, an inert no-op for release. Pick per build type and you're done — on Android, capture starts from a manifest-merged ContentProvider, so there is nothing to call.

Get the artifacts

Sharingan is on Maven Central. Add mavenCentral() to your repositories (settings.gradle.ktsdependencyResolutionManagement { repositories { mavenCentral(); … } }), then depend on the coordinate for your build type: io.github.mibrahimdev:sharingan:0.1.0 for debug (capture + UI) and io.github.mibrahimdev:sharingan-noop:0.1.0 for release (same API, inert, no UI).

Tested versions: Sharingan 0.1.0 → Kotlin 2.4.0, Ktor 3.5.0, Compose Multiplatform 1.11.1, AGP 8.13.2. Later versions may work but are unverified — Kotlin/Native has no cross-compiler-version binary compatibility guarantee, so match the Kotlin version exactly.

Building from source / contributing

To build against local changes, publish to your Maven local repo and add mavenLocal() to your repositories (dependencyResolutionManagement { repositories { mavenLocal(); … } }):

terminal
git clone https://github.com/mibrahimdev/Sharingan && cd Sharingan
./gradlew publishToMavenLocal

Install — Android app

app/build.gradle.kts
dependencies {
    debugImplementation("io.github.mibrahimdev:sharingan:0.1.0")
    releaseImplementation("io.github.mibrahimdev:sharingan-noop:0.1.0")
}

That is the whole build-type story: debug builds get capture + the log browser, release builds get the no-op (what "no effect" means).

Requirements: Android API 24+ · iOS arm64 + simulator arm64 · Ktor 3.x for the HTTP plugin.

Install — Kotlin Multiplatform (shared module + iOS)

Gradle resolves one dependency list per source set, so pick the artifact with a property — and note both iOS requirements: the dependency must be api in iosMain (not commonMain), and the framework block must export(...) it. Without both, Kotlin/Native generates an empty header and your Swift code won't see Sharingan at all.

shared/build.gradle.kts
kotlin {
    val sharinganArtifact = if (providers.gradleProperty("release").isPresent)
        "io.github.mibrahimdev:sharingan-noop:0.1.0" else "io.github.mibrahimdev:sharingan:0.1.0"

    listOf(iosArm64(), iosSimulatorArm64()).forEach { target ->
        target.binaries.framework {
            baseName = "shared"
            export(sharinganArtifact)   // surfaces Sharingan in shared.h
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation(sharinganArtifact)
        }
        iosMain.dependencies {
            api(sharinganArtifact)      // export() requires api at THIS source set
        }
    }
}
⚠ The -Prelease flag must reach every build that produces the framework Xcode links — including the Gradle invocation inside your Xcode build phase (./gradlew :shared:embedAndSignAppleFrameworkForXcode -Prelease). If the flag is missing there, a debug framework silently overwrites your noop one.

Kotlin/Native dead-code elimination additionally strips whatever the no-op doesn't reference.

Install — Pure Swift app (XCFramework)

terminal
./gradlew :sharingan:assembleSharinganReleaseXCFramework        # debug-tool build
./gradlew :sharingan-noop:assembleSharinganReleaseXCFramework   # inert twin

Outputs land in sharingan/build/XCFrameworks/release/Sharingan.xcframework and sharingan-noop/build/XCFrameworks/release/Sharingan.xcframework — same framework name on purpose, so import Sharingan compiles in every configuration and your build settings decide which one links:

  1. Copy both, e.g. Vendor/Debug/Sharingan.xcframework and Vendor/Release/Sharingan.xcframework.
  2. Add one of them to the target (General → Frameworks → Embed & Sign), then make both the search path and the embed input configuration-dependent: set FRAMEWORK_SEARCH_PATHS = $(SRCROOT)/Vendor/Debug for Debug and …/Release for Release. Reference the framework in the Embed Frameworks phase via $(FRAMEWORK_SEARCH_PATHS), and after building, verify the embedded framework inside the built .app matches the configuration. Linking one variant but embedding the other dyld-crashes at launch.
  3. import Sharingan, then SharinganViewControllerKt.presentSharingan(animated: true).

Don't mix this with the Maven/KMP path in one app — pick one.

Capture HTTP — the Ktor plugin

anywhere you build your HttpClient
val client = HttpClient {
    install(SharinganKtor) // that's it
}

The plugin records method, URL, status, headers, textual bodies (capped at 64 KB, configurable), duration, and a TTFB/Download timing split.

Tuning, plus MQTT/BLE wiring and OkHttp guidance, live in Integrations.

Open the log browser

PlatformEntry point
AndroidTap the capture notification (appears on first event), or call Sharingan.show(context)
iOSSharinganViewControllerKt.presentSharingan(animated: true) (one call, any thread), or embed SharinganViewControllerKt.SharinganViewController() yourself

The Android notification shows per-protocol counters, a three-event ticker when expanded, and a Pause/Resume action. It is silent and updates in place. Two platform realities to know:

Android 13+: the notification needs the POST_NOTIFICATIONS runtime permission — Sharingan declares it, your app requests it. Without the grant capture still works; open the browser with Sharingan.show(context).
Do Not Disturb: because the notification is silent, DND hides it on most devices. Wire Sharingan.show(context) to a debug-drawer button or shake gesture so the browser is always reachable.

On iOS the view controller is the platform-conventional entry; everything else — capture API, screens, share sheet — behaves identically on both platforms.

No plist key needed. Compose Multiplatform 1.11 crashes host apps whose Info.plist lacks CADisableMinimumFrameDurationOnPhone; Sharingan disables that strict check so the viewer can never take your app down. Adding the key yourself is still worthwhile on ProMotion devices — it unlocks 120 Hz.
Keep your shared module lean. If you only capture HTTP (no custom debug UI), you do not need any Compose Multiplatform dependencies of your own — Sharingan brings its UI with it.

A SwiftUI wrapper recipe is in Integrations.

Release safety — what "no effect" means

Prove parity in your own build: ./gradlew assembleRelease with the no-op swapped in compiles against the identical API surface.

Control surface

runtime API
Sharingan.events                 // StateFlow<List<SharinganEvent>>, oldest first
Sharingan.isRecording            // StateFlow<Boolean>
Sharingan.setRecording(false)    // pause capture (REC/PAUSED toggle)
Sharingan.clear()                // drop everything
Sharingan.setNotificationEnabled(false)  // Android: opt out of the notification

The buffer is an in-memory ring — default 300 events, SharinganStore(capacity) for custom sizes. Nothing is ever written to disk; process death clears it. Loggers are thread-safe and callable from any thread.