Mobile SDKs

Mobile SDKs

Ship native chat inside your iOS, Android, Flutter, and React Native apps. Each SDK hosts the AwnCare chat experience and bridges its events to your native code.

How it works

Every mobile SDK is a thin native wrapper around the same hosted chat page — so you get the full widget experience, updated server-side, with a small footprint.

The SDK loads the hosted chat page (/widget/mobile) inside a platform WebView. Your workspace id, locale, brand color, and visitor details are passed as query parameters, so the widget boots pre-configured and ready.

Widget events are forwarded from the page to your native code over a message channel, and native drives the widget back through an injected JavaScript API called AwnCareMobile. This two-way bridge is what powers identify, unread badges, and open/close control.

The hosted chat URL

This is the page each SDK loads. You normally never build it by hand — the SDK assembles it for you from the configuration you pass. The metadata parameter is base64url-encoded JSON.

text
https://awn.care/widget/mobile
  ?workspaceId=YOUR_WORKSPACE_ID
  &locale=ar
  &color=%232431E0
  &name=Sara
  &email=sara%40acme.sa
  &metadata=eyJwbGFuIjoicHJvIn0
The SDKs are distributed from git. Pin to a released version (1.0.0) so your builds stay reproducible.

iOS · Swift

A Swift package for UIKit and SwiftUI apps, distributed through Swift Package Manager.

Install

Add the package in Xcode from its Git URL, or declare it in Package.swift, then import AwnCareSDK.

swift
// Swift Package Manager
// Xcode ▸ File ▸ Add Package Dependencies… then enter the package URL
// and pin to "Up to Next Major Version" from 1.0.0:
https://github.com/awncare/awncare-ios

// …or add it to Package.swift:
dependencies: [
  .package(url: "https://github.com/awncare/awncare-ios", from: "1.0.0"),
]

Quickstart

Configure once with your workspace id, then present the chat full-screen from any view controller.

swift
import AwnCareSDK

AwnCare.configure(workspaceId: "YOUR_WORKSPACE_ID", locale: "ar", color: "#2431E0")

// Present the chat full-screen from any UIViewController:
AwnCare.present(from: self)

// Attach the visitor's identity:
AwnCare.identify(AwnCareIdentity(name: "Sara", email: "[email protected]"))

Identify the visitor

Attach who the visitor is so agents see a named contact. Update the locale or clear the identity on sign-out.

swift
AwnCare.identify(AwnCareIdentity(
  name: "Sara",
  email: "[email protected]",
  externalId: "usr_10294",
  metadata: ["plan": "pro"]
))

// Switch language or clear the identity later:
AwnCare.setLocale("en")
AwnCare.logout()

Events

React to the widget with closures on the shared instance, or by conforming to AwnCareDelegate.

swift
// Observe widget events with closures…
AwnCare.onReady = { print("AwnCare ready") }
AwnCare.onUnreadCountChanged = { count in
  UIApplication.shared.applicationIconBadgeNumber = count
}
AwnCare.onChatClosed = { print("Chat closed") }

// …or conform to AwnCareDelegate:
class Support: AwnCareDelegate {
  func awnCare(_ awn: AwnCare, didChangeUnread count: Int) { /* … */ }
  func awnCareDidClose(_ awn: AwnCare) { /* … */ }
}
AwnCare.delegate = Support()

Full-screen vs embedded

For full-screen, call AwnCare.present(from:). To embed the chat inside your own layout, add AwnCareChatViewController as a child view controller.

swift
// Embed the chat inside your own screen as a child view controller:
let chat = AwnCareChatViewController(
  workspaceId: "YOUR_WORKSPACE_ID",
  locale: "ar",
  color: "#2431E0"
)
addChild(chat)
chat.view.frame = container.bounds
container.addSubview(chat.view)
chat.didMove(toParent: self)

Android · Kotlin

An Android library for Kotlin and Java apps, distributed through JitPack.

Install

Add the JitPack repository and the dependency to your Gradle files, then import care.awn.sdk.AwnCare.

kotlin
// settings.gradle.kts — add the JitPack repository:
dependencyResolutionManagement {
  repositories {
    maven { url = uri("https://jitpack.io") }
  }
}

// app/build.gradle.kts — add the dependency:
dependencies {
  implementation("com.github.awncare:awncare-android:1.0.0")
}

Quickstart

Configure once with your workspace id, then open the chat full-screen from any Activity or Fragment context.

kotlin
import care.awn.sdk.AwnCare
import care.awn.sdk.AwnCareIdentity

AwnCare.configure(workspaceId = "YOUR_WORKSPACE_ID", locale = "ar", color = "#2431E0")

