Skip to content

Events

The Kindly SDK provides a unified event system that allows you to subscribe to all SDK events and state changes in real-time. This matches the web API pattern and provides a comprehensive way to monitor SDK behavior.

Event Types

The SDK emits the following event types:

  • kindly:load - Settings and configuration loaded
  • kindly:message - New messages (sent/received)
  • kindly:buttonclick - Button interactions
  • kindly:ui - UI actions (open, close, end chat, etc.)
  • kindly:state - State changes (connection, display status)

Basic Usage

Subscribe to All Events

import Combine
import Kindly

class MyEventHandler {
    private var cancellables = Set<AnyCancellable>()

    func setupEventListening() {
        KindlySDK.events
            .sink { event in
                print("Event: \(event.type.rawValue)")
                print("Timestamp: \(event.timestamp)")

                switch event.detail {
                case .load(let settings):
                    print("Settings loaded: \(settings.name)")

                case .message(let newMessage, let chatLog):
                    print("New message: \(newMessage.text ?? "No text")")

                case .buttonClick(let button, let buttonType, _, _):
                    print("Button clicked: \(button.label)")

                case .ui(let action):
                    print("UI Action: \(action.type.rawValue)")

                case .state(let state):
                    print("State changed - Connected: \(state.isConnected)")

                case .globalIcon:
                    // Note: globalIcon events are not currently implemented in mobile SDKs
                    break
                }
            }
            .store(in: &cancellables)
    }
}

Subscribe to State Changes Only

KindlySDK.state
    .sink { state in
        print("Chat displayed: \(state.isChatDisplayed)")
        print("SDK started: \(state.isStarted)")
        print("Socket connected: \(state.isSocketConnected)")
        print("Fully connected: \(state.isConnected)")
        print("Connection state: \(state.connectionState)")
    }
    .store(in: &cancellables)

Event Details

Load Event

Emitted when SDK settings are loaded from the server.

KindlySDK.events
    .filter { $0.type == .load }
    .sink { event in
        if case .load(let settings) = event.detail {
            print("Bot: \(settings.name) (ID: \(settings.id))")
            print("Languages: \(settings.languages.map { $0.code })")
            print("Theme: \(settings.style.background != nil ? "Custom" : "Default")")
        }
    }
    .store(in: &cancellables)

Message Event

Emitted for both incoming and outgoing messages.

KindlySDK.events
    .filter { $0.type == .message }
    .sink { event in
        if case .message(let newMessage, let chatLog) = event.detail {
            print("Message from \(newMessage.from): \(newMessage.text ?? "")")
            print("Chat has \(chatLog.count) total messages")
        }
    }
    .store(in: &cancellables)

Button Click Event

Emitted when users interact with chat buttons.

KindlySDK.events
    .filter { $0.type == .buttonClick }
    .sink { event in
        if case .buttonClick(let button, let buttonType, let chatLog, let lastMessage) = event.detail {
            print("Button: \(button.label)")
            print("Type: \(buttonType)")
            print("Value: \(button.value ?? "")")
        }
    }
    .store(in: &cancellables)

UI Events

Emitted for user interface actions like opening/closing chat, language changes, etc.

KindlySDK.events
    .filter { $0.type == .ui }
    .sink { event in
        if case .ui(let action) = event.detail {
            switch action.type {
            case .openChatBubble:
                print("Chat opened")
            case .closeChatBubble:
                print("Chat closed")
            case .endChat:
                print("Chat ended")
            case .restartChat:
                print("Chat restarted")
            case .changeLanguage:
                print("Language changed to: \(action.languageCode ?? "")")
            case .downloadChat:
                print("Chat downloaded")
            case .deleteChat:
                print("Chat deleted")
            case .showFeedback:
                print("Feedback form shown")
            case .submitFeedback:
                print("Feedback submitted")
            case .dismissFeedback:
                print("Feedback dismissed")
            }
        }
    }
    .store(in: &cancellables)

State Events

Emitted when SDK connection or display state changes.

KindlySDK.state
    .sink { state in
        if state.isConnected {
            print("✅ SDK is fully connected and ready!")
        } else if state.isStarted && !state.isSocketConnected {
            print("⚠️ SDK started but socket disconnected")
        } else if !state.isStarted {
            print("⏳ SDK not started yet")
        }
    }
    .store(in: &cancellables)

Advanced Usage

Track Chat Display Changes

KindlySDK.state
    .map { $0.isChatDisplayed }
    .removeDuplicates() // Only emit when value changes
    .sink { isDisplayed in
        if isDisplayed {
            print("📱 Chat interface is now visible")
        } else {
            print("🙈 Chat interface is now hidden")
        }
    }
    .store(in: &cancellables)

Monitor Connection Health

KindlySDK.state
    .map { state in
        (state.isStarted, state.isSocketConnected, state.isConnected)
    }
    .removeDuplicates { $0 == $1 }
    .sink { isStarted, isSocketConnected, isConnected in
        switch (isStarted, isSocketConnected, isConnected) {
        case (true, true, true):
            print("🟢 Fully connected")
        case (true, false, false):
            print("🟡 Started but disconnected")
        case (false, _, _):
            print("🔴 Not started")
        default:
            print("🟡 Partial connection")
        }
    }
    .store(in: &cancellables)

Filter Multiple Event Types

KindlySDK.events
    .filter { event in
        [.message, .buttonClick, .ui].contains(event.type)
    }
    .sink { event in
        print("User interaction event: \(event.type.rawValue)")
    }
    .store(in: &cancellables)

Event Data Models

KindlyEvent

struct KindlyEvent {
    let type: KindlyEventType
    let timestamp: TimeInterval
    let detail: KindlyEventDetail
}

KindlyEventState

struct KindlyEventState {
    let isChatDisplayed: Bool
    let isStarted: Bool
    let isSocketConnected: Bool
    let isConnected: Bool
    let connectionState: String
}

For more detailed information about event data structures, see the SDK source code documentation.

Memory Management

Always store your Combine cancellables to prevent memory leaks:

private var cancellables = Set<AnyCancellable>()

// In deinit or when done
cancellables.removeAll()