# iOS SDK

This guide walks you through installing, configuring, and launching the WotNot chat widget natively in your iOS app (SwiftUI or UIKit).

## 1. Installing the SDK

### Step 1: Add the SDK via Swift Package Manager (Recommended)

Supports Xcode 13+ for both SwiftUI and UIKit projects.

#### 1. Open Xcode → Add SPM Package

File → Add Packages…

#### 2. Enter the SDK Package URL

In the dialog box that appears, paste the WotNotSDK repository URL into the search bar/text field.

<https://github.com/wotnotbot/ios-sdk>

#### 3. Configure dependency rule

* Rule: *Up to Next Major Version*
* Set the version to the required SDK version

{% hint style="info" %}
This ensures you automatically get minor improvements without risking breaking changes.
{% endhint %}

#### 4. Add the package

Click Add Package, and Xcode will fetch and integrate the SDK.

### Step 2: Add required permissions

Add the following permission to your `Info.plist`:

```xml
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
```

### Step 3: Import the SDK

Add this import wherever you use the widget:

```swift
import SwiftUI
import WotNotSDK
```

## 2. Configuration

### Step 1: Create a WidgetConfig

This `WidgetConfig` object contains your bot credentials and optional UI settings.

```swift
import WotNotSDK

let config = WidgetConfig(
    botId: "YOUR_BOT_ID",                    // Required: Your bot ID
    visitorKey: "VISITOR_KEY",                // Required: Visitor key (32-bit long string)
    accountKey: "YOUR_ACCOUNT_KEY",           // Required: Your account key
    accountId: YOUR_ACCOUNT_ID,               // Required: Your account ID (Int)
    
    // Optional Configuration
    conversationKey: nil,                     // Optional: Pre-existing conversation key
    isMessageAvatarVisible: true,             // Show/hide message avatars (default: true)
    isHeaderVisible: true,                   // Show/hide headers (default: true)
    dataSessionPayload: nil,                 // Optional: Additional data payload
    badRequestMessage: "Bad request. Please check your configuration.",
    botNotActiveMessage: "Bot is not active."
)
```

#### Where to find these credentials

**YOUR\_BOT\_ID** — open the bot you want to embed, and copy the bot\_id from the URL.

<figure><img src="https://360969599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsrMxU8nO3RjusUiYuXBB%2Fuploads%2FUzpfG5rHg3jZLDSyJlhJ%2FCleanShot%202025-11-20%20at%2011.15.57.png?alt=media&#x26;token=cf13a15a-44be-44c9-84f9-05566a35dc5f" alt=""><figcaption></figcaption></figure>

**YOUR\_ACCOUNT\_ID** — goto Settings > Account Settings and copy the account\_id from the URL.

<figure><img src="https://360969599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsrMxU8nO3RjusUiYuXBB%2Fuploads%2FPAhJtBh3dWWIgeRDDNGW%2FCleanShot%202025-11-20%20at%2011.17.30.png?alt=media&#x26;token=0cc4d972-cfd1-46c4-805a-1f87df94d6e8" alt=""><figcaption></figcaption></figure>

**YOUR\_ACCOUNT\_KEY** — Goto the bot list screen, open the embed option from context menu and copy the highlighted key.

<figure><img src="https://360969599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsrMxU8nO3RjusUiYuXBB%2Fuploads%2FwB9MpzgCNpk5ppVy5O13%2Funknown.png?alt=media&#x26;token=e8b74e1e-6774-473e-ad84-e394e0066819" alt=""><figcaption></figcaption></figure>

### Step 2: Initialize the SDK

Initialize inside your `App` struct or `View`

```swift
import SwiftUI
import WotNotSDK

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                    initializeSDK()
                }
        }
    }
    
    private func initializeSDK() {
        Task {
            do {
                let config = WidgetConfig(
                    botId: "YOUR_BOT_ID",
                    visitorKey: "VISITOR_KEY_32_BIT_LONG",
                    accountKey: "YOUR_ACCOUNT_KEY",
                    accountId: "YOUR_ACCOUNT_ID"
                )
                
                try await wn.initialize(config: config)

            } catch {
                print("SDK Initialization failed: \(error.localizedDescription)")
            }
        }
    }
}
```

{% hint style="warning" %}
**IMPORTANT NOTES:**

