Configuration
LoopsAIChatConfig — agent, environment, feature flags, analytics, presentation.
Everything is configured through LoopsAIChatConfig.
let config = LoopsAIChatConfig(
agentId: "YOUR_AGENT_ID",
environment: .production,
initialContext: LoopsAIChatContext(productContext: ["productCode": "SKU123"]),
features: LoopsFeatureFlags(virtualTryOnEnabled: true),
analytics: LoopsAnalyticsConfig(loopsSinkEndpoint: nil),
locale: "en",
showCloseButton: true
)Options
| Parameter | Type | Default | Purpose |
|---|---|---|---|
agentId | String | — | Your Loops agent identifier (required). |
environment | LoopsEnvironment | .production | Which runtime host to load. |
initialContext | LoopsAIChatContext? | nil | Product/user context at launch. See Context. |
features | LoopsFeatureFlags | .default | Per-launch flow-mode flag overrides. |
analytics | LoopsAnalyticsConfig | .default | Native analytics destinations. See Analytics. |
locale | String? | nil | Pins the chat language, overriding the device. Accepts a plain code or a BCP-47 tag ("tr", "tr-TR", Locale.current.identifier) — it is normalized to the base language. Omit to follow the device. |
showCloseButton | Bool | true | Show the native close affordance. |
keepAliveEnabled | Bool | true | Keep the chat WKWebView warm between presentations for an instant reopen. See Performance & data. |
developmentMode | Bool | false | Load chat even when the agent's web channel is inactive — for staging / QA builds. |
designMode | Bool | false | Same override, for design / preview builds. |
Header close-button slot
showCloseButton: true (the default) renders the SDK's own native close button in
the chat header. When it is on, the SDK automatically tells the web widget how much
header space to reserve for that button (headerActionSlotWidthPx), so the native
button never overlaps the widget's own controls. In SDK mode the widget's
favorite / heart control moves to the left side of the header to stay clear of
the reserved area.
You don't lay any of this out yourself — enabling the native close button reserves
aligned header space automatically. Set showCloseButton: false when you present
your own close affordance (e.g. a navigation bar) and don't want the SDK's.
Performance & data
The SDK keeps the chat runtime warm across presentations. The first present
loads the web runtime; on dismiss the WKWebView is not destroyed but kept alive
in a process-wide pool, and the next present reattaches it instantly — no
re-download, no JavaScript re-execution, no session re-bootstrap. The conversation,
scroll position and live connection survive in the background, so reopening feels
native. All runtimes also share the persistent data store, so cookies /
localStorage / HTTP cache survive between opens regardless.
Set keepAliveEnabled: false to opt out and cold-load on every present (e.g. a
shared kiosk that must reset per session).
Two static helpers manage the cached state:
// Drop the warm WebView(s) only — next present cold-loads; session/data kept.
LoopsAIChat.clearWebCache()
// Full wipe: warm WebViews + native session + the web runtime's persisted data
// (cookies / localStorage / IndexedDB). Use on logout / account switch.
LoopsAIChat.resetAllData {
// called on the main thread once the website data is cleared
}Environment
public enum LoopsEnvironment {
case production // chat.loopsai.com
case custom(URL) // self-host; the host is added to the origin allowlist
}Feature flags
LoopsFeatureFlags lets you override flow-mode availability for a launch. Each
flag is optional — when left nil, the server-resolved agent config decides.
Only set a flag to deliberately override it.
let features = LoopsFeatureFlags(
virtualTryOnEnabled: true,
searchEscalationEnabled: true
)Available flags: virtualTryOnEnabled, searchEscalationEnabled,
productSuggestionEnabled, outfitSuggestionEnabled.
Flags are forwarded to the web runtime over the bridge. Use them to gate which
native entry points you surface in your own UI (e.g. show a "Try on" button
only when virtualTryOnEnabled != false).
Presentation
LoopsAIChat.present(from:config:style:delegate:) accepts a style:
LoopsAIChat.present(from: self, config: config, style: .fullScreen)
LoopsAIChat.present(from: self, config: config, style: .sheet()) // bottom sheet
LoopsAIChat.present(from: navController, config: config, style: .push) // nav stackEnabling & disabling chat (availability)
Chat can be turned on or off per platform from your LoopsAI dashboard — open Channels → iOS and use its Active / Passive toggle. This controls the iOS agent independently of Web and Android: turning iOS off never affects the web widget or your Android app, and vice versa. It takes effect without an app update, so you can keep iOS off at launch and flip it on later, or disable it for maintenance.
If a platform's toggle has not been set yet, that platform falls back to the Web channel status — so existing integrations keep working until you set the per-platform switch.
Check availability from the app
Before you show your chat entry point (button, tab, etc.), ask the SDK whether the agent is active for this platform and show/hide it accordingly:
LoopsAIChat.fetchAvailability(agentId: "YOUR_AGENT_ID") { available in
chatButton.isHidden = !available
}It fails open (returns true) on a network error, so a transient blip never
hides a working chat.
Load while inactive (staging / QA / preview)
To keep loading the chat even while the channel is Inactive — e.g. on a staging
build or a design preview — set developmentMode (or designMode) on the config:
let config = LoopsAIChatConfig(
agentId: "YOUR_AGENT_ID",
developmentMode: true // ignore the Inactive channel status
)Leave developmentMode / designMode off in production — otherwise a
disabled channel would still load. The dashboard toggle + fetchAvailability
check is the intended production path.
Next: Context.