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