# Mobile SDK integration

Use this guide after the publisher chooses **Mobile SDK** in the integration picker. This is an implementation route for the five official SDKs below, not a claim that the publisher's native application or reward backend has passed staging validation. The SDK catalog, published packages and native source contracts were checked on 2026-09-24. Recheck the selected SDK's official guide, release and installed package before changing dependencies; documentation URL suffixes are not reliable version numbers.

## Choose the SDK, then ask only the next unresolved question

Present **Android**, **iOS**, **Flutter**, **React Native** and **Unity** in one SDK picker and wait for the answer. Reuse an already explicit SDK choice. Inspect the repository's framework, lockfiles, native projects, deployment targets, authentication and wallet before asking for facts the code already provides. If the chosen SDK conflicts with the existing framework, explain that conflict and ask one question about the intended target; do not silently replace the application framework.

For Flutter, React Native or Unity, ask **“Which mobile platforms should we support: Android, iOS, or both?”** only if the intended shipping targets are still unclear. Android and iOS SDK choices already resolve their platform. Then ask about the Earn entry point only if its placement is unknown. Keep each question separate and wait for its answer. Continue the shared demand-format, currency-name, units-per-USD and explicit S2S callback workflow; do not repeat decisions already made.

Do not ask routine precision or rounding questions. New virtual currencies use integers (scale 0); decimals require an explicit request. Preserve existing wallet precision, previously chosen financial settings and historical ownership. Choosing a mobile SDK does not change those rules or authorize a wallet migration.

## Current official choices and released contracts