* SDK must be initialized before launching any chat UI.
* Initialization is asynchronous (async throws), so use Task or an async context.
* All required fields (i.e. `botID`, `visitorKey`, `accountKey`, `accountID`) must be provided.
* The SDK performs validation and throws errors if credentials are invalid.
  {% endhint %}

## 3. Launching chat screens (SwiftUI)

The SDK provides several functions that return SwiftUI `View` objects. These can be presented using `NavigationLink`, `.sheet()`, or other SwiftUI presentation methods.

### 3.1 Launch conversation list (Full Screen)

Opens the conversation in a full screen mode.

```swift
do {
    let view = try wn.launchConversationListFullScreen()
    // Present the view
} catch {
    print("Failed to launch: \(error)")
}
```

### 3.2 Launch conversation list (Bottom Sheet)

Opens the conversation list in a bottom sheet.

```swift
do {
    let view = try wn.launchConversationListBottomSheet()
    // Present bottom sheet
} catch {
    print("Failed to launch: \(error)")
}
```

### 3.3 Launch conversation detail (Without Passing a Key)

Opens the conversation detail screen without passing a key. Useful when history retention logic is handled by bot preferences.

```swift
Task {
    do {
        let key = try await wn.openConversationDetailScreenWithoutKey()
        let view = try wn.launchConversation(conversationId: key)
        // Present the view
    } catch {
        print("Failed to open: \(error)")
    }
}
```

## 4. Theme customization

Customize colors for bubbles, text, accents, and error states.

### 4.1 Theme colors reference

<table><thead><tr><th width="192.69879150390625">Property</th><th width="227.533935546875">Setter</th><th width="99.999267578125">Default</th><th>Used For</th></tr></thead><tbody><tr><td>accentPrimary</td><td>accentPrimary: Color(...)</td><td>#0075ff</td><td>Buttons, headers, links</td></tr><tr><td>userMessageTextColor</td><td>visitorMessageTextColor: Color(...)</td><td>#FFFFFF</td><td>User message text &#x26; timestamp</td></tr><tr><td>botMessageBackgroundColor</td><td>botMessageBackgroundColor: Color(...)</td><td>#F2F5F8</td><td>Bot message bubble background color</td></tr><tr><td>botMessageTextColor</td><td>botMessageTextColor: Color(...)</td><td>#1C1C1E</td><td>Bot text &#x26; timestamp</td></tr><tr><td>accentSecondary</td><td>accentSecondary: Color(...)</td><td>#d4e3ffff</td><td>Secondary accents</td></tr><tr><td>gray8</td><td>setGray8()</td><td>#F2F5F8</td><td>Bot message background</td></tr><tr><td>failurePrimary</td><td>failurePrimary: Color(...)</td><td>#FF0000</td><td>Error messages</td></tr><tr><td>failureSecondary</td><td>failureSecondary: Color(...)</td><td>#FFEBEE</td><td>Error states</td></tr></tbody></table>

### 4.2 Applying a custom theme

Apply the theme after initialization:

```swift
Task {
    do {
        // 1. Initialize SDK (omitted for brevity)
        // try await wn.initialize(config: config)
        
        // 2. Apply custom theme
        let customTheme = WidgetTheme(
            // Right side (user)
            accentPrimary: Color(red: 0/255, green: 117/255, blue: 255/255), // #0075ff
            visitorMessageTextColor: Color.white,
            
            // Left side (bot)
            botMessageBackgroundColor: Color(red: 242/255, green: 245/255, blue: 248/255), // #F2F5F8
            botMessageTextColor: Color(red: 28/255, green: 28/255, blue: 30/255), // #1C1C1E
            
            // Accent colors
            accentSecondary: Color(red: 212/255, green: 227/255, blue: 255/255), // #d4e3ff
            
            // Failure colors
            failurePrimary: Color.red
        )
        
        try wn.setTheme(customTheme)
    } catch {
        print("SDK initialization or theme setup failed: \(error)")
    }
}

```

## Complete integration example

Below is a streamlined full example.

