iOS SDK
A Swift package with no dependencies and no UI. It keeps the conversation — messages, typing, connection — as observable state, and you draw it however your app looks. iOS 15 or later.
Setup
Allow your app in vatio.yml. Its origin is ios-app:// plus its bundle id:
widget:
allowed_origins:
- ios-app://com.acme.appvatio push
vatio tokens create --env live --label iosAdd the package https://github.com/urcalab/vatio-ios in Xcode (File → Add Package Dependencies…), then create a client with the vatpub_ token. It is meant to ship inside the app; a vat_ token must never.
import Vatio
let vatio = Vatio(workspace: "acme", token: "vatpub_REPLACE_ME")
let chat = VatioChat(vatio)Using it
chat.messages // [VatioMessage], oldest first: id, role, content, createdAt, isPending
chat.isTyping // the agent is writing
chat.status // .idle, .connecting, .connected, .reconnecting, .closed
try await chat.send("What does Acme sell?")VatioChat is an ObservableObject, so a SwiftUI view that observes it redraws as the reply arrives. Creating one opens nothing: the conversation starts on the first send, and the visitor's message is in messages at once, with isPending set until the server confirms it. Call await chat.resume() when the view appears to reopen the visitor's conversation, if they have one.
A common layout is a text bar at the bottom of a screen that, on submit, switches to a conversation tab and sends:
AskBar { question in
tab = .conversation
Task { try? await chat.send(question) }
}The package README has the complete SwiftUI example.
| Method | Purpose |
|---|---|
vatio.config() | Agent name, avatar, accent color, greeting and suggestions |
vatio.conversations(visitorToken:) | This visitor's conversations on this device, newest first |
chat.send(_:) | Send a message; starts the conversation on first call |
chat.resume() | Reopen the stored conversation; never starts a new one |
chat.open(_:) | Switch to a conversation from conversations() |
chat.startOver() | Forget the stored conversation; server history is kept |
chat.rate(_:comment:) | Answer the feedback moment: .good, .neutral, .bad |
chat.dismissFeedback() | Decline the feedback moment |
chat.close() | Disconnect |
send throws a VatioError (code, message, status) and removes the pending message, so put the text back in your field. Failures outside a send land in chat.lastError.
Visitor feedback
When a request looks resolved, Vatio offers a feedback moment, at most once per conversation (see the widget guide). chat.feedback holds it; show your own prompt while isOpen:
if let feedback = chat.feedback, feedback.isOpen {
RatingCard(agent: feedback.agentName) { rating, comment in
Task { try await chat.rate(rating, comment: comment) }
}
}It goes back to nil when the visitor writes again without answering.
Signed-in users
Pass a visitor token your backend signed to say who the user is, as described in Sessions and channels:
let chat = VatioChat(vatio, visitorToken: signedToken)A different token subject is a different person with their own conversation, so signing out of your app signs out of the chat. An anonymous conversation is kept when the user signs in.
Delivery and storage
Replies arrive over a WebSocket. When it drops, the SDK reconnects, polls in the meantime, and fetches what it missed; messages are deduplicated by id. replyStyle (.stream by default, .paced, .instant) works as on the web.
The conversation and the visitor id are stored in UserDefaults, per workspace and signed-in user. Chat credentials last 12 hours. Rate limits and message length are the same as on the web.