// Launch the chat full-screen:
AwnCare.open(context)

// Attach the visitor's identity:
AwnCare.identify(AwnCareIdentity(name = "Sara", email = "[email protected]"))

Identify the visitor

Attach who the visitor is so agents see a named contact. Update the locale or clear the identity on sign-out.

kotlin
AwnCare.identify(
  AwnCareIdentity(
    name = "Sara",
    email = "[email protected]",
    externalId = "usr_10294",
    metadata = mapOf("plan" to "pro"),
  )
)

// Switch language or clear the identity later:
AwnCare.setLocale("en")
AwnCare.logout()

Events

Register an AwnCareListener to receive ready, unread-count, conversation-started, and close callbacks.

kotlin
AwnCare.setListener(object : AwnCareListener {
  override fun onReady() { /* … */ }
  override fun onUnreadCountChanged(count: Int) {
    // update your own badge
  }
  override fun onConversationStarted(conversationId: String) { /* … */ }
  override fun onChatClosed() { /* … */ }
})

Full-screen vs embedded

For full-screen, call AwnCare.open(context). To embed the chat in your own layout, place an AwnCareChatView in your XML and configure it from code.

kotlin
<!-- Drop the chat into any layout: -->
<care.awn.sdk.AwnCareChatView
    android:id="@+id/awnChat"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

// Configure the embedded view from code:
findViewById<AwnCareChatView>(R.id.awnChat).apply {
  workspaceId = "YOUR_WORKSPACE_ID"
  locale = "ar"
  color = "#2431E0"
}

Flutter

A Flutter plugin built on webview_flutter for iOS and Android from a single Dart codebase.

Install

Add the git dependency and webview_flutter to your pubspec.yaml, then run flutter pub get.

yaml
# pubspec.yaml
dependencies:
  webview_flutter: ^4.0.0
  awncare_flutter:
    git:
      url: https://github.com/awncare/awncare-flutter
      path: sdks/flutter/awncare_flutter

Quickstart

Configure once with your workspace id, then push AwnCareChatScreen as a full-screen route.

dart
import 'package:awncare_flutter/awncare_flutter.dart';

AwnCare.configure(workspaceId: 'YOUR_WORKSPACE_ID', locale: 'ar', color: '#2431E0');

// Push the chat as a full-screen route:
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (_) => AwnCareChatScreen(
      onUnreadCountChanged: (count) => setState(() => unread = count),
      onClosed: () => Navigator.pop(context),
    ),
  ),
);

Identify the visitor

Attach who the visitor is so agents see a named contact. Update the locale or clear the identity on sign-out.

dart
AwnCare.identify(const AwnCareIdentity(
  name: 'Sara',
  email: '[email protected]',
  externalId: 'usr_10294',
  metadata: {'plan': 'pro'},
));

// Switch language or clear the identity later:
AwnCare.setLocale('en');
AwnCare.logout();

Events

Pass callbacks to AwnCareChatScreen, or subscribe to the global AwnCareEvents stream for app-wide handling.

dart
// Per-screen callbacks:
AwnCareChatScreen(
  onReady: () => debugPrint('ready'),
  onUnreadCountChanged: (count) => setState(() => unread = count),
  onConversationStarted: (id) => debugPrint('conversation $id'),
  onClosed: () => Navigator.pop(context),
);

// …or subscribe to the global event stream:
final sub = AwnCareEvents.stream.listen((event) {
  if (event is AwnCareUnread) setState(() => unread = event.count);
});

Full-screen vs embedded

For full-screen, push AwnCareChatScreen with Navigator. To embed the chat, drop the AwnCareChatView widget anywhere in your widget tree.

dart
// Embed the chat inside any widget tree:
SizedBox(
  height: 480,
  child: AwnCareChatView(
    workspaceId: 'YOUR_WORKSPACE_ID',
    locale: 'ar',
    color: '#2431E0',
    onUnreadCountChanged: (count) => setState(() => unread = count),
  ),
);

React Native

A React Native component built on react-native-webview, installed straight from GitHub.

Install

Install the package and react-native-webview from npm, then install the iOS pods.

bash
npm install github:awncare/awncare-react-native react-native-webview

# iOS only — install the native pods:
cd ios && pod install

Quickstart

Render the AwnCareChat component with your workspace id and let it fill the screen.

tsx
import { AwnCareChat } from '@awncare/react-native';

