- Updated: March 20, 2026
- 8 min read
Unified End‑to‑End Mobile Demo: Integrating OpenClaw Explainability Widget into iOS (Swift) and Android (Kotlin) SDKs
The Unified End‑to‑End Mobile Demo demonstrates how to integrate the OpenClaw Explainability Widget into native iOS (Swift) and Android (Kotlin) applications using a single, reusable architecture.
1. Introduction
OpenClaw’s Explainability Widget empowers developers to surface model‑level insights—feature importance, decision paths, and confidence scores—directly inside a mobile UI. By delivering these explanations where users interact, you boost trust, reduce churn, and meet emerging regulatory requirements.
Creating a unified mobile demo means you can showcase the same widget on both platforms with minimal duplication, a consistent user experience, and a shared backend contract. This guide walks you through the full lifecycle: from architecture design to code, performance tuning, and final deployment.
2. Architecture Overview
The demo follows a clean, three‑tier model:
- Client Apps (iOS & Android) – Native UI layers that embed the OpenClaw widget.
- OpenClaw SDK – A thin wrapper that handles widget rendering, event routing, and secure token exchange.
- Backend Services – UBOS‑hosted APIs that provide model predictions, explanation data, and user authentication.
Diagram description (textual): Imagine a bidirectional arrow from each mobile client to the OpenClaw SDK. The SDK forwards a GET /explain request to the UBOS backend, which queries the AI model, formats the explanation JSON, and returns it to the SDK. The SDK then renders the widget inside the native view hierarchy.
3. Prerequisites
Before you start, ensure the following tools are installed and up‑to‑date:
| Component | Version / Tool |
|---|---|
| macOS / Xcode | Xcode 15+ (Swift 5.9) |
| iOS Simulator / Device | iOS 16+ |
| Android Studio | Arctic Fox (2022.1.1) or newer |
| Kotlin | 1.9+ |
| UBOS Account | Free tier or paid plan (see UBOS pricing plans) |
| OpenClaw API Key | Generate from the OpenClaw console |
4. iOS Integration (Swift)
4.1 Project Setup
Create a new Xcode project (App template) named OpenClawDemo. Choose SwiftUI or UIKit based on your preference; the code snippets below use UIKit for maximum compatibility.
4.2 Adding OpenClaw SDK via Swift Package Manager
- Open File → Add Packages….
- Enter the repository URL:
https://github.com/openclaw/ios-sdk. - Select the latest release and click Add Package.
4.3 Initializing the Widget
Place the initialization code in AppDelegate.swift or the SwiftUI App struct, ensuring the API key is loaded securely from the keychain.
// AppDelegate.swift
import UIKit
import OpenClawSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Load API key from secure storage
let apiKey = KeychainHelper.shared.get(key: "OpenClawAPIKey") ?? ""
OpenClaw.configure(apiKey: apiKey, environment: .production)
return true
}
}
4.4 Embedding the Widget in a ViewController
The widget is a UIView subclass. Add it to any view hierarchy like a regular view.
// ExplainViewController.swift
import UIKit
import OpenClawSDK
class ExplainViewController: UIViewController {
private let explainWidget = OpenClawExplainabilityWidget()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
setupWidget()
fetchExplanation()
}
private func setupWidget() {
explainWidget.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(explainWidget)
NSLayoutConstraint.activate([
explainWidget.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
explainWidget.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
explainWidget.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
explainWidget.heightAnchor.constraint(equalToConstant: 300)
])
// Optional: customize appearance
explainWidget.theme = .dark
}
private func fetchExplanation() {
// Example payload – replace with your own model input
let input = ["age": 34, "income": 72000, "region": "EMEA"] as [String : Any]
OpenClaw.requestExplanation(for: input) { result in
switch result {
case .success(let explanation):
DispatchQueue.main.async {
self.explainWidget.render(explanation: explanation)
}
case .failure(let error):
print("Explanation error: \(error)")
}
}
}
}
4.5 Handling Callbacks and Events
The SDK emits delegate callbacks for user interactions (e.g., tapping a feature bar). Implement OpenClawExplainabilityDelegate to capture them.
extension ExplainViewController: OpenClawExplainabilityDelegate {
func widgetDidSelectFeature(_ feature: String) {
print("User selected feature: \(feature)")
// You could navigate to a detailed view or log analytics here.
}
func widgetDidFailToRender(error: Error) {
// Show a fallback UI
let alert = UIAlertController(title: "Error",
message: "Unable to display explanation.",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
5. Android Integration (Kotlin)
5.1 Project Setup
Start a new Android Studio project named OpenClawDemo. Choose Empty Activity and set the language to Kotlin.
5.2 Adding OpenClaw SDK via Gradle
In the settings.gradle file, add the Maven repository, then declare the dependency in app/build.gradle.
// settings.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://repo.openclaw.ai/sdk" }
}
}
// app/build.gradle
dependencies {
implementation("com.openclaw:android-sdk:1.4.2")
}
5.3 Initializing the Widget
Initialize the SDK in your Application subclass. Store the API key securely using Android’s EncryptedSharedPreferences.
// MyApplication.kt
package com.example.openclawdemo
import android.app.Application
import com.openclaw.sdk.OpenClaw
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val apiKey = SecurePrefs.getString(this, "OpenClawAPIKey") ?: ""
OpenClaw.configure(this, apiKey, OpenClaw.Environment.PRODUCTION)
}
}
5.4 Embedding the Widget in an Activity
The widget is provided as a Fragment. Add it to your layout XML and bind it in the activity.
<!-- activity_main.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/explainFragment"
android:name="com.openclaw.sdk.ExplainabilityFragment"
android:layout_width="0dp"
android:layout_height="300dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
// MainActivity.kt
package com.example.openclawdemo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.openclaw.sdk.ExplainabilityFragment
import com.openclaw.sdk.OpenClaw
class MainActivity : AppCompatActivity(),
ExplainabilityFragment.ExplainabilityListener {
private lateinit var explainFragment: ExplainabilityFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
explainFragment = supportFragmentManager
.findFragmentById(R.id.explainFragment) as ExplainabilityFragment
// Optional UI customisation
explainFragment.setTheme(ExplainabilityFragment.Theme.DARK)
fetchExplanation()
}
private fun fetchExplanation() {
val input = mapOf(
"age" to 28,
"income" to 54000,
"region" to "APAC"
)
OpenClaw.requestExplanation(input) { result ->
runOnUiThread {
result.onSuccess { explanation ->
explainFragment.renderExplanation(explanation)
}.onFailure { error ->
// Show fallback UI
showError(error.localizedMessage ?: "Unknown error")
}
}
}
}
// ----- ExplainabilityListener callbacks -----
override fun onFeatureSelected(feature: String) {
// Log analytics or navigate
println("Feature selected: $feature")
}
override fun onRenderFailed(error: Throwable) {
showError(error.message ?: "Render failed")
}
private fun showError(message: String) {
// Simple toast for demo purposes
android.widget.Toast.makeText(this, message, android.widget.Toast.LENGTH_LONG).show()
}
}
5.5 Handling Callbacks and Events
The fragment implements ExplainabilityListener. Use the callbacks to react to user taps or rendering failures, as shown above.
6. Performance Tips
Even a lightweight widget can become a bottleneck if not tuned. Follow these best‑practice categories:
6.1 Lazy Loading & Caching
- Load the explanation only when the user navigates to the screen; avoid pre‑fetching on app launch.
- Cache the JSON payload (e.g., using
NSCacheon iOS orLruCacheon Android) for up to 5 minutes to prevent duplicate network calls. - Store rendered SVG/bitmap assets locally to reduce re‑draw overhead.
6.2 Memory Management
- Release the widget view in
viewWillDisappear(iOS) oronDestroyView(Android) to free GPU memory. - Use weak references for delegate callbacks to avoid retain cycles.
6.3 Network Optimization
- Compress the explanation payload with
gzipon the UBOS backend. - Enable HTTP/2 or HTTP/3 on the server side to reduce latency.
- Implement exponential back‑off for retry logic in case of transient failures.
7. Deployment Guide
7.1 Testing on Simulators & Emulators
Run the following checks before a release:
- Validate that the widget renders correctly on iPhone 14, iPad Pro, and Android Pixel 7 emulators.
- Test network failure scenarios using
Network Link Conditioner(iOS) andadb shell tc(Android). - Confirm that callbacks fire on both tap and swipe gestures.
7.2 Building Release Binaries
For iOS, archive the app via Xcode → Product → Archive, then export an .ipa signed with your App Store Distribution certificate.
For Android, run:
./gradlew clean assembleRelease
This generates a signed .aab (recommended) or .apk ready for Google Play.
7.3 Publishing to App Store & Google Play
- Upload the
.ipavia App Store Connect, fill the “What’s New” section with a note about the new Explainability Widget. - Upload the
.aabto the Google Play Console, set the appropriate rollout percentage, and monitor the “Pre‑launch report” for crashes.
7.4 Post‑Launch Monitoring
Integrate UBOS’s OpenClaw hosting solution to collect usage metrics (widget load time, API latency, error rates). Set alerts for > 2 seconds average latency, which typically indicates a network or caching issue.
8. Conclusion
By following this step‑by‑step guide, you now have a production‑ready, unified mobile demo that showcases the OpenClaw Explainability Widget on both iOS (Swift) and Android (Kotlin). The architecture keeps the client thin, the SDK reusable, and the backend scalable—exactly the pattern modern AI‑enabled products need.
Next steps include extending the demo with:
- Multi‑language support via the OpenAI ChatGPT integration.
- Real‑time analytics dashboards powered by the UBOS platform.
- Custom branding of the widget to match your product’s visual identity.
Happy coding, and let the explanations empower your users!
