Skip to content

About

Official iOS SDK for Chatty AI chatbots — native SwiftUI, no WebView

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Chatty iOS SDK

Native SwiftUI chat UI for Chatty — zero WebView, zero compromise.

Drop a fully native, on-brand support chat into any iOS app in minutes. Talks directly to the same /api/widget/* backend as the Chatty web widget, and renders every bubble, avatar, and composer with real SwiftUI views — no WKWebView, no JS bridge, no compromise on feel.

CI SPM compatible CocoaPods License: MIT iOS 15+ Stars

Install · Quick start · Design gallery · API reference · Example app

LiveKit voice

Call ChattyClient(botId: botId).createVoiceToken(sessionId:) and connect the returned token with the official LiveKit Swift client. ChattyEmbedView already includes the complete widget voice UI and real-time transcript for hosts that prefer zero native LiveKit setup.

For a standalone screen, use ChattyVoiceView(botId:); it loads the same official LiveKit UI in voice-only mode.


Note

Developed and code-reviewed in an environment without an Xcode/swiftc toolchain, so changes are never compiled locally before pushing. ci.yml runs swift build / swift test on real macOS runners on every push — check the badge above is green before relying on a pre-release build, and please open an issue if it isn't.

Why this SDK

No WebView, anywhere Every bubble, avatar, and the composer are real SwiftUI views — no iframe, no JS bridge, no WKWebView overhead.
Matches your dashboard automatically Fetches the bot's theme and renders with the exact colors, corner radii, and launcher shape chosen in the dashboard — no manual styling.
Two integration shapes A floating ChattyLauncher button + sheet, or an embedded ChattyChatView inside your own view hierarchy.
A real composer, not a stub Full-Unicode emoji picker (search + categories, ~1,850 emoji), animated attach menu (camera, photo library, documents, location), and mic-to-text voice notes — built in, not bolted on.
Zero third-party dependencies Only Apple's own SwiftUI, PhotosUI, AVFoundation, and Foundation.

Install

Swift Package Manager (recommended)

In Xcode: File → Add Package Dependencies… and enter:

https://github.com/PersonaliAI/chatty-ios-sdk

Or add it to Package.swift directly:

dependencies: [
    .package(url: "https://github.com/PersonaliAI/chatty-ios-sdk", from: "1.1.0")
]
CocoaPods
pod 'ChattySDK', '~> 1.1.0'

Quick start

Find your bot ID in the Chatty dashboard under Embed & Integrate → iOS SDK.

Floating launcher (recommended) — a button that expands into a chat sheet, the native equivalent of the web widget's launcher:

import ChattySDK

struct RootView: View {
    var body: some View {
        ContentView()
            .overlay(ChattyLauncher(botId: "YOUR_BOT_ID"))
    }
}

Embedded full-screen chat — place it directly in your own navigation, e.g. as a "Support" tab:

import ChattySDK

struct SupportScreen: View {
    var body: some View {
        ChattyChatView(botId: "YOUR_BOT_ID")
    }
}

Design gallery

The SDK ships all 10 Chatty widget designs as SwiftUI color/radius tokens, ported 1:1 from the web widget's globals.css, so a native screen looks like whatever design is chosen in the dashboard rather than one generic look. No configuration required — the SDK fetches the bot's theme and resolves the matching token set automatically, including legacy widget_style IDs from older presets.

Design Accent
minimal #1c1a15
playful #ff8a5c
corporate #1c2e4a
dark-sleek #00e5c7
gradient-glow #a855f7
glassmorphism #8f6ff0
ecommerce #0f9d8c
healthcare-calm #6f9c7d
neubrutalism #ff3d67
luxury-editorial #161412

Font pairing (each web design uses a distinct Google Font) is intentionally out of scope for this release; color, radius, and header/bubble treatment carry most of a design's identity.

API reference

ChattyLauncher

public init(
    botId: String,
    baseURL: String = chattyDefaultBaseURL,
    host: String? = nil,
    position: ChattyPosition = .bottomTrailing,
    color: Color? = nil,
    onNotificationBellPress: (() -> Void)? = nil,
    enableVoiceNotes: Bool = true,
    enableNotificationBell: Bool = true,
    enableLocationSharing: Bool = true
)
Param Description
botId Required. Your bot's ID from the dashboard.
baseURL Chatty backend base URL. Defaults to the production API.
host Advisory only — sent to the backend but not used for access control. See Notes.
position .bottomLeading or .bottomTrailing. Default .bottomTrailing.
color Overrides the launcher color. Defaults to the active design's accent color.
onNotificationBellPress Forwarded to ChattyChatView's header notification bell. See Notes.
enableVoiceNotes Forwarded to ChattyChatView. See Permissions.
enableNotificationBell Forwarded to ChattyChatView. See Permissions.
enableLocationSharing Forwarded to ChattyChatView. See Permissions.

ChattyChatView

public init(
    botId: String,
    baseURL: String = chattyDefaultBaseURL,
    host: String? = nil,
    onMessage: ((ChattyMessage) -> Void)? = nil,
    onNotificationBellPress: (() -> Void)? = nil,
    onClose: (() -> Void)? = nil,
    enableVoiceNotes: Bool = true,
    enableNotificationBell: Bool = true,
    enableLocationSharing: Bool = true
)
Param Description
botId Required. Your bot's ID from the dashboard.
baseURL Chatty backend base URL. Defaults to the production API.
host Advisory only — sent to the backend but not used for access control. See Notes.
onMessage Called for every inbound message — useful for unread badges or analytics.
onNotificationBellPress Header notification-bell button tapped, after the OS permission prompt resolves. See Notes.
onClose Renders a close (✕) button in the header when set. ChattyLauncher passes this for you.
enableVoiceNotes Default true. Set false to hide the composer's mic button — the SDK then never calls AVAudioSession.requestRecordPermission at all. See Permissions.
enableNotificationBell Default true. Set false to hide the header's bell button — the SDK then never calls UNUserNotificationCenter.requestAuthorization at all. See Permissions.
enableLocationSharing Default true. Set false to hide the attach menu's Location option — the SDK then never calls CLLocationManager's authorization/location APIs at all. See Permissions.

Notes

Permissions — what this SDK requests, and how to opt out

As a Swift Package, this SDK can't embed its own Info.plist usage-description keys — your app's Info.plist must declare them (see Requirements below). But when the underlying OS permission API actually fires is controlled entirely by this SDK, in direct response to a specific button being tapped:

API called Risk Used for Requested when
AVAudioSession.requestRecordPermission Dangerous (microphone) Composer mic button → voice-note transcription User taps the mic button, only if enableVoiceNotes (default true)
UNUserNotificationCenter.requestAuthorization Sensitive (notifications) Header bell button → local notification-permission ask User taps the bell, only if enableNotificationBell (default true)
CLLocationManager authorization/location Dangerous (location) Attach menu's Location option → drops a Google Maps link into the composer text User taps Location, only if enableLocationSharing (default true)

Camera and Photo Library don't appear here because this SDK never calls their permission APIs directly — UIImagePickerController(sourceType: .camera) and PhotosPicker/PHPickerViewController trigger their own OS-managed prompts (or, for the photo picker, no prompt at all) when presented, outside this SDK's control. Documents (UIDocumentPickerViewController) needs no permission or Info.plist key at all — it's a sandboxed system file browser, iOS-only (no macOS equivalent is wired up, same as Camera).

The SDK never calls a permission API speculatively or on load — only in direct response to the matching button being tapped. If you don't want your app requesting mic access, notification authorization, or location access through this SDK at all, set the matching enable* param to false; the button disappears and the SDK will never touch that API. Your app remains free to request AVAudioSession/UNUserNotificationCenter/CLLocationManager permission itself, on its own schedule, for its own purposes — this SDK's opt-out only stops this SDK from requesting them.

If enableVoiceNotes is false, you can also drop NSMicrophoneUsageDescription from your own Info.plist (nothing in the SDK will ever trigger the mic prompt to need it). Same for NSLocationWhenInUseUsageDescription if enableLocationSharing is false.

Security — bot_id and domain restriction

bot_id is not a secret — it's extractable from any client, web or mobile. Domain restriction (allowed_domains in the dashboard) is enforced by the backend as a rate-limit tier, not a hard reject: verified web traffic gets 30 msgs/60s per bot+IP, everything else (including all mobile SDK traffic — there's no way for a native app to obtain a "verified" token the way a browser's Referer allows) gets throttled to 5 msgs/120s. The host param this SDK sends is advisory only and isn't used for access control. If your bot is mobile-primary, leave allowed_domains empty to get the normal 30/60s tier instead.

Notification bell — what it does and doesn't do

Tapping it requests the OS notification permission and then calls onNotificationBellPress. That's as far as this SDK goes — actually delivering a push when a reply arrives while the app is backgrounded needs APNs (or a wrapper like OneSignal) wired up at the app level: register the device token, send it to your backend, store it against the session/user, and have the backend call APNs when a message lands for a session that isn't actively polling. None of that exists yet — it's backend work in chatty-backend.

  • Lead capture and meeting booking happen conversationally (the assistant decides to ask/act) — there's no separate REST call to trigger them from the SDK.
  • Polling for human-agent takeover messages runs every 4s while ChattyChatView is active, matching the web widget's behavior.
  • Conversation history is persisted locally (UserDefaults), mirroring the web widget's localStorage cache, so a returning user sees their prior messages.

Example app

Example/ChattySDKExample.xcodeproj is a minimal, runnable SwiftUI app demonstrating both integration styles side by side. Open it in Xcode (it resolves ChattySDK as a local Swift Package pointing at the repo root) and run on a simulator to try the floating launcher and the embedded full-screen chat against a live demo bot.

Requirements

  • iOS 15+ (macOS 13+ for the library target)

  • Swift 5.7+, SwiftUI

  • Uses async/await, @StateObject — no third-party dependencies

  • Add these keys to your app's Info.plist to use the composer's mic, camera, and attach-menu Location button (a library target can't inject Info.plist entries — this has to be in the consuming app):

    <key>NSMicrophoneUsageDescription</key>
    <string>Used to record voice messages in chat.</string>
    <key>NSCameraUsageDescription</key>
    <string>Used to attach photos in chat.</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Used to share your location in chat.</string>

    Without these, tapping the mic/camera silently does nothing (iOS kills the process on a missing usage string rather than showing an error).


Contributing — bug reports, design-parity fixes, and PRs are welcome.

Licensed under MIT © PersonaliAI

About

Official iOS SDK for Chatty AI chatbots — native SwiftUI, no WebView

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages