Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 59 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,64 +69,95 @@ To enable photo capture or image upload features in the chat interface, **you mu

## ⚙️ Configuration Parameters

| Parameter | Type | Required | Description |
|--|-----------|----------|---------------------------------------------|
| `accountId` | `Int` | ✅ | Unique ID for the Chatwoot account |
| `apiHost` | `String` | ✅ | Chatwoot API host URL |
| `accessToken` | `String` | ✅ | Access token for authentication |
| `pubsubToken` | `String` | ✅ | Token for real-time updates |
| `websocketUrl` | `String` | ✅ | WebSocket URL for real-time communication |
| Parameter | Type | Required | Default | Description |
|----------------------|-----------|----------|---------|---------------------------------------------|
| `accountId` | `Int` | ✅ | - | Unique ID for the Chatwoot account |
| `apiHost` | `String` | ✅ | - | Chatwoot API host URL |
| `accessToken` | `String` | ✅ | - | Access token for authentication |
| `pubsubToken` | `String` | ✅ | - | Token for real-time updates |
| `websocketUrl` | `String` | ✅ | - | WebSocket URL for real-time communication |
| `inboxName` | `String` | ✅ | - | Display name for the inbox/chat channel |
| `backArrowIcon` | `UIImage` | ✅ | - | Back arrow icon for the header bar |
| `connectedIcon` | `UIImage` | ✅ | - | Icon displayed when the app is online |
| `disconnectedIcon` | `UIImage` | ✅ | - | Icon displayed when the app is offline |
| `disableEditor` | `Bool` | ❌ | `false` | Disables the message editor in chat UI |
| `editorDisableUpload`| `Bool` | ❌ | `false` | Disables file upload in the message editor |

---

## 🛠️ Example Usage

### UIKit
### Step 1: Set up the SDK

```swift
import ChatwootSDK
// Create mandatory header icons as UIImage objects from SVG files using SVGKit
let backArrowIcon = createUIImageFromSVG(named: "back_arrow.svg")
let connectedIcon = createUIImageFromSVG(named: "connected_icon.svg")
let disconnectedIcon = createUIImageFromSVG(named: "disconnected_icon.svg")

// 1. Setup in AppDelegate/SceneDelegate
ChatwootSDK.setup(ChatwootConfiguration(
accountId: 1,
apiHost: "https://your-chatwoot.com",
accessToken: "YOUR_ACCESS_TOKEN",
pubsubToken: "YOUR_PUBSUB_TOKEN",
websocketUrl: "wss://your-chatwoot.com"
websocketUrl: "wss://your-chatwoot.com",
inboxName: "Support", // Display name for the inbox
backArrowIcon: backArrowIcon, // Required: UIImage object
connectedIcon: connectedIcon, // Required: UIImage object
disconnectedIcon: disconnectedIcon, // Required: UIImage object
disableEditor: false, // Optional: disable message editor
editorDisableUpload: false // Optional: disable file uploads
))
```

### Step 2: Show the Chat Interface (Recommended: Always Present from UIKit)

// 2. Present chat modally
**Present Modally from UIKit (Recommended for Theming):**

```swift
// UIKit Example
ChatwootSDK.presentChat(from: self, conversationId: 123)
```

### SwiftUI
**SwiftUI Example (Call UIKit Presentation):**

```swift
import SwiftUI
import ChatwootSDK

struct ChatwootWrapper: UIViewControllerRepresentable {
let conversationId: Int

func makeUIViewController(context: Context) -> UIViewController {
return ChatwootSDK.loadChatUI(conversationId: conversationId)
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

struct ContentView: View {
@State private var showChat = false
@State private var conversationId: String = "14635"

var body: some View {
Button("Open Chat") {
showChat = true
presentChatFromRoot()
}
.fullScreenCover(isPresented: $showChat) {
ChatwootWrapper(conversationId: 123)
}

private func presentChatFromRoot() {
guard let conversationIdInt = Int(conversationId) else { return }
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootVC = windowScene.windows.first?.rootViewController {
ChatwootSDK.presentChat(from: rootVC, conversationId: conversationIdInt)
}
}
}
```

The conversationId is required to load the chat UI. Make sure you have a valid conversation ID before calling loadChatUI.
- This approach ensures correct status bar theming and avoids SwiftUI modal limitations.
- Do **not** use `.fullScreenCover` or `UIViewControllerRepresentable` for presenting the chat if you want full theming support.

---

The conversationId is required to load the chat UI. Make sure you have a valid conversation ID before calling loadChatUI.

## 🎨 Theme Customization

```swift
// Set colors using UIColor or hex string
ChatwootSDK.setThemeColor(.systemBlue)
ChatwootSDK.setThemeColor("#1f93ff")
ChatwootSDK.setTextColor(.white)
```

