SparkScan KMP Skill
Critical: Do Not Trust Internal Knowledge
Scandit's Kotlin Multiplatform (KMP) SDK is new, shipping in 8.6. Your training data almost
certainly predates it and contains zero reliable knowledge of its API — do not pattern-match
it against the Android-native or iOS-native SparkScan SDKs you may know. The KMP API packages are
com.kmp.datacapture.* (the com.scandit.datacapture.kmp name is only the Maven group id,
never a Kotlin import), and shapes diverge from both native SDKs in specific ways (see below).
Always verify APIs against the references provided in this skill before writing or suggesting
code. Do not rely on memorized method signatures, parameters, or property names from any other
Scandit platform. If you cannot find an API in the provided references, fetch the relevant
documentation page before responding.
KMP-specific gotchas worth flagging:
- Import root is
com.kmp.datacapture.* — e.g. com.kmp.datacapture.barcode.spark.SparkScan,
com.kmp.datacapture.core.capture.DataCaptureContext. Never write com.scandit.datacapture.*
in KMP code.
SparkScan(settings) is a plain constructor — unlike other KMP modes (e.g.
BarcodeCapture.forContext(...), BarcodeAr.forContext(...)), SparkScan has no
forContext/factory function. SparkScan binds to the shared DataCaptureContext implicitly
the same way native SDKs do.
SparkScanSettings and SparkScanViewSettings have no public constructor — use the
factories SparkScanSettings.sparkScanSettings() (or the capturePresets overload) and
SparkScanViewSettings.sparkScanViewSettings(). Writing SparkScanSettings() is a compile
error.
SparkScanView is platform-divergent by construction signature: Android's constructor is
SparkScanView(context: android.content.Context, dataCaptureContext: DataCaptureContext, sparkScan: SparkScan, settings: SparkScanViewSettings);
iOS's is SparkScanView(dataCaptureContext: DataCaptureContext, sparkScan: SparkScan, settings: SparkScanViewSettings) —
no Context parameter on iOS. Shared commonMain code cannot construct a SparkScanView
directly; each platform host constructs it and hands it to shared code (e.g. the screen model)
to wire the feedback delegate and lifecycle.
- To embed the native view: Android uses
view.toAndroidView(): View (import
com.kmp.datacapture.barcode.ui.toAndroidView) inside a Compose AndroidView factory or a
plain ViewGroup; iOS uses view.toUIView(): UIView inside a UIViewRepresentable. These are
the only supported bridges — never call toNative() from application code (it's public only
because Kotlin's internal cannot span the multi-module KMP SDK).
SparkScanListener has exactly two methods to implement:
onBarcodeScanned(sparkScan, session, frameData) and
onSessionUpdated(sparkScan, session, frameData) — both required (no default bodies), and
frameData: FrameData is non-null on KMP (Android-native's data: FrameData? is nullable).
Read the scanned barcode from session.newlyRecognizedBarcode.
SparkScanFeedbackDelegate.getFeedbackForBarcode(barcode) returns SparkScanBarcodeFeedback?.
Build success/error feedback with SparkScanBarcodeSuccessFeedback(...) /
SparkScanBarcodeErrorFeedback(errorMessage, resumeCapturingDelay, ...) — the error
constructor's message parameter is named errorMessage (not message), and
resumeCapturingDelay is a plain Long in milliseconds (not TimeInterval like
Android-native, not a .NET TimeSpan). feedbackDelegate is a property on the platform
SparkScanView instance, not on SparkScanSettings.
- There is no
SparkScanScanningMode.Target on KMP. Native Android's combined
"Target scanning mode" class is not bound. Instead, SparkScanViewSettings exposes two
independent properties: scanningBehavior: SparkScanScanningBehavior (SINGLE / CONTINUOUS)
and previewBehavior: SparkScanPreviewBehavior (DEFAULT / PERSISTENT). Correspondingly,
SparkScanViewUiListener has no onScanningModeChange callback — don't invent one.
- UI-chrome toggles (
triggerButtonVisible, torchControlVisible, toolbarBackgroundColor,
triggerButtonCollapsedColor, etc.) are var properties on the constructed SparkScanView
instance itself, not on SparkScanViewSettings — SparkScanViewSettings only configures
construction-time behavior (zoom, timeouts, sound/haptic, hardware trigger, toast, mini-preview
size, camera position, periscope mode, scanning/preview behavior). The Compose Multiplatform
SparkScanView composable exposes the view-level toggles directly as parameters since it owns
view construction.
- Reactive alternative to implementing
SparkScanListener: sparkScan.recognizedBarcodes: Flow<Barcode>
and sparkScan.sessionUpdates: Flow<SparkScanSession> (from the same com.kmp.datacapture.barcode.spark
package). Collecting either Flow registers a listener; cancelling the collection removes it.
Each collector gets an independent listener — share a single upstream listener with
.shareIn(scope, SharingStarted.WhileSubscribed()) if multiple coroutines need the same stream.
- Compose Multiplatform:
@Composable fun SparkScanView(...) (from the barcode-compose module)
starts scanning as the final step on entering composition and stops it on dispose — do not
add manual onResume()/onPause()/startScanning()/stopScanning() calls when using this
composable; it manages the whole lifecycle. Use rememberSparkScan(context, settings) to build
the mode (defaults context to DataCaptureContext.sharedInstance).
- Teardown when NOT using the Compose composable (i.e. the manual
SparkScanView pattern):
sparkScan.removeListener(this) then dataCaptureContext.removeMode(sparkScan) — in that
order, on screen/model disposal. This matches the canonical sample's dispose() exactly; there
is no separate onDestroy().
- The license key placeholder is exactly
-- ENTER YOUR SCANDIT LICENSE KEY HERE -- (matches the
canonical sample). Use this exact string, not a different placeholder.
- On iOS, distribution is via Swift Package Manager (
Scandit/datacapture-kmp-spm, an umbrella
XCFramework) — an app links exactly one Kotlin framework product from that package; pick the
variant that bundles the barcode module (needed for SparkScan). The native Scandit XCFrameworks
(core, barcode) resolve transitively — do not add them as separate SPM dependencies.
- Request the
CAMERA permission at runtime on Android before starting scanning (the manifest
declaration alone is not sufficient). On iOS, NSCameraUsageDescription in Info.plist
triggers the OS permission prompt automatically on first camera use.
Intent Routing
Based on the user's request, load the appropriate reference file before responding:
- Integrating SparkScan from scratch, configuring symbologies, customizing feedback, handling
scan results, wiring the Compose Multiplatform view, customizing the SparkScan UI, or tearing
down the integration (e.g. "add SparkScan to my KMP app", "set up barcode scanning in my
shared module", "how do I use SparkScan with Compose Multiplatform", "reject a barcode and show
an error", "enable continuous scanning", "hide the trigger button") → read
references/integration.md and follow the instructions there.
API Usage Policy
Only use APIs that are explicitly documented in the Scandit references below. Do not invent or
guess method signatures, parameters, or property names — and never carry over a signature from
the Android-native or iOS-native SDKs without verifying it also holds for KMP. If unsure whether
an API exists or how it is called — or if a compile error occurs — fetch the relevant reference
page before responding. Do not tell the user to check the docs themselves. After answering,
always include the relevant link so the user can explore further.
Never construct or guess documentation URLs. When you need a specific class or property's API
page:
- First check whether the page you already fetched (e.g. the Advanced Configurations page)
contains a direct hyperlink to it — topic pages link directly to relevant API symbols. Always
request links alongside content in your fetch prompt.
- If no direct link was found, fetch the API index (see Full API reference in the table
below), extract the actual link from it, and follow that.
URL structures can vary (e.g. api/ui/ subdirectory) and guessing will lead to 404s.
References
Direct users to the right resource based on their question:
| Topic |
Resource |
| Get Started |
Intro · Get Started |
| Advanced topics (custom feedback, scanning behavior, UI customization) |
references/integration.md |
| Compose Multiplatform |
Core Concepts |
| Core concepts (context, camera, views) |
Core Concepts |
1---2name: sparkscan-kmp3description: SparkScan single-barcode scanning with the pre-built scanning UI in Kotlin Multiplatform (KMP) and Compose Multiplatform projects using Scandit's KMP SDK (`com.kmp.datacapture.*` imports). Use for integration, scan settings, result handling, feedback and UI customization, embedding `SparkScanView` in shared code, or troubleshooting.4license: MIT5---67# SparkScan KMP Skill89## Critical: Do Not Trust Internal Knowledge1011Scandit's Kotlin Multiplatform (KMP) SDK is new, shipping in 8.6. Your training data almost12certainly predates it and contains **zero** reliable knowledge of its API — do not pattern-match13it against the Android-native or iOS-native SparkScan SDKs you may know. The KMP API packages are14`com.kmp.datacapture.*` (the `com.scandit.datacapture.kmp` name is only the **Maven group id**,15never a Kotlin import), and shapes diverge from both native SDKs in specific ways (see below).1617**Always verify APIs against the references provided in this skill before writing or suggesting18code.** Do not rely on memorized method signatures, parameters, or property names from any other19Scandit platform. If you cannot find an API in the provided references, fetch the relevant20documentation page before responding.2122KMP-specific gotchas worth flagging:2324- Import root is `com.kmp.datacapture.*` — e.g. `com.kmp.datacapture.barcode.spark.SparkScan`,25 `com.kmp.datacapture.core.capture.DataCaptureContext`. Never write `com.scandit.datacapture.*`26 in KMP code.27- **`SparkScan(settings)` is a plain constructor** — unlike other KMP modes (e.g.28 `BarcodeCapture.forContext(...)`, `BarcodeAr.forContext(...)`), SparkScan has no29 `forContext`/factory function. `SparkScan` binds to the shared `DataCaptureContext` implicitly30 the same way native SDKs do.31- `SparkScanSettings` and `SparkScanViewSettings` have **no public constructor** — use the32 factories `SparkScanSettings.sparkScanSettings()` (or the `capturePresets` overload) and33 `SparkScanViewSettings.sparkScanViewSettings()`. Writing `SparkScanSettings()` is a compile34 error.35- `SparkScanView` is **platform-divergent by construction signature**: Android's constructor is36 `SparkScanView(context: android.content.Context, dataCaptureContext: DataCaptureContext, sparkScan: SparkScan, settings: SparkScanViewSettings)`;37 iOS's is `SparkScanView(dataCaptureContext: DataCaptureContext, sparkScan: SparkScan, settings: SparkScanViewSettings)` —38 **no `Context` parameter on iOS**. Shared `commonMain` code cannot construct a `SparkScanView`39 directly; each platform host constructs it and hands it to shared code (e.g. the screen model)40 to wire the feedback delegate and lifecycle.41- To embed the native view: Android uses `view.toAndroidView(): View` (import42 `com.kmp.datacapture.barcode.ui.toAndroidView`) inside a Compose `AndroidView` factory or a43 plain `ViewGroup`; iOS uses `view.toUIView(): UIView` inside a `UIViewRepresentable`. These are44 the *only* supported bridges — never call `toNative()` from application code (it's public only45 because Kotlin's `internal` cannot span the multi-module KMP SDK).46- `SparkScanListener` has exactly two methods to implement:47 `onBarcodeScanned(sparkScan, session, frameData)` and48 `onSessionUpdated(sparkScan, session, frameData)` — both required (no default bodies), and49 `frameData: FrameData` is **non-null** on KMP (Android-native's `data: FrameData?` is nullable).50 Read the scanned barcode from `session.newlyRecognizedBarcode`.51- `SparkScanFeedbackDelegate.getFeedbackForBarcode(barcode)` returns `SparkScanBarcodeFeedback?`.52 Build success/error feedback with `SparkScanBarcodeSuccessFeedback(...)` /53 `SparkScanBarcodeErrorFeedback(errorMessage, resumeCapturingDelay, ...)` — the error54 constructor's message parameter is named **`errorMessage`** (not `message`), and55 `resumeCapturingDelay` is a plain **`Long` in milliseconds** (not `TimeInterval` like56 Android-native, not a `.NET TimeSpan`). `feedbackDelegate` is a property on the platform57 `SparkScanView` instance, not on `SparkScanSettings`.58- **There is no `SparkScanScanningMode.Target` on KMP.** Native Android's combined59 "Target scanning mode" class is not bound. Instead, `SparkScanViewSettings` exposes two60 independent properties: `scanningBehavior: SparkScanScanningBehavior` (`SINGLE` / `CONTINUOUS`)61 and `previewBehavior: SparkScanPreviewBehavior` (`DEFAULT` / `PERSISTENT`). Correspondingly,62 `SparkScanViewUiListener` has no `onScanningModeChange` callback — don't invent one.63- UI-chrome toggles (`triggerButtonVisible`, `torchControlVisible`, `toolbarBackgroundColor`,64 `triggerButtonCollapsedColor`, etc.) are `var` properties on the constructed **`SparkScanView`65 instance itself**, not on `SparkScanViewSettings` — `SparkScanViewSettings` only configures66 construction-time behavior (zoom, timeouts, sound/haptic, hardware trigger, toast, mini-preview67 size, camera position, periscope mode, scanning/preview behavior). The Compose Multiplatform68 `SparkScanView` composable exposes the view-level toggles directly as parameters since it owns69 view construction.70- Reactive alternative to implementing `SparkScanListener`: `sparkScan.recognizedBarcodes: Flow<Barcode>`71 and `sparkScan.sessionUpdates: Flow<SparkScanSession>` (from the same `com.kmp.datacapture.barcode.spark`72 package). Collecting either Flow registers a listener; cancelling the collection removes it.73 Each collector gets an independent listener — share a single upstream listener with74 `.shareIn(scope, SharingStarted.WhileSubscribed())` if multiple coroutines need the same stream.75- Compose Multiplatform: `@Composable fun SparkScanView(...)` (from the `barcode-compose` module)76 starts scanning as the final step on entering composition and stops it on dispose — **do not77 add manual `onResume()`/`onPause()`/`startScanning()`/`stopScanning()` calls** when using this78 composable; it manages the whole lifecycle. Use `rememberSparkScan(context, settings)` to build79 the mode (defaults `context` to `DataCaptureContext.sharedInstance`).80- Teardown when NOT using the Compose composable (i.e. the manual `SparkScanView` pattern):81 `sparkScan.removeListener(this)` then `dataCaptureContext.removeMode(sparkScan)` — in that82 order, on screen/model disposal. This matches the canonical sample's `dispose()` exactly; there83 is no separate `onDestroy()`.84- The license key placeholder is exactly `-- ENTER YOUR SCANDIT LICENSE KEY HERE --` (matches the85 canonical sample). Use this exact string, not a different placeholder.86- On iOS, distribution is via Swift Package Manager (`Scandit/datacapture-kmp-spm`, an umbrella87 XCFramework) — an app links exactly **one** Kotlin framework product from that package; pick the88 variant that bundles the barcode module (needed for SparkScan). The native Scandit XCFrameworks89 (core, barcode) resolve transitively — do not add them as separate SPM dependencies.90- Request the `CAMERA` permission at runtime on Android before starting scanning (the manifest91 declaration alone is not sufficient). On iOS, `NSCameraUsageDescription` in `Info.plist`92 triggers the OS permission prompt automatically on first camera use.9394## Intent Routing9596Based on the user's request, load the appropriate reference file before responding:9798- **Integrating SparkScan from scratch, configuring symbologies, customizing feedback, handling99 scan results, wiring the Compose Multiplatform view, customizing the SparkScan UI, or tearing100 down the integration** (e.g. "add SparkScan to my KMP app", "set up barcode scanning in my101 shared module", "how do I use SparkScan with Compose Multiplatform", "reject a barcode and show102 an error", "enable continuous scanning", "hide the trigger button") → read103 `references/integration.md` and follow the instructions there.104105## API Usage Policy106107Only use APIs that are explicitly documented in the Scandit references below. Do not invent or108guess method signatures, parameters, or property names — and never carry over a signature from109the Android-native or iOS-native SDKs without verifying it also holds for KMP. If unsure whether110an API exists or how it is called — or if a compile error occurs — fetch the relevant reference111page before responding. Do not tell the user to check the docs themselves. After answering,112always include the relevant link so the user can explore further.113114**Never construct or guess documentation URLs.** When you need a specific class or property's API115page:1161. First check whether the page you already fetched (e.g. the Advanced Configurations page)117 contains a direct hyperlink to it — topic pages link directly to relevant API symbols. Always118 request links alongside content in your fetch prompt.1192. If no direct link was found, fetch the API index (see **Full API reference** in the table120 below), extract the actual link from it, and follow that.121122URL structures can vary (e.g. `api/ui/` subdirectory) and guessing will lead to 404s.123124## References125126Direct users to the right resource based on their question:127128| Topic | Resource |129|---|---|130| Get Started | [Intro](https://docs.scandit.com/sdks/kmp/sparkscan/intro/) · [Get Started](https://docs.scandit.com/sdks/kmp/sparkscan/get-started/) |131| Advanced topics (custom feedback, scanning behavior, UI customization) | `references/integration.md` |132| Compose Multiplatform | [Core Concepts](https://docs.scandit.com/sdks/kmp/core-concepts/) |133| Core concepts (context, camera, views) | [Core Concepts](https://docs.scandit.com/sdks/kmp/core-concepts/) |