
語言 🇹🇼 繁體中文
文件
入門
驗證
使用方式
Add Comments to Your iOS App
這是 FastComments 的官方 iOS 函式庫。
在你的 iOS 應用中嵌入即時評論、聊天和審核小工具。
儲存庫
功能 
- 具備巢狀回覆與分頁的多層討論串留言樹
- 具備貼文創建、回應與媒體附件的社群動態
- 即時聊天模式,含自動滾動與日期分隔
- 透過 WebSocket 的即時更新(新留言、投票、使用者在線狀態)
- 單一登入(測試用的簡易 SSO、上線用的安全 SSO)
- 富文字編輯,支援粗體、斜體、程式碼與 @提及
- 支援可配置樣式的投票(上下箭頭或愛心)
- 管理操作:檢舉、置頂、鎖定、封鎖
- 完整主題化,提供預設樣式與完整客製化
- 自訂工具列按鈕,用於留言與動態貼文的建立
- 圖片上傳
- 支援歐盟地區
- 使用者在席狀態(在線/離線指示)
- 基於標籤的動態過濾
- 在地化支援
安裝 
使用 Swift Package Manager 將 FastCommentsUI 新增到您的專案。
在 Xcode 中:File > Add Package Dependencies,然後輸入儲存庫 URL。
或者將它新增到您的 Package.swift:
dependencies: [
.package(url: "https://github.com/fastcomments/fastcomments-ios.git", from: "1.0.0")
]
接著將該產品加入到您的 target:
.target(
name: "YourApp",
dependencies: [
.product(name: "FastCommentsUI", package: "fastcomments-ios")
]
)
在需要的地方匯入這兩個模組:
import FastCommentsUI
import FastCommentsSwift
快速開始 
顯示評論小工具的最小設定:
import SwiftUI
import FastCommentsUI
struct ContentView: View {
@StateObject private var sdk = FastCommentsSDK(
config: FastCommentsWidgetConfig(
tenantId: "demo",
urlId: "my-page-1",
url: "https://example.com/page-1",
pageTitle: "My Page"
)
)
var body: some View {
FastCommentsView(sdk: sdk)
.task {
try? await sdk.load()
}
}
}
將 "demo" 替換為您的 FastComments 租戶 ID。urlId 用來識別儲存評論的頁面或討論串。
驗證(SSO) 
FastComments 支援三種驗證模式:
- 匿名 -- 無 SSO 令牌;使用者會獲得基於會話的身分
- 簡易 SSO -- 用於展示與本機測試的用戶端令牌(不安全)
- 安全 SSO -- 適用於生產環境、由伺服器簽署的令牌
簡易 SSO
適用於展示和本機測試。任何人都可以使用簡易 SSO 偽裝成任何使用者,因此不要在生產環境中使用。
import FastCommentsSwift
let userData = SimpleSSOUserData(
username: "Jane Doe",
email: "jane@example.com",
avatar: "https://example.com/avatar.jpg"
)
let sso = FastCommentsSSO.createSimple(simpleSSOUserData: userData)
let token = try? sso.prepareToSend()
let config = FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "my-page-1",
sso: token
)
let sdk = FastCommentsSDK(config: config)
SimpleSSOUserData also supports optional fields:
id-- 使用者 ID(如果未設定則預設為電子郵件)displayName-- 分開的顯示名稱displayLabel-- 顯示在名稱旁的自訂標籤(例如「VIP」)websiteUrl-- 使用者名稱的連結locale-- 語系代碼isProfileActivityPrivate-- 隱藏個人檔案活動(預設為 true)
安全 SSO
在生產環境中,您的後端會使用 API 秘密產生已簽署的 SSO 令牌。iOS 應用程式會從您的伺服器取得該令牌並傳遞至設定中。
在您的後端(使用 FastComments Swift SDK 或任何語言):
let userData = SecureSSOUserData(
id: "user-123",
email: "user@example.com",
username: "Display Name",
avatar: "https://example.com/avatar.jpg"
)
let sso = try FastCommentsSSO.createSecure(apiKey: "YOUR_API_KEY", secureSSOUserData: userData)
let token = try sso.prepareToSend()
// Return this token to your iOS app via your API
在您的 iOS 應用程式中:
struct MyView: View {
@StateObject private var sdk = FastCommentsSDK(
config: FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "my-page-1"
)
)
@State private var isLoadingToken = true
var body: some View {
Group {
if isLoadingToken {
ProgressView("Loading...")
} else {
FastCommentsView(sdk: sdk)
}
}
.task {
// Fetch the token from your backend
let token = try? await fetchSSOTokenFromYourBackend()
// Create a new config with the token, or set it before load
isLoadingToken = false
try? await sdk.load()
}
}
}
SecureSSOUserData supports additional fields:
optedInNotifications-- 選擇接收電子郵件通知displayLabel-- 自訂標籤displayName-- 顯示名稱websiteUrl-- 網站 URLgroupIds-- 所屬群組isAdmin-- 管理員權限isModerator-- 版主權限isProfileActivityPrivate-- 個人檔案活動隱私
分層留言 
基本用法
struct CommentsPage: View {
@StateObject private var sdk = FastCommentsSDK(
config: FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "article-42",
url: "https://example.com/article/42",
pageTitle: "Article Title"
)
)
var body: some View {
FastCommentsView(sdk: sdk)
.task {
try? await sdk.load()
}
}
}
投票樣式
預設的投票樣式顯示上下箭頭。傳入 ._1 可使用愛心樣式的投票:
FastCommentsView(sdk: sdk, voteStyle: ._1)
| 樣式 | 外觀 |
|---|---|
._0 |
上下箭頭按鈕,顯示淨票數 |
._1 |
單一愛心按鈕,並顯示數量 |
事件回調
使用 modifier 風格的回調來處理使用者互動:
FastCommentsView(sdk: sdk)
.onCommentPosted { comment in
print("New comment: \(comment.commentHTML)")
}
.onReplyClick { renderableComment in
print("Replying to: \(renderableComment.comment.id)")
}
.onUserClick { context, userInfo, source in
// source 是 .name 或 .avatar
print("Tapped \(userInfo.displayName)")
}
套用主題
透過 SwiftUI 環境傳入主題:
FastCommentsView(sdk: sdk)
.fastCommentsTheme(myTheme)
.task { try? await sdk.load() }
或直接在 SDK 上設定:
sdk.theme = FastCommentsTheme.modern
排序方向
sdk.defaultSortDirection = .nf // 由新到舊(預設)
sdk.defaultSortDirection = .of // 由舊到新
sdk.defaultSortDirection = .mr // 最相關
即時聊天 
LiveChatView 提供即時聊天體驗,具自動滾動、日期分隔符和緊湊的佈局。它會自動將 SDK 設定為由最舊至最新排序並立即顯示實況。
struct ChatView: View {
@StateObject private var sdk: FastCommentsSDK = {
let config = FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "chat-room-1",
sso: ssoToken // 建議使用 SSO,讓使用者有名稱
)
return FastCommentsSDK(config: config)
}()
var body: some View {
LiveChatView(sdk: sdk)
.onCommentPosted { comment in
print("Sent: \(comment.commentHTML)")
}
.task {
try? await sdk.load()
}
}
}
LiveChatView 支援以下回呼:
.onCommentPosted-- 在使用者發送訊息時觸發.onCommentDeleted-- 當訊息被刪除時觸發.onUserClick-- 當使用者的名稱或頭像被點擊時觸發
社群動態 
Feed 系統是獨立的 SDK(FastCommentsFeedSDK),並具有自己的檢視。
載入並顯示 Feed
struct FeedPage: View {
@StateObject private var sdk: FastCommentsFeedSDK = {
let config = FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "my-feed",
sso: ssoToken
)
return FastCommentsFeedSDK(config: config)
}()
@State private var commentsPost: FeedPost?
var body: some View {
FastCommentsFeedView(sdk: sdk)
.onPostSelected { post in
commentsPost = post
}
.onCommentsRequested { post in
commentsPost = post
}
.onSharePost { post in
// 顯示分享畫面
}
.onUserClick { context, userInfo, source in
// 導覽至使用者檔案
}
.onMediaClick { mediaItem, index in
// 顯示全螢幕圖像檢視器
}
.task {
try? await sdk.load()
}
}
}
Feed 檢視會自動包含下拉重新整理與無限滾動。
建立文章
使用 FeedPostCreateView 顯示文章建立表單:
@State private var showCreatePost = false
// In your view body:
.sheet(isPresented: $showCreatePost) {
FeedPostCreateView(
sdk: sdk,
onPostCreated: { post in
showCreatePost = false
Task { try? await sdk.refresh() }
},
onCancelled: {
showCreatePost = false
}
)
}
對文章做出反應
SDK 使用樂觀更新來處理反應:
try await sdk.reactPost(postId: post.id, reactionType: "l")
// Check reaction state
let hasLiked = sdk.hasUserReacted(postId: post.id, reactType: "l")
let likeCount = sdk.getLikeCount(postId: post.id)
在文章上開啟留言
使用 CommentsSheet 顯示 Feed 文章的留言。它會使用 Feed SDK 的設定在內部建立一個 FastCommentsSDK 實例:
.sheet(item: $commentsPost) { post in
CommentsSheet(post: post, feedSDK: sdk, onUserClick: { context, userInfo, source in
// 處理使用者點擊
})
}
注意:FeedPost 必須遵從 Identifiable,以便使用 .sheet(item:)。新增此延伸:
extension FeedPost: @retroactive Identifiable {}
根據標籤篩選 Feed
實作 TagSupplier 協定以依標籤篩選 Feed 文章:
struct TeamTagSupplier: TagSupplier {
func getTags(currentUser: UserSessionInfo?) -> [String]? {
guard let user = currentUser else { return nil }
return ["team:\(user.id ?? "")", "public"]
}
}
sdk.tagSupplier = TeamTagSupplier()
對於未篩選的全域 Feed,回傳 nil。
儲存與還原 Feed 狀態
在檢視生命週期事件之間保存分頁狀態:
let state = sdk.savePaginationState()
// Later...
sdk.restorePaginationState(state)
刪除文章
sdk.onPostDeleted = { postId in
print("Post \(postId) was deleted")
}
主題設定 
主題預設
提供四個內建預設:
// 系統預設
sdk.theme = FastCommentsTheme.default
// 有陰影且大圓角的卡片
sdk.theme = FastCommentsTheme.modern
// 扁平,無陰影,小圓角,無討論串線
sdk.theme = FastCommentsTheme.minimal
// 將所有操作顏色設定為單一品牌色
sdk.theme = FastCommentsTheme.allPrimary(.indigo)
評論顯示樣式
var theme = FastCommentsTheme()
theme.commentStyle = .flat // 扁平列表,含分隔線(預設)
theme.commentStyle = .card // 帶陰影的圓角卡片
theme.commentStyle = .bubble // 聊天泡泡樣式
顏色
所有顏色屬性都是選填。未設定的值會回退到合適的系統預設。
var theme = FastCommentsTheme()
// 品牌色
theme.primaryColor = .indigo
theme.primaryLightColor = .indigo.opacity(0.6)
theme.primaryDarkColor = Color(red: 0.2, green: 0.1, blue: 0.5)
// 背景
theme.commentBackgroundColor = Color(.secondarySystemGroupedBackground)
theme.containerBackgroundColor = Color(.systemGroupedBackground)
// 操作按鈕
theme.actionButtonColor = .indigo
theme.replyButtonColor = .indigo
theme.toggleRepliesButtonColor = .indigo.opacity(0.8)
theme.loadMoreButtonTextColor = .indigo
// 投票
theme.voteActiveColor = .red
theme.voteCountColor = .primary
theme.voteCountZeroColor = .secondary
theme.voteDividerColor = Color(.separator)
// 連結
theme.linkColor = .indigo
theme.linkColorPressed = .indigo.opacity(0.5)
// 對話框
theme.dialogHeaderBackgroundColor = .indigo
theme.dialogHeaderTextColor = .white
// 輸入列
theme.inputBarBackgroundColor = Color(.systemBackground)
theme.inputBarBorderColor = Color(.separator)
// 其他
theme.onlineIndicatorColor = .green
theme.separatorColor = Color(.separator)
theme.badgeBackgroundColor = .gray.opacity(0.2)
theme.threadLineColor = .indigo.opacity(0.15)
排版
theme.commenterNameFont = .subheadline.weight(.bold)
theme.bodyFont = .body
theme.captionFont = .caption
theme.actionFont = .caption.weight(.medium)
佈局與間距
theme.cornerRadius = .large // 可選:.none, .small, .medium, .large
theme.commentSpacing = 4 // 評論列之間的間距(單位:點)
theme.nestingIndent = 20 // 每層巢狀的縮排(單位:點)
theme.avatarSize = 36 // 根評論的頭像直徑
theme.replyAvatarSize = 28 // 巢狀回覆的頭像直徑
視覺效果
theme.showShadows = true // 卡片上的細微陰影
theme.showThreadLine = true // 連接巢狀回覆的垂直線
theme.animateVotes = true // 投票變化時的彈簧動畫
套用主題
兩種方式:
// 透過 SwiftUI 環境(建議用於檢視層級)
FastCommentsView(sdk: sdk)
.fastCommentsTheme(theme)
// 直接在 SDK 上設定
sdk.theme = theme
自訂工具列按鈕 
評論工具列按鈕
實作 CustomToolbarButton 協定,以新增按鈕到評論輸入工具列:
struct EmojiButton: CustomToolbarButton {
let id = "emoji"
let iconSystemName = "face.smiling" // SF Symbol 名稱
let contentDescription = "Add Emoji"
let badgeText: String? = nil // 可選的徽章數量
func onClick(text: Binding<String>) {
text.wrappedValue += "\u{1F44D}"
}
// 可選的覆寫(預設為 true)
func isEnabled() -> Bool { true }
func isVisible() -> Bool { true }
}
在建立視圖時傳入自訂按鈕:
FastCommentsView(
sdk: sdk,
customToolbarButtons: [EmojiButton(), CodeBlockButton()]
)
或在 SDK 上全域新增(套用到所有實例):
sdk.addGlobalCustomToolbarButton(EmojiButton())
sdk.removeGlobalCustomToolbarButton(id: "emoji")
sdk.clearGlobalCustomToolbarButtons()
Feed 工具列按鈕
針對貼文建立表單,實作 FeedCustomToolbarButton:
struct HashtagButton: FeedCustomToolbarButton {
let id = "hashtag"
let iconSystemName = "number"
let contentDescription = "Add Hashtag"
func onClick(content: Binding<String>) {
content.wrappedValue += "#"
}
}
傳遞它們到建立視圖:
FeedPostCreateView(
sdk: sdk,
customToolbarButtons: [HashtagButton()],
onPostCreated: { _ in },
onCancelled: { }
)
或在 feed SDK 上全域設定:
sdk.globalFeedToolbarButtons = [HashtagButton()]
內容審核 
所有使用者可用的操作
- 檢舉/取消檢舉 -- 回報評論以供審查
try await sdk.flagComment(commentId: commentId)
try await sdk.unflagComment(commentId: commentId)
- 封鎖/取消封鎖 -- 隱藏該使用者的所有評論(以檢視者為單位)
try await sdk.blockUser(commentId: commentId)
try await sdk.unblockUser(commentId: commentId)
僅限管理員的操作
- 置頂/取消置頂 -- 將評論釘選於討論串頂端
try await sdk.pinComment(commentId: commentId)
try await sdk.unpinComment(commentId: commentId)
- 鎖定/解鎖 -- 防止在該評論上新增回覆
try await sdk.lockComment(commentId: commentId)
try await sdk.unlockComment(commentId: commentId)
所有管理操作也可以透過 UI 中的評論快顯選單執行。僅當目前使用者為站點管理員(透過 SSO isAdmin 標誌或在儀表板設定)時,才會顯示管理員操作。
即時更新 
在呼叫 sdk.load() 之後,SDK 會自動訂閱為已設定 urlId 所開啟的 WebSocket 事件。處理以下事件:
- 新的留言、編輯與刪除
- 投票(新增與移除)
- 釘選、鎖定、檢舉與封鎖狀態變更
- 使用者在席(加入/離開)
- 討論串開啟/關閉
- 徽章授予
- 伺服器設定更新
Controlling Live Display
預設情況下,其他使用者的新留言會立即顯示:
sdk.showLiveRightAway = true // 預設:立即顯示
將此設為 false,可將新留言暫存於 "N 則新留言" 按鈕之後,讓使用者選擇何時顯示它們:
sdk.showLiveRightAway = false
使用者在席
當伺服器啟用出席追蹤時,使用者頭像上會自動顯示在線/離線指示。不需要在客戶端進行額外設定。
分頁 
頁面大小
// 評論:預設 30
sdk.pageSize = 50
// Feed:預設 10
feedSDK.pageSize = 20
載入更多評論
使用者介面會自動顯示分頁控制項。您也可以以程式觸發分頁:
// 載入下一頁
try await sdk.loadMore()
// 載入所有剩餘(若超過 2000 則為效能考量而停用)
try await sdk.loadAll()
// 檢查狀態
sdk.hasMore // 是否還有更多頁面
sdk.shouldShowLoadAll()
sdk.getCountRemainingToShow()
子評論分頁
巢狀回覆採延遲載入。當使用者展開討論串時,會載入前 5 個子回覆。如果還有更多回覆,會顯示「載入更多回覆」控制項。此行為由使用者介面自動處理。
狀態與可觀察性 
Both FastCommentsSDK and FastCommentsFeedSDK are ObservableObject classes with @Published properties. You can observe these in your SwiftUI views for reactive UI updates.
FastCommentsSDK Published Properties
| Property | Type | Description |
|---|---|---|
commentCountOnServer |
Int |
伺服器上的評論總數 |
newRootCommentCount |
Int |
緩衝的新評論(當 showLiveRightAway 為 false 時) |
currentUser |
UserSessionInfo? |
當前已驗證的使用者 |
isSiteAdmin |
Bool |
當前使用者是否為網站管理員 |
isClosed |
Bool |
評論串是否已關閉 |
hasBillingIssue |
Bool |
是否存在帳單問題 |
isLoading |
Bool |
是否有網路請求正在進行 |
hasMore |
Bool |
是否存在更多評論頁面 |
blockingErrorMessage |
String? |
阻礙 UI 正常運作的錯誤 |
warningMessage |
String? |
非阻斷的警告訊息 |
isDemo |
Bool |
是否以示範模式執行 |
commentsVisible |
Bool |
評論可見性的切換 |
toolbarEnabled |
Bool |
是否顯示格式工具列 |
FastCommentsFeedSDK Published Properties
| Property | Type | Description |
|---|---|---|
feedPosts |
[FeedPost] |
目前已載入的 Feed 帖文 |
hasMore |
Bool |
是否存在更多頁面 |
currentUser |
UserSessionInfo? |
當前已驗證的使用者 |
blockingErrorMessage |
String? |
阻斷性錯誤訊息 |
isLoading |
Bool |
是否有網路請求正在進行 |
newPostsCount |
Int |
自上次載入以來的新帖數量 |
Comment Tree
The comment tree is accessible via sdk.commentsTree:
// 用於渲染的可見節點扁平列表
sdk.commentsTree.visibleNodes
// 透過 ID 查找評論
sdk.commentsTree.commentsById["comment-id"]
歐盟區域 
要使用歐盟 (EU) 資料中心,請在您的設定中將 region 欄位設為:
let config = FastCommentsWidgetConfig(
tenantId: "YOUR_TENANT_ID",
urlId: "my-page",
region: "eu"
)
這會將所有 API 請求和 WebSocket 連線導向 eu.fastcomments.com。
清理 
當您完成使用 SDK 實例時(例如視圖即將被關閉),呼叫 cleanup() 以關閉 WebSocket 連線並取消背景任務:
sdk.cleanup()
對於由 SwiftUI 的 @StateObject 管理的視圖,通常會在 .onDisappear 或視圖被釋放(deallocated)時呼叫。
圖片上傳 
評論
let imageUrl = try await sdk.uploadImage(imageData: jpegData, filename: "photo.jpg")
回傳已上傳圖片的 URL 字串。
動態貼文
let mediaItem = try await feedSDK.uploadImage(imageData: jpegData, filename: "photo.jpg")
// 並行上傳多張圖片
let mediaItems = try await feedSDK.uploadImages(images: [
(jpegData1, "photo1.jpg"),
(jpegData2, "photo2.jpg")
])
使用者提及 
搜尋使用者以支援 @mention 的自動完成功能:
let results = try await sdk.searchUsers(query: "jan")
// 回傳 [UserSearchResult],包含 userId、username、avatar 等。
內建的 CommentInputBar 會自動處理 @mention 的自動完成功能。
編輯與刪除留言 
Edit
try await sdk.editComment(commentId: commentId, newText: "Updated text")
伺服器會重新渲染 HTML。本地留言會自動更新。
Delete
try await sdk.deleteComment(commentId: commentId)
刪除留言也會從本地樹狀結構中移除其子孫。
兩個操作在 UI 的留言快顯選單中可用,當前使用者為留言作者(或網站管理員)時。
錯誤處理 
SDK 方法會拋出 FastCommentsError,它符合 LocalizedError:
do {
try await sdk.load()
} catch let error as FastCommentsError {
print(error.translatedError ?? error.reason ?? "Unknown error")
} catch {
print(error.localizedDescription)
}
FastCommentsError 屬性:
code-- 來自 API 的錯誤代碼reason-- 英文錯誤描述translatedError-- 伺服器提供的本地化錯誤訊息
封鎖錯誤也會自動透過 sdk.blockingErrorMessage 顯示出來,內建視圖會將其展示給使用者。
需要幫助?
如果您在使用 iOS 函式庫時遇到任何問題或有任何疑問,請:
貢獻
歡迎貢獻!請造訪 GitHub 倉庫 以取得貢獻指南。