This SDK gives you a ready-to-use chat UI + chat core for Swift (SwiftUI, SPM).
Part of the Ethora SDK ecosystem — see all SDKs, tools, and sample apps. Follow cross-SDK updates in the Release Notes.
In Xcode:
- File -> Add Package Dependencies...
- Enter repository URL:
https://github.com/dappros/ethora-sdk-swift
- Add products to your app target:
XMPPChatCoreXMPPChatUI
import XMPPChatCore
import XMPPChatUI// Package.swift
.dependencies([
.package(url: "https://github.com/dappros/ethora-sdk-swift", branch: "main")
]),
.targets([
.target(
name: "YourTarget",
dependencies: [
.product(name: "XMPPChatCore", package: "ethora-sdk-swift"),
.product(name: "XMPPChatUI", package: "ethora-sdk-swift")
]
)
])Use this if you want SDK sources in your app repository.
Copy these folders from this repository:
Sources/XMPPChatCoreSources/XMPPChatUI
Recommended: create a local Swift package in your app workspace and point it to copied sources.
Reliable integration flow in Swift is:
- Build
ChatConfig - Apply config with
ConfigStore.shared.mergeConfig(...)(orupdateConfig(...)) - Render
ChatWrapperView(config:initialRoomJID:onUnreadCountChanged:)
import SwiftUI
import XMPPChatCore
import XMPPChatUI
struct EthoraChatScreen: View {
private let singleRoomJid = "699c6923429c2757ac8ab6a4_playground-room-1"
private var appConfig: ChatConfig {
var config = ChatConfig()
config.appId = BuildConfig.APP_ID
config.baseUrl = BuildConfig.API_BASE_URL
config.customAppToken = BuildConfig.API_TOKEN
config.disableRooms = true
config.defaultLogin = false
config.chatHeaderSettings = ChatHeaderSettingsConfig(
roomTitleOverrides: [singleRoomJid: "Playground Room 1"],
chatInfoButtonDisabled: true,
backButtonDisabled: true
)
config.xmppSettings = XMPPSettings(
xmppServerUrl: BuildConfig.XMPP_DEV_SERVER,
host: BuildConfig.XMPP_HOST,
conference: BuildConfig.XMPP_CONFERENCE
)
if !BuildConfig.USER_TOKEN.isEmpty {
config.jwtLogin = JWTLoginConfig(
token: BuildConfig.USER_TOKEN,
enabled: true
)
}
return config
}
var body: some View {
ChatWrapperView(
config: appConfig,
initialRoomJID: singleRoomJid,
onUnreadCountChanged: { totalUnread in
// use this to update your external tab bar badge
print("Unread count: \(totalUnread)")
}
)
.onAppear {
ConfigStore.shared.mergeConfig(appConfig)
}
}
}disableRooms = true+initialRoomJIDgives single-room mode behavior.jwtLoginis used whenenabled = trueand token is provided.chatHeaderSettings.roomTitleOverrideslets you replace raw JID in header.customAppToken,baseUrl,appId,xmppSettingsare consumed by API/XMPP flows.
var config = ChatConfig()
config.jwtLogin = JWTLoginConfig(token: "YOUR_JWT", enabled: true)
ConfigStore.shared.updateConfig(config)ChatWrapperView will run JWT autologin automatically when this is enabled.
let response = try await AuthAPI.loginWithEmail(
email: email,
password: password
)
await UserStore.shared.setUser(from: response)Use RoomStore.shared.$totalUnreadCount to drive a badge outside chat UI.
import SwiftUI
import XMPPChatCore
struct ChatTabBadge: View {
@ObservedObject private var roomStore = RoomStore.shared
var body: some View {
ZStack(alignment: .topTrailing) {
Image(systemName: "message")
if roomStore.totalUnreadCount > 0 {
Text(displayCount(roomStore.totalUnreadCount, maxCount: 99))
.font(.caption2)
.padding(4)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Capsule())
.offset(x: 10, y: -8)
}
}
}
private func displayCount(_ count: Int, maxCount: Int) -> String {
count > maxCount ? "\(maxCount)+" : "\(count)"
}
}Important behavior:
- Unread values become meaningful after chat initialization and room loading.
- If chat is not initialized yet, count is
0.
ConfigStorepersists codable config fields inUserDefaults.UserStorepersists user/token/refresh-token inUserDefaults.RoomStoretracks total unread state for badge integration.
swift build