```swift
import SwiftUI
import WotNotSDK

@main
struct WotNotDemoApp: App {
    
    init() {
        Task { await initializeSDK() }
    }
    
    var body: some Scene {
        WindowGroup {
            // Embed ContentView in a NavigationView for proper UI context
            NavigationView {
                ContentView()
            }
        }
    }
    
    // Asynchronous SDK Initialization Function
    private func initializeSDK() async {
        do {
            let config = WidgetConfig(
                botId: "YOUR_BOT_ID",
                visitorKey: "YOUR_VISITOR_KEY",
                accountKey: "YOUR_ACCOUNT_KEY",
                accountId: "YOUR_ACCOUNT_ID"
            )
            
            // Set a basic theme (optional, for visual consistency)
            let theme = WidgetTheme(
                primaryBlue: Color.blue,
                primaryRed: Color.red
            )
            
            try wn.setTheme(theme)
            try await wn.initialize(config: config)

        } catch {
            print("SDK Initialization Error: \(error.localizedDescription)")
            // The ContentView status rows will reflect the failed state
        }
    }
}

// --- 2. ContentView: Launches Bottom Sheet Directly ---
struct ContentView: View {
    @State private var showingAlert = false
    @State private var alertMessage = ""
    
    var body: some View {
        VStack(spacing: 30) {
            
            // Header
            VStack(spacing: 16) {
                Image(systemName: "message.circle.fill")
                    .font(.system(size: 80))
                    // Use theme color for header icon
                    .foregroundColor(wn.getTheme()?.primaryBlue ?? Color.blue) 
                
                Text("WotNot SDK")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                
                Text("Chat Integration")
                    .font(.subheadline)
                    // Use theme color for secondary text
                    .foregroundColor(wn.getTheme()?.textSecondaryColor ?? Color.secondary)
            }
            
            // SDK Status
            VStack(spacing: 12) {
                StatusRow(
                    title: "SDK Status",
                    status: wn.initializationStatus.description,
                    isSuccess: wn.isSDKInitialized()
                )
                
                StatusRow(
                    title: "Validation Status",
                    status: wn.validationStatus.description,
                    isSuccess: wn.isBotValidated()
                )
            }
            
            // Open Bottom Sheet Button - Simple SDK method call!
            Button(action: {
                openBottomSheetConversationList()
            }) {
                HStack {
                    Image(systemName: "list.bullet.rectangle")
                        .font(.title3)
                    
                    Text("Open Conversation List")
                        .fontWeight(.semibold)
                }
                // Use theme color for foreground and background
                .foregroundColor(wn.getTheme()?.white ?? Color.white)
                .frame(maxWidth: .infinity)
                .padding()
                .background(wn.getTheme()?.primaryBlue ?? Color.blue)
                .cornerRadius(12)
            }
            // Button is disabled until the SDK confirms initialization
            .disabled(!wn.isSDKInitialized()) 
            
            Spacer()
        }
        .padding()
        .navigationTitle("SDK Home")
        .alert("Alert", isPresented: $showingAlert) {
            Button("OK") { }
        } message: {
            Text(alertMessage)
        }
    }
    
    // Function to launch the SDK UI
    private func openBottomSheetConversationList() {
        do {
            // Directly calls the SDK's presentation function
            try wn.presentBottomSheetConversationList()
        } catch {
            alertMessage = "Failed to open conversation list: \(error.localizedDescription)"
            showingAlert = true
        }
    }
}


// --- 3. Helper Structs and Extensions ---

// Status Row Component
struct StatusRow: View {
    let title: String
    let status: String
    let isSuccess: Bool
    
    var body: some View {
        HStack {
            Text(title)
                .fontWeight(.medium)
            Spacer()
            Text(status)
                .foregroundColor(wn.getTheme()?.textSecondaryColor ?? Color.secondary)
            Image(systemName: isSuccess ? "checkmark.circle.fill" : "xmark.circle.fill")
                .foregroundColor(isSuccess ? (wn.getTheme()?.primaryGreen ?? Color.green) : (wn.getTheme()?.primaryRed ?? Color.red))
        }
        .padding()
        .background(wn.getTheme()?.lightGray ?? Color(.systemGray6))
        .cornerRadius(12)
    }
}

// Extension for InitializationStatus
extension InitializationStatus {
    var description: String {
        switch self {
        case .notInitialized:
            return "Not Initialized"
        case .initialized:
            return "Initialized"
        case .initializationFailed(let error):
            return "Failed: \(error)"
        @unknown default:
            return "Unknown"
        }
    }
}

// Extension for ValidationStatus
extension ValidationStatus {
    var description: String {
        switch self {
        case .notValidated:
            return "Not Validated"
        case .validating:
            return "Validating..."
        case .validated:
            return "Validated"
        case .validationFailed(let error):
            return "Failed: \(error)"
        @unknown default:
            return "Unknown"
        }
    }
}

```
