
语言 🇨🇳 简体中文
文档
入门
身份验证
使用
Add Comments to Your iOS App
这是 FastComments 的官方 iOS 库。
在您的 iOS 应用中嵌入实时评论、聊天和评价小部件。
仓库
功能 
- 线程化评论树,支持嵌套回复和分页
- 带帖子创建、反应和媒体附件的社交信息流
- 带自动滚动和日期分隔符的实时聊天模式
- 通过 WebSocket 实时更新(新评论、投票、在线状态)
- 单点登录(测试用的简单 SSO、生产用的安全 SSO)
- 富文本编辑,支持加粗、斜体、代码和 @提及
- 可配置样式的投票(上下箭头或爱心)
- 管理操作:标记、置顶、锁定、屏蔽
- 全面的主题支持,含预设和完全自定义
- 用于评论和动态发帖创建的自定义工具栏按钮
- 图片上传
- 支持欧盟区域
- 用户在线状态(在线/离线指示)
- 基于标签的动态过滤
- 本地化支持
安装 
使用 Swift 包管理器将 FastCommentsUI 添加到您的项目。
在 Xcode 中:File > Add Package Dependencies,然后输入仓库 URL。
或者将其添加到您的 Package.swift:
dependencies: [
.package(url: "https://github.com/fastcomments/fastcomments-ios.git", from: "1.0.0")
]
然后将该产品添加到您的目标:
.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 支持三种认证模式:
- Anonymous -- 无 SSO 令牌;用户获得基于会话的身份
- Simple SSO -- 客户端令牌,用于演示和测试(不安全)
- Secure SSO -- 服务器签名的令牌,用于生产环境
Simple SSO
适用于演示和本地测试。任何人都可以使用 Simple 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 也支持可选字段:
id-- user ID(如果未设置则默认为 email)displayName-- 单独的显示名称displayLabel-- 在姓名旁显示的自定义标签(例如 "VIP")websiteUrl-- 用户姓名上的链接locale-- 区域代码isProfileActivityPrivate-- 隐藏个人资料活动(默认值为 true)
Secure 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()
// 通过你的 API 将此令牌返回给你的 iOS 应用
在你的 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 {
// 从你的后端获取令牌
let token = try? await fetchSSOTokenFromYourBackend()
// 使用令牌创建新的配置,或在加载前设置它
isLoadingToken = false
try? await sdk.load()
}
}
}
SecureSSOUserData 支持额外字段:
optedInNotifications-- email 通知的选择加入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 |
Up/down arrow buttons with net count |
._1 |
Single heart button with count |
事件回调
使用修饰器样式的回调来处理用户交互:
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 // Newest first (default)
sdk.defaultSortDirection = .of // Oldest first
sdk.defaultSortDirection = .mr // Most relevant
实时聊天 
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-- 当用户的名字或头像被点击时触发
社交动态 
The 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
// 在你的视图主体中:
.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
// 处理用户点击
})
}
注意:为了使用 .sheet(item:),FeedPost 必须遵循 Identifiable。添加此扩展:
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 事件。处理以下事件:
- 新的评论、编辑和删除
- 投票(新增和移除)
- 置顶、锁定、标记和屏蔽状态更改
- 用户在线状态(加入/离开)
- 线程打开/关闭
- 徽章授予
- 服务器配置更新
控制实时显示
默认情况下,来自其他用户的新评论会立即出现:
sdk.showLiveRightAway = true // 默认:立即显示
将此设置为 false 会将新评论缓存在一个 “N 条新评论” 按钮后面,让用户选择何时显示它们:
sdk.showLiveRightAway = false
用户在线状态
当服务器启用存在跟踪时,用户头像上会自动显示在线/离线指示器。客户端无需额外配置。
分页 
页面大小
// 评论:默认 30
sdk.pageSize = 50
// 信息流:默认 10
feedSDK.pageSize = 20
加载更多评论
UI 会自动显示分页控件。你也可以以编程方式触发分页:
// 加载下一页
try await sdk.loadMore()
// 加载所有剩余(若评论超过2000则为性能原因被禁用)
try await sdk.loadAll()
// 检查状态
sdk.hasMore // 是否存在更多页面
sdk.shouldShowLoadAll()
sdk.getCountRemainingToShow()
子评论分页
嵌套回复采用惰性加载。当用户展开一个线程时,会加载前 5 条子评论。如果存在更多,会出现“加载更多回复”控件。此功能由 UI 自动处理。
状态与可观测性 
Both FastCommentsSDK and FastCommentsFeedSDK are ObservableObject classes with @Published properties. You can observe these in your SwiftUI views for reactive UI updates.
FastCommentsSDK 已发布的属性
| 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 已发布的属性
| Property | Type | Description |
|---|---|---|
feedPosts |
[FeedPost] |
当前加载的 Feed 帖子 |
hasMore |
Bool |
是否存在更多页 |
currentUser |
UserSessionInfo? |
当前已认证用户 |
blockingErrorMessage |
String? |
阻塞性错误信息 |
isLoading |
Bool |
是否有网络请求正在进行 |
newPostsCount |
Int |
自上次加载以来的新帖子数量 |
评论树
可以通过 sdk.commentsTree 访问评论树:
// Flat list of visible nodes for rendering
sdk.commentsTree.visibleNodes
// Lookup a comment by ID
sdk.commentsTree.commentsById["comment-id"]
欧盟区域 
要使用欧盟数据中心,请在配置中设置 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 中调用,或在视图被释放时调用。
图片上传 
评论
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 仓库 获取贡献指南。