| SDK | Current official guide and release source | Reviewed initialization and opening contract |
| --- | --- | --- |
| Android | [Android guide](https://developer.bitlabs.ai/docs/android-sdk-v3); [official 5.1.0 release](https://github.com/BitBurst-GmbH/bitlabs-android-library/releases/tag/5.1.0) | Maven Central package `com.prodege.bitlabs:core:5.1.0`; create an account-scoped instance with `BitLabs.OFFERWALL.create(publicAppToken, uid)`, then `offerwall.launch(activityContext)`. |
| iOS | [iOS guide](https://developer.bitlabs.ai/docs/ios-sdk-v3); [official 4.2.0 release](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/releases/tag/4.2.0) | Use the official Swift package and `BitLabs.OFFERWALL.create(token: publicAppToken, uid: uid)`, then `offerwall.launch(parent: presentingViewController)`. |
| Flutter | [Flutter guide](https://developer.bitlabs.ai/docs/flutter-sdk-v2); [published bitlabs 4.1.0](https://pub.dev/packages/bitlabs/versions/4.1.0); [official repository](https://github.com/BitBurst-GmbH/bitlabs-flutter) | `BitLabs.instance.init(publicAppToken, uid)`, then `BitLabs.instance.launchOfferWall(context)`. The released package is a native Android/iOS plugin. |
| React Native | [Current React Native guide](https://developer.bitlabs.ai/docs/bitlabs-react-native-sdk); [published bitlabs 2.1.0](https://www.npmjs.com/package/bitlabs/v/2.1.0); [official repository](https://github.com/BitBurst-GmbH/bitlabs-react-native) | Import `BitLabsOfferwall` from `bitlabs`; call `BitLabsOfferwall.init(publicAppToken, uid)`, then `BitLabsOfferwall.launch()`. This uses native modules. |
| Unity | [Unity guide](https://developer.bitlabs.ai/docs/unity-sdk-v3); [official 4.0.0 release](https://github.com/BitBurst-GmbH/bitlabs-unity/releases/tag/4.0.0) | UPM package `com.prodege.bitlabs`, resolved from `https://github.com/BitBurst-GmbH/bitlabs-unity.git#4.0.0`; call `BitLabs.Init(publicAppToken, uid, onSuccess, onError)` and launch with `BitLabs.LaunchOfferWall()` after successful initialization. Android/iOS device builds are the supported targets. |

The version numbers above are a verified snapshot, not an instruction to downgrade an existing working integration or install an unreviewed future version. Resolve and lock the chosen published release, record its provenance and use its actual APIs. Check native dependency requirements before editing the publisher's build system. A CLI WebView snippet is not an installation of one of these SDKs.

Important source differences to account for:

- Android's current API uses offerwall instances. The same guide retains deprecated `BitLabs.init` and widget examples. Prefer Maven Central over the guide's older JitPack alternative. The reviewed Android release declares min SDK 21 and uses compile/target SDK 35 in its source build. Its [published dependency graph](https://repo.maven.apache.org/maven2/com/prodege/bitlabs/core/5.1.0/core-5.1.0.pom) includes Kotlin 2.1.21, Compose and AndroidX. These facts are not a complete consumer compatibility guarantee; resolve the graph and build the publisher's actual target rather than silently changing its release settings.
- iOS recommends Swift Package Manager. The guide's dependency example still mentions 4.1.0, while the verified official release is 4.2.0. Its package declares iOS 12 and Swift tools 5.9. Compile against the resolved release and inspect its public signatures; do not copy syntax errors or deprecated examples from prose.
- Flutter's guide still shows a 2.x dependency example. Published 4.1.0 requires Flutter >=3.24.0 and Dart >=3.5.0 <4.0.0, and declares Android and iOS plugins. Version 4 removed older survey/leaderboard widgets. The [published archive](https://pub.dev/api/archives/bitlabs-4.1.0.tar.gz) uses Android Gradle Plugin 8.6.0, Kotlin 2.1.0, compile SDK 35/min SDK 21 and BitLabs core 5.1.0. Its iOS CocoaPods route pins `BLCustom` 4.1.0; its Swift package route pins a source revision. Inspect the actual resolved route and lockfile rather than assuming the latest native iOS release is installed. Do not assume Flutter Web or desktop support.
- **The current React Native npm package is still named `bitlabs`.** It is the old implementation and old `/docs/react-native-sdk` guide that are deprecated, not that package name. Use the current guide and [v2 migration guide](https://developer.bitlabs.ai/docs/migrate-to-v2). The [published 2.1.0 archive](https://registry.npmjs.org/bitlabs/-/bitlabs-2.1.0.tgz) declares React >=19 and React Native >=0.79 peers; its Android defaults include min SDK 24, compile/target SDK 35 and Kotlin 2.0.21, with BitLabs core 5.1.0. CocoaPods pins `BLCustom` 4.1.0. [React Native 0.79's own minimums](https://github.com/facebook/react-native/blob/v0.79.0/packages/react-native/scripts/cocoapods/helpers.rb) include iOS 15.1 and Xcode 15.1. Inspect the resolved dependency graph; the wrapper's older iOS 10 declaration does not override those requirements. A browser preview or JavaScript-only execution does not validate the native module.
- Unity requires External Dependency Manager for Unity. The verified 4.0.0 package declares Unity 2023.1 and EDM 1.2.186. Its [native dependencies](https://github.com/BitBurst-GmbH/bitlabs-unity/blob/4.0.0/Editor/BitLabsDependencies.xml) are Android `com.prodege.bitlabs:unity:5.1.1` and iOS `BitLabs/Unity ~> 4.1.1`; these are Unity-flavor dependencies, not interchangeable with the native core versions above. Inspect the resolved lockfiles before promising compatibility. The official release tag is `4.0.0`, without a `v` prefix. Do not copy the guide's `#v4.0.0` example or a moving `#upm` branch into a pinned setup. The published C# facade is the global `BitLabs` class with `LaunchOfferWall`, not the README's conflicting namespace/`ShowOfferwall` example. Guard native calls against `UNITY_EDITOR` in the publisher adapter; editor or desktop execution is not a supported device run. The C# facade does not expose the direct offer launch methods present in other SDKs; do not invent them.

## Initialize with the publisher identity and public token

Use the selected app's public App Token from sanitized `api.client.token` discovery when available. SDK guides sometimes call this an API key or token; it is not the Management API key, App Secret or private S2S API token. No private credential belongs in a mobile binary, build log, source file or agent conversation.

Resolve the UID automatically from the authenticated publisher account. Reuse its existing BitLabs identity or its persistent server-side opaque mapping. When the real identifier is numeric-only, sensitive or incompatible with the selected SDK, create/reuse a cryptographically random opaque alias on the server and use the same mapping for callback account resolution. Preserve historical aliases and wallet ownership. Never accept an arbitrary client-supplied wallet destination.

If the project has no identity system, generate a cryptographically random demo UID, persist it per demo session and reuse it across reloads. Never use `0`, a numeric-only placeholder, the nil UUID or a fixed shared demo ID. Keep demo identities and balances isolated from redeemable accounts. A signed-in application must not switch to this fallback merely because its user is logged out. Follow the selected SDK's identity limits; the supplied callback reference accepts at most 65 ASCII letters, digits, underscores or hyphens.

Create a small application-owned adapter for initialization, opening, closing, account transitions and UI events. Bind it to one app/environment/UID generation. Disable the Earn action until identity and configuration are valid, the actual initialization contract below is satisfied, and any old presentation has been cleared. Preserve existing app-wide settings; display configuration must not become an excuse to mutate unrelated dashboard settings. Do not put sensitive user data into callback tags.

### Pass UID through the selected SDK's public API

For SDK integration, supply the already resolved publisher UID as the **SDK initialization/factory argument** in the table above. Android takes the second argument to `BitLabs.OFFERWALL.create`; iOS takes its labeled `uid:` argument; Flutter and React Native take the second argument to `init`; Unity takes the second argument to `BitLabs.Init`. The official implementations preserve this distinction: [Android factory](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/core/java/ai/bitlabs/sdk/BitLabs.kt#L250-L252), [iOS factory](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.2.0/BitLabs/Core/Sources/BitLabs.swift#L202-L205), [Flutter initialization](https://github.com/BitBurst-GmbH/bitlabs-flutter/blob/master/lib/bitlabs.dart), [React Native initialization](https://github.com/BitBurst-GmbH/bitlabs-react-native/blob/01ee2ed843f1d5d7d908f488e0f4fb3e03c2ffda/src/bitlabs-offerwall.tsx#L6-L7) and [Unity initialization](https://github.com/BitBurst-GmbH/bitlabs-unity/blob/4.0.0/Runtime/BitLabs.cs#L50-L70).

Let the selected SDK construct its own offerwall/API requests. Do not replace the initialization argument with a manually injected `X-User-Id` header, custom `uid` tag, advertising ID or workspace/app ID. This does not imply that the SDK never uses HTTP headers internally: direct user-based API calls use their documented headers, while an independently implemented web iframe uses `uid` and `token` in its `src` query. Keep those routes distinct. An offerwall instance also does not initialize a separate native API singleton; follow the explicit API setup and wrapper limitations below if those optional methods are selected.

### Use each SDK's actual readiness contract

- Native Android/iOS `create` returns an account-bound object; it is not authentication or inventory verification and has no initialization-success callback. iOS also starts an asynchronous configuration fetch. Handle construction, presentation, loading and failure as separate application states; never invent an awaitable readiness API. [Android factory](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/core/java/ai/bitlabs/sdk/BitLabs.kt#L250-L252), [iOS initialization](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.2.0/BitLabs/Shared/Sources/Offerwall.swift#L29-L59).
- Flutter 4.1.0 public `init` and `launchOfferWall` return `void`, discarding the platform call's future. React Native 2.1.0 `init` and `launch` also expose no success promise. Do not add `await` or an invented success callback and claim it catches native initialization errors. Validate local inputs, prevent duplicate initialization, observe actual presentation/error behavior and test the native bridge. If the app requires an acknowledgement the wrapper does not expose, treat that as adapter work requiring a reviewed implementation and tests.
- Unity exposes initialization callbacks. Use them for the selected platform's documented initialization result; they still do not certify inventory, native teardown or callback delivery.

### Protect reserved fields and account-owned parameters

Keep `uid`, `token`, `sdk`, `os`, advertising/device identity fields such as `maid`, and deep-link selectors such as `offer-id` under the adapter's control. Reject those keys in custom tags/options, reject conflicting duplicate keys between maps, and allow only reviewed display parameters and non-sensitive custom tags. A caller must not override automatically resolved identity, public-token selection, platform or consent-derived identifiers. Both [Android](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/main/java/ai/bitlabs/sdk/offerwall/util/OfferwallUrl.kt#L38-L44) and [iOS](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.2.0/BitLabs/Shared/Sources/Utilities/OfferwallURL.swift#L50-L72) append custom entries after canonical query fields without rejecting duplicate keys; do not depend on the service's duplicate-parameter precedence.

Android 5.1.0 declares `options` but passes only `tags` into its [launch/deep-link URL builder](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/main/java/ai/bitlabs/sdk/offerwall/Offerwall.kt#L45-L76). Do not promise that `offerwall.options["theme"]` or another option changes its UI. Use a reviewed supported tag when the documented parameter permits it, otherwise mark the setting unsupported and verify the actual result. iOS forwards both maps.

Flutter/React Native bridges merge tags on Android and replace them on iOS. Unity's Android singleton also retains its tag map across re-initialization. Maintain a complete reviewed parameter map for each identity in the host adapter, including approved existing settings. An empty `setTags` call does not clear Android's previous entries, and omission does not reset a prior `in_app` override. Before reusing a wrapper, prove its old account parameters are cleared or use a tested fresh-instance/session path; do not silently restore stale settings. For Unity, use the documented `AddTag` path for individual approved values and validate any bulk-map bridge in the selected release before relying on it.

## Clear the actual earning surface on account changes

Logout and account switching must close the previous account's earning surface before another account can launch it. Removing a React/Flutter component, replacing a JavaScript variable or throwing an invalid-UID error alone does not demonstrate that a native modal, activity, WebView or external browser has gone away.

1. Disable all Earn launch paths and invalidate the old identity generation immediately. Clear account-specific UI state and reject late UI events from that generation. Never use those events to credit wallets.
2. Cancel in-flight operations that can present an old view; ignoring their completion is insufficient. Dismiss or finish the actual owned native presentation through a supported application/platform lifecycle, wait for that transition, release account-scoped SDK instances and detach application-owned subscriptions where the selected public API permits it. Verify that back navigation and background/foreground transitions cannot reopen the previous account's view. Serialize launches so double taps cannot create overlapping presentations.
3. Resolve the new authenticated identity and initialize the selected SDK only after teardown has been verified. Android/iOS offerwall instances bind token and UID when created; construct a new instance for a new identity instead of reusing the old one.
4. Do not invent `logout`, `reset`, `destroy` or listener-removal methods. The reviewed native Android/iOS offerwalls and Flutter, React Native and Unity public facades do not expose a complete reset/close contract. Inspect the exact installed native implementation and implement a tested host lifecycle. If safe switching cannot be demonstrated, keep Earn disabled for the changed identity and require a verified fresh native process/session before re-enabling it; record the limitation instead of claiming teardown passed. Restart alone does not prove shared WebView cookie/site-data isolation. Test that separately and do not indiscriminately erase the publisher's unrelated browser data.

In Android 5.1.0, [launch and deep-link calls](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/main/java/ai/bitlabs/sdk/offerwall/Offerwall.kt#L31-L95) return coroutine `Job` objects. Retain and cancel each job on account transition before releasing the instance: an IO lookup can otherwise finish later and start the old account's activity. A new instance does not cancel an older instance's work. The SDK unregisters listeners when its activity is destroyed; the wrapper must also prove the actual activity is finished. In iOS 4.2.0, launch [presents a native controller](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.2.0/BitLabs/Shared/Sources/Offerwall.swift#L127-L138); use a dedicated application-owned presentation boundary to dismiss that controller and wait for completion. Replacing the offerwall variable does not dismiss it.

React Native 2.1.0 registers an internal close-event listener on each `init` without returning a public unsubscribe handle. Do not initialize on every render or assume repeatedly calling `init` is a clean reset. Flutter/React Native close/reward events carry no reliable originating UID. Replacing a global callback can cause a late account-A event to invoke account B's newly installed callback. A generation check inside B's callback cannot recover the event's missing identity. Use generation guards for application-owned asynchronous work, but require verified native teardown before accepting a new account's SDK events. Test duplicate listeners and events arriving after logout; when teardown cannot be proven, keep Earn disabled until the documented fresh-process fallback is satisfied.

Unity's [iOS callbacks](https://github.com/BitBurst-GmbH/bitlabs-unity/blob/4.0.0/Runtime/BitLabsIOSCallbacks.cs) also use static callback slots, and deferred callbacks invoke the current slot. Serialize initialization and optional API calls; do not replace an outstanding callback slot or assume anonymous responses can be correlated to concurrent requests/accounts. Apply the same teardown requirement to identity transitions.

An already opened external-browser session cannot be silently recalled by the mobile app. Document that limitation if the selected route opens a browser, disable future launches on logout, and keep any delayed signed callback bound to its original account. Never remap an old callback to whichever user is currently signed in. If the publisher requires forced closure, treat that requirement as unresolved until the chosen SDK/host can satisfy it.

### Flutter 4.1.0 optional API and error-handler limitations

Do not use Flutter's iOS `getSurveys`/`checkSurveys` as a readiness gate or promise those optional APIs work from offerwall initialization alone. Its published bridge initializes `BitLabs.OFFERWALL` but calls the older `BitLabs.shared` survey methods. [Native iOS 4.1.0](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.1.0/BitLabs/Core/Sources/BitLabs.swift#L108-L196) keeps those identities separate and returns without completing an unconfigured shared request. A previously configured singleton may hold another account. The Flutter bridge's success branch also attempts a second error reply after serializing surveys. Keep those methods disabled until a corrected, reviewed bridge is tested for one completion and the correct identity; do not fix it by configuring a stale global singleton in parallel.

Flutter initialization also replaces `PlatformDispatcher.instance.onError` and configures SDK diagnostics. Review and preserve the app's existing error-reporting contract using a tested adapter or reviewed package correction. Do not assume the previous handler remains chained. The published SDK can send the opaque UID, public app token and error/stack information in diagnostics; assess that behavior before initialization. ATT handles tracking permission on iOS; it is not blanket consent or disclosure for all diagnostics.

## Review demand and platform gates

Keep the shared **Surveys, Offers and Gaming** choices distinct. Check the selected app's access, the installed SDK and the actual device/region. An enabled app setting, SDK deep-link method or empty inventory response does not establish that a format works for every platform, user or country.

- Offers and Gaming share the offers parent setting but have separate tab flags. Preserve the parent when either is enabled. Apply only explicit demand decisions, then read back app configuration. SDK display parameters do not grant missing account access.
- **iOS Gaming requires a separate review.** BitLabs documents that gaming is hidden by default in iOS SDK/WebView integrations. If Gaming and iOS are selected, explain the limitation and ask one unresolved choice: use the documented external-browser route, keep Gaming disabled on iOS, or review the publisher's approved native approach. Do not silently set `in_app=false`, bypass platform review or promise App Store approval. Read [BitLabs' iOS Gaming guide](https://developer.bitlabs.ai/docs/enable-games-for-sdk-integrations-for-ios) and [Apple's current review guidelines](https://developer.apple.com/app-store/review/guidelines/#unacceptable) before implementing that decision.
- For iOS tracking, follow the selected SDK's documented ATT flow and the publisher's consent policy. Add a truthful `NSUserTrackingUsageDescription` when that flow is used. Do not invent an advertising identifier when permission is denied. Native iOS 4.2.0 [caches IDFA at construction or permission grant](https://github.com/BitBurst-GmbH/bitlabs-ios-sdk/blob/4.2.0/BitLabs/Shared/Sources/Offerwall.swift#L45-L71); it does not clear the cached value on a later denied result, and launch reuses it. Skipping the SDK's ATT helper does not disable IDFA access when authorization was already granted. Recheck authorization on foreground/consent-status changes, dismiss/invalidate old presentations and recreate the instance for the same UID with current permission state before relaunch. Test initial denial and grant → revoke → resume. Do not invent an SDK clear-IDFA method. Review [BitLabs' iOS privacy guidance](https://developer.bitlabs.ai/docs/ios-privacy-guidelines-for-bitlabs-sdk-integration), actual dependency manifests and required App Store privacy disclosures; the absence of a manifest is not a privacy certification.
- Inspect the Android merged manifest and release network policy. The [5.1.0 SDK manifest](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/main/AndroidManifest.xml#L4-L28) adds Internet, network-state and AD_ID permissions, a FileProvider and an application-level network-security configuration. Its [provider-domain list permits cleartext](https://github.com/BitBurst-GmbH/bitlabs-android-library/blob/5.1.0/library/src/main/res/xml/network_security_config.xml#L4-L34). Preserve the publisher's security policy and review necessary scoped exceptions rather than silently replacing it or enabling cleartext globally. The SDK retrieves/logs advertising IDs and can send UID-tagged diagnostics; review actual collection, logging and the [Google Play Data Safety guidance](https://developer.bitlabs.ai/docs/complete-the-google-play-data-safety-questionnaire). Verify the merged provider configuration on the selected Android target.
- Review the chosen format's callback contract, including [Offer callbacks](https://developer.bitlabs.ai/docs/offer-callbacks) and [Survey callbacks](https://developer.bitlabs.ai/docs/survey-callbacks). Do not route unsupported pending, rejection or reconciliation states into a general credit operation. Keep the format blocked for real crediting until those semantics are implemented and tested.

## Complete callbacks and test the native application

Continue the shared explicit S2S reward callback decision. Unless the publisher already chose preview only, guide backend preparation, durable wallet integration, private App Secret configuration, callback registration, isolated debugger delivery and separately authorized signed staging verification. The SDK's public token and client reward listeners do not replace signed server-to-server callbacks. Do not request a private S2S API token merely to launch an SDK offerwall.

Reuse the callback signature/idempotency logic where its event contract fits, and test the publisher's actual transactional wallet adapter. The bundled Node/SQLite reference requires a persistent host; selecting a mobile SDK does not make that backend suitable for an ephemeral or distributed host. Do not force an SDK choice into the legacy iframe-only helper manifest or claim that manifest validates native integration.

Verify the selected platform builds and, where available, a real device or simulator/emulator execution. Test invalid configuration, native loading/presentation failures, opening/closing, repeated taps, back navigation, background/resume, consent changes, logout, account A → B transitions and late SDK events. Include logout while an Android launch is still pending, reserved-parameter rejection, tag reset behavior, existing error-handler preservation and optional API completion/identity tests when those APIs are used. Exercise supported deep links and the selected demand formats without enabling extra formats to hide empty inventory. Build-only success, a source inspection or an editor mock does not prove device behavior or reward delivery.

For the backend, verify signatures, exact amounts, unknown-user rejection, durable callback records, atomic balance changes, duplicate/concurrent delivery, conflicting transaction reuse, debug isolation and approved reconciliation behavior. Record **backend_ready**, **secret_configured**, **callback_registered**, **debug_callback_verified** and **signed_test_verified** separately. The dashboard Callback Tester uses `debug=true`; it must leave real balances unchanged and cannot prove non-debug reward crediting.

Save **implementation prepared**, **native device checks**, **staging evidence** and **human production review** separately in a sanitized setup report, with `PASS`, `FAIL`, `NOT_RUN` or `HUMAN_CONFIRMED`, changed files and non-secret evidence references. Keep the final chat reply concise and in plain language: what works, a verified app/build link when available, whether real rewards are configured and verified, and the next action. Link the report instead of pasting the file inventory and technical test checklist. Explicitly say when native device checks or live reward checks have not run, and surface failures that affect use. If preview only was chosen, mark only unperformed checks `NOT_RUN`; preserve existing configuration and verified evidence. Report the observed reward state and any verification still outstanding, without inferring that configuration is absent. A visible offerwall, SDK reward event, successful build or HTTP 200 alone is not completion.