---
116 changes: 115 additions & 1 deletion Sources/ChatwootSDK/ChatwootSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,62 @@ public struct ChatwootConfiguration {
public let accessToken: String
public let pubsubToken: String
public let websocketUrl: String
public let inboxName: String
public let disableEditor: Bool
public let editorDisableUpload: Bool
#if canImport(UIKit)
public let backArrowIcon: UIImage
public let connectedIcon: UIImage
public let disconnectedIcon: UIImage
#endif

#if canImport(UIKit)
public init(
accountId: Int,
apiHost: String,
accessToken: String,
pubsubToken: String,
websocketUrl: String
websocketUrl: String,
inboxName: String,
disableEditor: Bool = false,
editorDisableUpload: Bool = false,
backArrowIcon: UIImage,
connectedIcon: UIImage,
disconnectedIcon: UIImage
) {
self.accountId = accountId
self.apiHost = apiHost
self.accessToken = accessToken
self.pubsubToken = pubsubToken
self.websocketUrl = websocketUrl
self.inboxName = inboxName
self.disableEditor = disableEditor
self.editorDisableUpload = editorDisableUpload
self.backArrowIcon = backArrowIcon
self.connectedIcon = connectedIcon
self.disconnectedIcon = disconnectedIcon
}
#else
public init(
accountId: Int,
apiHost: String,
accessToken: String,
pubsubToken: String,
websocketUrl: String,
inboxName: String,
disableEditor: Bool = false,
editorDisableUpload: Bool = false
) {
self.accountId = accountId
self.apiHost = apiHost
self.accessToken = accessToken
self.pubsubToken = pubsubToken
self.websocketUrl = websocketUrl
self.inboxName = inboxName
self.disableEditor = disableEditor
self.editorDisableUpload = editorDisableUpload
}
#endif
}

public enum ChatwootSDK {
Expand All @@ -36,6 +78,78 @@ public enum ChatwootSDK {
}

#if canImport(UIKit)
private static var currentThemeColor: UIColor = .white // Default as per theme.md
private static var currentTextColor: UIColor? = nil // nil means auto-detect based on theme color
/// Sets the theme color for the Chatwoot UI
/// - Parameter color: The UIColor to use for theming.
public static func setThemeColor(_ color: UIColor) {
currentThemeColor = color
}

/// Sets the theme color for the Chatwoot UI using a hex string
/// - Parameter hex: Hex color string (supports formats: "#RRGGBB", "#RGB", "RRGGBB", "RGB")
/// - Returns: True if the color was set successfully, false if the hex string is invalid
@discardableResult
public static func setThemeColor(hex: String) -> Bool {
guard let color = UIColor(hex: hex) else {
print("[Chatwoot] Warning: Invalid hex color string '\(hex)'. Theme color not changed.")
return false
}
currentThemeColor = color
return true
}

/// Sets the theme color for the Chatwoot UI using a hex string (convenient overload)
/// - Parameter hexString: Hex color string (supports formats: "#RRGGBB", "#RGB", "RRGGBB", "RGB")
/// - Returns: True if the color was set successfully, false if the hex string is invalid
@discardableResult
public static func setThemeColor(_ hexString: String) -> Bool {
return setThemeColor(hex: hexString)
}

/// Sets the text color for the Chatwoot UI
/// - Parameter color: The UIColor to use for text elements (close button, labels, etc.).
public static func setTextColor(_ color: UIColor) {
currentTextColor = color
}

/// Sets the text color for the Chatwoot UI using a hex string
/// - Parameter hex: Hex color string (supports formats: "#RRGGBB", "#RGB", "RRGGBB", "RGB")
/// - Returns: True if the color was set successfully, false if the hex string is invalid
@discardableResult
public static func setTextColor(hex: String) -> Bool {
guard let color = UIColor(hex: hex) else {
print("[Chatwoot] Warning: Invalid hex color string '\(hex)'. Text color not changed.")
return false
}
currentTextColor = color
return true
}

/// Sets the text color for the Chatwoot UI using a hex string (convenient overload)
/// - Parameter hexString: Hex color string (supports formats: "#RRGGBB", "#RGB", "RRGGBB", "RGB")
/// - Returns: True if the color was set successfully, false if the hex string is invalid
@discardableResult
public static func setTextColor(_ hexString: String) -> Bool {
return setTextColor(hex: hexString)
}

/// Gets the current theme color
/// - Returns: The currently set UIColor for the theme.
public static func getCurrentThemeColor() -> UIColor {
return currentThemeColor
}

/// Gets the current text color (auto-detects based on theme if not explicitly set)
/// - Returns: The UIColor to use for text elements.
public static func getCurrentTextColor() -> UIColor {
if let textColor = currentTextColor {
return textColor
}
// Auto-detect based on theme color luminance
return currentThemeColor.isLight ? .black : .white
}

/// Creates and returns a UIViewController for the Chatwoot chat interface
/// - Parameter conversationId: Conversation ID to load a specific conversation
/// - Returns: A UIViewController that can be presented modally or pushed onto a navigation stack
Expand Down
Loading