ایپ کے اندر چیٹ (WebView)
کسی بھی موبائل یا ڈیسک ٹاپ ایپ میں WebView کے ذریعے مکمل چیٹ وجیٹ شامل کریں، تمام فیچرز اور خودکار اپ ڈیٹس کے ساتھ۔
کسی نیٹو ایپ میں کسٹمر سپورٹ شامل کرنے کا سب سے تیز طریقہ یہ ہے کہ ہمارا ہوسٹ کیا ہوا فل اسکرین وجیٹ صفحہ ایک WebView میں لوڈ کیا جائے۔ آپ کو بالکل وہی وجیٹ ملتا ہے جو ویب سائٹ پر ہے: ہوم، پیغامات، ہیلپ سینٹر، سرچ، ایموجی، اٹیچمنٹس، ٹائم اسٹیمپس، پڑھے جانے کی اطلاع اور گفتگو ختم کرنا، اور یہ خودبخود اپ ڈیٹ ہوتا ہے۔ ایک بار انٹیگریٹ کرنے کے بعد وجیٹ کی تبدیلیوں کے لیے ایپ کا نیا ریلیز کبھی جاری نہیں کرنا پڑتا۔
فوری آغاز
WebView میں یہ URL کھولیں۔ لاگ اِن صارف کے لیے لازمی ہے کہ آپ externalId اور email (اور بہتر ہے کہ name بھی) شامل کریں، ورنہ ایجنٹ کو ہمیشہ صرف ایک گمنام وزیٹر نظر آئے گا۔ ہر ویلیو کو URL-encode کریں:
https://<your-domain>/api/widget-embed?appId=YOUR_APP_ID&externalId=USER_ID&email=USER_EMAIL&name=USER_NAMEگمنام شکل صرف اس وقت استعمال کریں جب صارف لاگ اِن نہ ہو:
https://<your-domain>/api/widget-embed?appId=YOUR_APP_ID⚠️ ایجنٹ کو بغیر ای میل کے “گمنام وزیٹر” نظر آ رہا ہے؟ یہ انٹیگریشن کی سب سے عام غلطی ہے: آپ کے WebView کے URL میں email/externalId موجود نہیں۔ سرور انہیں خود سے نہیں بنا سکتا، ایپ کو انہیں بھیجنا ہوگا۔ اگر وزیٹر کی ID anon-… جیسی دکھتی ہے تو کچھ بھی نہیں بھیجا گیا۔
JavaScript / کوئی بھی WebView (Flutter کے علاوہ)
Electron، ڈیسک ٹاپ شیلز، React Native، یا نیٹو WKWebView جہاں URL آپ خود سیٹ کرتے ہیں، وہاں URL کو JS میں بنائیں۔ URLSearchParams ہر ویلیو کو آپ کے لیے خود encode کر دیتا ہے:
// Build the support URL for the signed-in user before opening the WebView.
function buildSupportUrl(domain, user) {
const params = new URLSearchParams({
appId: 'YOUR_APP_ID',
platform: 'windows', // Strongly recommended: decides which help articles this visitor sees
// ios / macos_appstore / macos / android / windows / web
// locale: userSelectedLanguage, // Optional: only if your app has its own language switch; also add forceLocale: '1'
// Omitted = follow the device language (covers the App Store's 50 localizations)
// — Always pass these for signed-in users, or agents only see an "anonymous visitor" —
externalId: user.id, // The user's unique ID in your system
email: user.email, // The user's email (important)
name: user.name || '', // The user's name (optional)
// hmac: user.hmac, // Optional: computed on your server to prevent impersonation (see HMAC below)
// attrs: JSON.stringify({ plan: user.plan, expiresAt: user.expireAt }), // Optional: custom attributes
});
return `https://${domain}/api/widget-embed?${params.toString()}`;
}
// Usage: load the returned url in your WebView (instead of the old ?appId=...-only URL).
const url = buildSupportUrl('askais.com', currentUser);
myWebView.loadURL(url); // Electron: win.loadURL(url); native: load this URLFlutter (webview_flutter)
pubspec.yaml میں webview_flutter: ^4.x شامل کریں، پھر:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class SupportPage extends StatefulWidget {
const SupportPage({super.key});
@override
State<SupportPage> createState() => _SupportPageState();
}
class _SupportPageState extends State<SupportPage> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
// For signed-in users always include externalId + email, or agents see an "anonymous visitor" (no name / email).
// Pass only appId + locale when the user is not signed in. Uri.https URL-encodes the values.
final uri = Uri.https('askais.com', '/api/widget-embed', {
'appId': 'YOUR_APP_ID',
'platform': Platform.isIOS ? 'ios' : 'android', // Strongly recommended: decides which help articles are visible
// No locale = follow the device language (covers the App Store's 50 localizations).
// Only pass it if your app has its own language switch, together with 'forceLocale': '1':
// 'locale': appSettings.selectedLanguage,
'externalId': user.id, // The user's unique ID in your system (important)
'email': user.email, // The user's email (important)
'name': user.name, // Optional
// 'hmac': hmacFromYourServer, // Optional: computed on your server to prevent impersonation
});
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Colors.white)
..loadRequest(uri);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Support')),
// Full-screen: drop the appBar and use your own close button.
body: SafeArea(child: WebViewWidget(controller: _controller)),
);
}
}
// Open it from wherever your "Support" button lives:
// Navigator.push(context,
// MaterialPageRoute(builder: (_) => const SupportPage()));دوسرے اسٹیکس میں iOS/Android کے WebView (Swift WKWebView، Kotlin WebView، React Native react-native-webview) کے لیے بھی طریقہ یہی ہے: URL لوڈ کریں اور JavaScript فعال کریں۔
نیٹو iOS اور Android
کوئی SDK انسٹال نہیں کرنا پڑتا، آپ وہی URL بنا کر WebView کے حوالے کرتے ہیں۔ Mac کے معاملے پر توجہ دیں: App Store بلڈ اور DMG بلڈ ایک ہی آپریٹنگ سسٹم پر چلتے ہیں، اس لیے یہ ویلیو رن ٹائم چیک سے نہیں بلکہ بلڈ فلیگ سے آنی چاہیے۔
// ── iOS (Swift / WKWebView) ────────────────────────────────
// On Mac Catalyst / macOS, set platform to macos_appstore or macos.
#if targetEnvironment(macCatalyst)
let platform = "macos_appstore" // App Store build; a DMG build from your website passes "macos"
#else
let platform = "ios" // iPhone and iPad are both ios
#endif
var comps = URLComponents(string: "https://\(domain)/api/widget-embed")!
comps.queryItems = [
.init(name: "appId", value: "YOUR_APP_ID"),
.init(name: "platform", value: platform),
.init(name: "externalId", value: user.id), // Required for signed-in users
.init(name: "email", value: user.email), // Required for signed-in users
.init(name: "name", value: user.name),
// Only pass these two if your app has its own language switch:
// .init(name: "locale", value: settings.language),
// .init(name: "forceLocale", value: "1"),
]
webView.load(URLRequest(url: comps.url!)) // URLComponents URL-encodes the values
// ── Android (Kotlin / WebView) ─────────────────────────────
val url = Uri.parse("https://$domain/api/widget-embed")
.buildUpon()
.appendQueryParameter("appId", "YOUR_APP_ID")
.appendQueryParameter("platform", "android")
.appendQueryParameter("externalId", user.id)
.appendQueryParameter("email", user.email)
.appendQueryParameter("name", user.name)
// .appendQueryParameter("locale", settings.language)
// .appendQueryParameter("forceLocale", "1")
.build()
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true // The widget uses localStorage to remember the visitor
webView.loadUrl(url.toString())URL پیرامیٹرز
appId(لازمی): آپ کے ان باکس کی App ID۔locale: انٹرفیس کی زبان۔ تقریباً ہر صورت میں اسے نہ بھیجیں؛ تب وجیٹ اس ڈیوائس زبان کی پیروی کرتا ہے جو WebView بتاتا ہے، اور صارفین یہی توقع کرتے ہیں۔ انٹرفیس App Store Connect کی تمام 50 لوکلائزیشنز میں ترجمہ شدہ ہے؛ اس فہرست سے باہر کی کوئی بھی زبان انگریزی پر آ جاتی ہے۔ اسے صرف تب بھیجیں جب آپ کی ایپ میں زبان بدلنے کا اپنا آپشن ہو، اور اس صورت میں صارف کی منتخب کردہ زبان کے ساتھforceLocale=1بھی بھیجیں تاکہ وہ ڈیوائس کی سیٹنگ پر غالب آئے۔ کوئی ویلیو کبھی ہارڈ کوڈ نہ کریں: ہارڈ کوڈ کیا گیاzh-Hansہی وہ وجہ ہے جس سے کسی جاپانی ایپ میں چینی وجیٹ نظر آنے لگتا ہے۔platform(پرزور سفارش): وزیٹر کون سا بلڈ استعمال کر رہا ہے۔ اسی سے طے ہوتا ہے کہ اسے کون سے ہیلپ آرٹیکلز نظر آئیں گے اور آیا AI، App Store کے قواعد کے تحت جواب دے گا۔ اسے نہ بھیجیں تو پلیٹ فارم کا اندازہ WebView کے User-Agent سے لگایا جاتا ہے: اس سے iPhone، Android اور Windows تو پہچانے جاتے ہیں، لیکن Mac App Store بلڈ اور DMG بلڈ میں فرق نہیں ہو پاتا، اور ڈیسک ٹاپ موڈ والا iPad بھی نہیں پہچانا جاتا۔ ویلیوز:ios(iPhone اور iPad)،macos_appstore(Mac App Store بلڈ)،macos(براہ راست DMG بلڈ)،android،windows،web۔externalId: آپ کی ایپ میں صارف کی منفرد ID۔ اسے بھیجنے سے ایجنٹ صارف کو پہچان لیتا ہے اور اس کی سابقہ گفتگو یکجا ہو جاتی ہے۔email،name،avatarUrl: ایجنٹس کو دکھائی جانے والی پروفائل معلومات۔hmac: شناخت کا دستخط (نیچے دیکھیں)۔ اختیاری۔
پلیٹ فارم کے لحاظ سے مواد چھپانا (App Store کے قواعد کی پابندی)
App Store کا اصول 3.1.1 کسی iOS ایپ کو بیرونی خریداری، سبسکرپشن یا ریفرل سے متعلق مواد دکھانے کی اجازت نہیں دیتا۔ اسی لیے ہر ہیلپ آرٹیکل اور ہر عمومی سوال کے ساتھ پلیٹ فارم کے لحاظ سے نظر آنے کی سیٹنگ ہوتی ہے: اپنے ڈیش بورڈ میں ہیلپ سینٹر یا عمومی سوالات کا صفحہ کھولیں اور اس آئٹم پر پلیٹ فارم چپس کو آن یا آف کریں۔ منتخب پلیٹ فارم کے وزیٹرز اسے فہرست میں نہیں دیکھ پاتے اور براہ راست لنک سے بھی نہیں کھول سکتے۔
AI ہیلپ آرٹیکلز یا عمومی سوالات سے جواب نہیں دیتا، بلکہ اُس مواد سے دیتا ہے جو آپ نے “Training” میں شامل کیا ہے۔ وہاں کی فائلوں اور سوال و جواب کے جوڑوں پر بھی یہی پلیٹ فارم چپس ہوتی ہیں، اور جو آئٹم کسی پلیٹ فارم کے لیے چھپایا گیا ہو وہ اس پلیٹ فارم کے وزیٹرز کے جوابات میں استعمال نہیں ہوتا۔ اگر AI کو بھی اس کا ذکر نہیں کرنا چاہیے تو اسے دونوں جگہ چھپائیں۔
یہ قابلِ اعتماد طور پر کام کرے گا یا نہیں، اس کا انحصار دو باتوں پر ہے۔ پہلی، آپ کی ایپ کو خود platform بھیجنا چاہیے؛ User-Agent سے اندازہ لگانا صرف متبادل انتظام ہے۔ دوسری، ویلیو کو صرف آپریٹنگ سسٹم نہیں بلکہ بلڈ کی عکاسی کرنی چاہیے:
- iPad الگ نہیں ہے۔ یہ انہی قواعد کے تحت وہی iOS ایپ چلاتا ہے، اس لیے
iosمیں iPhone اور iPad دونوں شامل ہیں۔ - Mac کے دو بلڈ ہوتے ہیں۔ Mac App Store بلڈ اصول 3.1.1 کا پابند ہے؛ جو DMG آپ اپنی ویب سائٹ سے دیتے ہیں وہ نہیں۔ دونوں کا آپریٹنگ سسٹم ایک ہی ہے، اس لیے رن ٹائم پر کوئی چیز ان میں فرق نہیں کر سکتی؛ یہ فیصلہ بلڈ کے وقت کریں (مثلاً Xcode فلیگ سے) اور اسی کے مطابق
macos_appstoreیاmacosبھیجیں۔ غلطی ہوئی تو یا تو اسٹور بلڈ ریویو کے دوران سبسکرپشن کا مواد دکھا دے گا، یا DMG بلڈ بلاوجہ آپ کا آدھا ہیلپ سینٹر چھپا دے گا۔
تبدیلیاں فوراً نافذ ہو جاتی ہیں: یہ سیٹنگ ایپ میں نہیں بلکہ آپ کے ڈیش بورڈ میں ہوتی ہے، اس لیے جو کچھ نظر آتا ہے اسے بدلنے کے لیے کبھی نیا ریلیز جاری نہیں کرنا پڑتا۔
تصدیق شدہ شناخت (HMAC، اختیاری)
یہ ثابت کرنے کے لیے کہ کوئی externalId واقعی آپ کا لاگ اِن صارف ہے (اور کسی کو دوسرے کا روپ دھارنے سے روکنے کے لیے)، HMAC دستخط بھیجیں۔ اسے اپنے سرور پر کیلکولیٹ کریں، خفیہ کلید کبھی ایپ میں نہ رکھیں۔
hmac = HMAC-SHA256(secretKey, externalId)، آؤٹ پٹ چھوٹے حروف والے hex میں۔- دستخط کیا جانے والا ڈیٹا (payload)
externalIdہے، اس لیے اسے ہمیشہ بھیجیں: اس کے بغیر وجیٹ اپنی گمنام ID استعمال کرتا ہے، اور صرفemailپر کیا گیا دستخط مسترد ہو جاتا ہے۔ secretKeyآپ کے ان باکس کی خفیہ کلید ہے (ہر ان باکس کی ایک، ان باکس کی ترتیبات سے حاصل کریں)۔hmacکے بغیر وزیٹر کو غیر تصدیق شدہ سمجھا جاتا ہے۔
import { createHmac } from 'node:crypto';
// SECRET_KEY lives only on your server, never ship it inside the app.
const hmac = createHmac('sha256', SECRET_KEY)
.update(externalId) // exactly the externalId you pass to the widget
.digest('hex');
// Return { externalId, email, hmac } to the app, which appends them to the URL.پلیٹ فارم سے متعلق نوٹس
- Android:
INTERNETکی اجازت برقرار رکھیں؛ بیک بٹن کو انٹرسیپٹ کریں تاکہ وہ پہلے WebView کے اندر ہی پیچھے جائے۔webview_flutterکے حالیہ ورژن اٹیچمنٹس کے لیے<input type="file">کو سپورٹ کرتے ہیں۔ - iOS: HTTPS کسی ATS استثنا کے بغیر کام کرتا ہے۔ تصویر اپ لوڈ کرنا وجیٹ کا بلٹ اِن فیچر ہے:
WKWebViewخود ہی نیٹو فوٹو پِکر کھول دیتا ہے، اس لیے آپ کو کچھ بنانے کی ضرورت نہیں۔ فوٹو لائبریری سے تصویر چننے کے لیے کسی اجازت کی ضرورت نہیں۔Info.plistمیںNSCameraUsageDescriptionصرف اس صورت میں درکار ہے جب آپ کیمرے والا “تصویر لیں” آپشن چاہتے ہوں؛ اس کے بغیر صارف کے اس آپشن پر ٹیپ کرتے ہی ایپ کریش ہو جاتی ہے۔
نیٹو UI کو ترجیح دیتے ہیں؟
اگر آپ کو WebView کے بجائے خود بنایا ہوا نیٹو چیٹ انٹرفیس چاہیے تو موبائل SDKs صفحے پر موجود صرف لاجک والے کلائنٹس استعمال کریں۔ زیادہ تر ایپس کے لیے اوپر والا WebView جلد لانچ ہو جاتا ہے اور ہمیشہ تمام فیچرز کے ساتھ آتا ہے۔