export function SupportScreen({ navigation }) {
  const [unread, setUnread] = useState(0);
  return (
    <AwnCareChat
      workspaceId="YOUR_WORKSPACE_ID"
      locale="ar"
      color="#2431E0"
      identity={{ name: 'Sara', email: '[email protected]' }}
      onUnreadCountChanged={(c) => setUnread(c)}
      onClosed={() => navigation.goBack()}
    />
  );
}

Identify the visitor

Pass an identity prop, or drive the chat imperatively through a ref to call identify, setLocale, and logout.

tsx
import { useRef } from 'react';
import { AwnCareChat, AwnCareChatRef } from '@awncare/react-native';

const ref = useRef<AwnCareChatRef>(null);

// Imperatively drive the chat through the ref:
ref.current?.identify({
  name: 'Sara',
  email: '[email protected]',
  externalId: 'usr_10294',
  metadata: { plan: 'pro' },
});
ref.current?.setLocale('en');
ref.current?.logout();

<AwnCareChat ref={ref} workspaceId="YOUR_WORKSPACE_ID" />;

Events

Handle events with the onReady, onUnreadCountChanged, onConversationStarted, onMessageReceived, and onClosed props.

tsx
<AwnCareChat
  workspaceId="YOUR_WORKSPACE_ID"
  onReady={() => console.log('ready')}
  onUnreadCountChanged={(count) => setUnread(count)}
  onConversationStarted={(id) => console.log('conversation', id)}
  onMessageReceived={({ text, sender }) => console.log(sender, text)}
  onClosed={() => navigation.goBack()}
/>

Full-screen vs embedded

AwnCareChat is a plain View — render it full-screen, or inside any sized container to embed it.

tsx
// <AwnCareChat> is just a View — render it inside any container:
<View style={{ height: 480, borderRadius: 16, overflow: 'hidden' }}>
  <AwnCareChat workspaceId="YOUR_WORKSPACE_ID" locale="ar" color="#2431E0" />
</View>

Bridge & events

The same event contract underpins every SDK. Under the hood, the hosted page and your native code exchange messages over this bridge.

Events from the widget

The hosted chat page posts these events out to the native layer. Each SDK surfaces them as idiomatic callbacks, delegates, or listeners.

EventPayloadDescription
ready—The chat page has loaded and is ready to use.
open—The chat window opened.
close—The chat window closed — dismiss the screen or clear the badge.
unread{ count }The unread message count changed — update your app or icon badge.
conversation:started{ conversationId }A new conversation was created.
message:received{ text, sender }An agent or bot message arrived; sender is "agent" or "bot".
json
// The hosted chat page posts these events to the native layer:
{ "type": "ready" }
{ "type": "open" }
{ "type": "close" }
{ "type": "unread", "count": 3 }
{ "type": "conversation:started", "conversationId": "cnv_8f2a" }
{ "type": "message:received", "text": "How can I help?", "sender": "agent" }

The AwnCareMobile API

Native drives the widget by calling this JavaScript API, injected into the WebView. Every SDK wraps these calls in native methods so you rarely touch the bridge directly.

FieldTypeDescription
identify(identity)→ voidAttach or update the visitor's identity (name, email, external id, metadata).
setLocale(locale)"ar" | "en"Switch the widget UI language at runtime.
logout()→ voidClear the current visitor identity to start a fresh session.
open()→ voidOpen the chat window programmatically.
js
// Native drives the widget by calling the injected AwnCareMobile API:
AwnCareMobile.identify({ name: "Sara", email: "[email protected]" });
AwnCareMobile.setLocale("en");   // "ar" | "en"
AwnCareMobile.logout();
AwnCareMobile.open();

Push notifications

Notify visitors on their phone when an agent or bot replies while the app is closed — using your own Firebase project.

Architecture — bring your own Firebase

AwnCare never bundles Firebase. The app owner brings their own Firebase project and pastes its service-account JSON into Integrations. The mobile SDKs feed device tokens tied to the current visitor, and the AwnCare server sends the push via the FCM HTTP v1 API whenever an agent or bot replies to a visitor who is offline.

Because you own the Firebase project, delivery, quotas, and APNs certificates stay entirely under your control. AwnCare only holds the service-account credential and the device tokens your app hands it.

Workspace setup

In the dashboard, open Integrations → "Push Notifications (FCM)", paste your Firebase service-account JSON, and press Connect. The connection is tested before it is saved, and the derived project id is shown on the card.

Wire device tokens & handle taps

In each app, hand the FCM device token to AwnCare via setPushToken (now and on every refresh), and open the conversation when the user taps a notification. The conversation id arrives in data.conversationId.

iOS · Swift

Fetch the token from FirebaseMessaging and forward it; handle the tap in your UNUserNotificationCenter delegate.

