Skip to main content

iOS Events

The Lovina Chat SDK emits events through the onEvent closure on LovinaChatWidget (UIKit) or the onEvent parameter on LovinaChatView (SwiftUI).

Event Listener Pattern

UIKit

chatWidget.onEvent = { event in
switch event {
case .loaded(let authToken):
// ...
case .messageReceived(let message):
// ...
case .unreadCountChanged(let count):
// ...
case .closed(let reason):
// ...
case .error(let code, let message):
// ...
}
}

SwiftUI

LovinaChatView(
config: config,
onEvent: { event in
switch event {
case .messageReceived(let message):
print("New message: \(message)")
default:
break
}
}
)

ChatEvent Types

.loaded

Emitted when the widget has finished loading and is ready for interaction.

Associated ValueTypeDescription
authTokenString?Session auth token, if the user has an active session.
case .loaded(let authToken):
print("Widget loaded, token: \(authToken ?? "none")")

.messageReceived

Emitted when a new message is received (from an agent or bot).

Associated ValueTypeDescription
message[String: Any]Full message payload including content, sender, attachments, etc.
case .messageReceived(let message):
let content = message["content"] as? String
print("Message: \(content ?? "")")

.unreadCountChanged

Emitted when the number of unread messages changes. Useful for updating notification badges.

Associated ValueTypeDescription
countIntCurrent number of unread messages.
case .unreadCountChanged(let count):
tabBarItem.badgeValue = count > 0 ? "\(count)" : nil

.closed

Emitted when the chat is closed, either by the user or the agent.

Associated ValueTypeDescription
reasonString?Optional reason for closure (e.g., "user", "resolved").
case .closed(let reason):
print("Chat closed: \(reason ?? "unknown")")
dismiss(animated: true)

.error

Emitted when an error occurs within the widget.

Associated ValueTypeDescription
codeStringError code (e.g., "network_error", "auth_failed").
messageStringHuman-readable error description.
case .error(let code, let message):
print("Error [\(code)]: \(message)")

Full Example

chatWidget.onEvent = { [weak self] event in
switch event {
case .loaded(let authToken):
print("Ready, token: \(authToken ?? "none")")

case .messageReceived(let message):
let content = message["content"] as? String ?? ""
self?.showLocalNotification(body: content)

case .unreadCountChanged(let count):
self?.tabBarItem.badgeValue = count > 0 ? "\(count)" : nil

case .closed:
self?.dismiss(animated: true)

case .error(let code, let message):
self?.showAlert(title: "Error \(code)", message: message)
}
}

Common Patterns

// Push chat onto NavigationStack
struct ChatButton: View {
@State private var showChat = false

var body: some View {
NavigationStack {
Button("Open Chat") { showChat = true }
.navigationDestination(isPresented: $showChat) {
LovinaChatView(config: config) { event in
if case .closed = event {
showChat = false // pops back
}
}
.navigationBarBackButtonHidden(true) // widget has its own ← button
}
}
}
}
Button("Chat") { showChat = true }
.sheet(isPresented: $showChat) {
LovinaChatView(config: config) { event in
if case .closed = event {
showChat = false // dismisses sheet
}
}
}

Show unread badge on tab bar

case .unreadCountChanged(let count):
tabBarItem.badgeValue = count > 0 ? "\(count)" : nil