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 Value | Type | Description |
|---|---|---|
authToken | String? | 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 Value | Type | Description |
|---|---|---|
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 Value | Type | Description |
|---|---|---|
count | Int | Current 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 Value | Type | Description |
|---|---|---|
reason | String? | 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 Value | Type | Description |
|---|---|---|
code | String | Error code (e.g., "network_error", "auth_failed"). |
message | String | Human-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
Navigation: Push chat page, pop on close
// 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
}
}
}
}
Modal: Present as sheet, dismiss on close
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