swift
import FirebaseMessaging
import UserNotifications

// 1. Hand the current FCM token to AwnCare — and again on every refresh:
Messaging.messaging().token { token, _ in
  if let token { AwnCare.setPushToken(token) }
}

func messaging(_ messaging: Messaging, didReceiveRegistrationToken token: String?) {
  if let token { AwnCare.setPushToken(token) }
}

// 2. Open the conversation when the user taps the notification:
func userNotificationCenter(
  _ center: UNUserNotificationCenter,
  didReceive response: UNNotificationResponse,
  withCompletionHandler completionHandler: @escaping () -> Void
) {
  let info = response.notification.request.content.userInfo
  if let conversationId = info["conversationId"] as? String {
    // conversationId is available if you need to deep-link a specific thread.
    AwnCare.present(from: rootViewController)
  }
  completionHandler()
}

Android · Kotlin

Forward the token from FirebaseMessagingService.onNewToken, and post your own notification whose tap opens the chat.

kotlin
class AppMessagingService : FirebaseMessagingService() {

  // 1. Hand every new token to AwnCare:
  override fun onNewToken(token: String) {
    AwnCare.setPushToken(token)
  }

  // 2. Post a notification whose tap opens the AwnCare chat:
  override fun onMessageReceived(msg: RemoteMessage) {
    if (msg.data["type"] != "awncare_message") return
    val conversationId = msg.data["conversationId"] ?: return

    val intent = Intent(this, MainActivity::class.java).apply {
      putExtra("awncare_conversationId", conversationId)
    }
    val pending = PendingIntent.getActivity(
      this, conversationId.hashCode(), intent,
      PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
    )

    val notification = NotificationCompat.Builder(this, "awncare")
      .setContentTitle(msg.notification?.title ?: msg.data["title"])
      .setContentText(msg.notification?.body ?: msg.data["body"])
      .setSmallIcon(R.drawable.ic_chat)
      .setContentIntent(pending)
      .setAutoCancel(true)
      .build()

    NotificationManagerCompat.from(this)
      .notify(conversationId.hashCode(), notification)
  }
}

// In the Activity opened by the tap:
AwnCare.open(context)

Flutter

Forward getToken() and onTokenRefresh; open the chat screen from onMessageOpenedApp.

dart
import 'package:firebase_messaging/firebase_messaging.dart';

// 1. Hand the token to AwnCare — now and on every refresh:
FirebaseMessaging.instance.getToken().then((t) {
  if (t != null) AwnCare.setPushToken(t);
});
FirebaseMessaging.instance.onTokenRefresh.listen(AwnCare.setPushToken);

// 2. Open the conversation when a notification is tapped:
FirebaseMessaging.onMessageOpenedApp.listen((m) {
  if (m.data['type'] != 'awncare_message') return;
  final conversationId = m.data['conversationId'];
  Navigator.push(
    context,
    MaterialPageRoute(builder: (_) => const AwnCareChatScreen()),
  );
});

React Native

Forward messaging().getToken() to the chat ref, and navigate from onNotificationOpenedApp.

tsx
import messaging from '@react-native-firebase/messaging';
import { useEffect, useRef } from 'react';
import { AwnCareChat, AwnCareChatRef } from '@awncare/react-native';

const chatRef = useRef<AwnCareChatRef>(null);

useEffect(() => {
  // 1. Hand the token to AwnCare — now and on every refresh:
  (async () => {
    const token = await messaging().getToken();
    chatRef.current?.setPushToken(token);
  })();
  const unsubscribe = messaging().onTokenRefresh((token) => {
    chatRef.current?.setPushToken(token);
  });

  // 2. Navigate to the chat when a notification opens the app:
  messaging().onNotificationOpenedApp((rm) => {
    if (rm.data?.type !== 'awncare_message') return;
    const conversationId = rm.data?.conversationId;
    // navigate to your <AwnCareChat> screen, optionally using conversationId
  });

  return unsubscribe;
}, []);

<AwnCareChat ref={chatRef} workspaceId="YOUR_WORKSPACE_ID" />;
The Firebase SDK itself is the host app’s responsibility — AwnCare never bundles it. Your app configures Firebase and APNs, and simply hands us the token via setPushToken.

Payload contract

Every push AwnCare sends follows this shape. All data values are strings.

FieldValue
notification.titleYour workspace name.
notification.bodyThe reply text — markdown stripped, truncated to 120 characters.
data.conversationIdThe id of the conversation to open when the notification is tapped.
data.typeAlways the literal string "awncare_message" — use it to detect AwnCare pushes.