FastComments.com

Add Comments to Your iOS App


Ово је званична iOS библиотека за FastComments.

Уградите коментаре уживо, ћаскање и видгете за рецензије у вашу iOS апликацију.

Репозиторијум

Погледајте на GitHub


Функције Internal Link

  • Нити коментара са уграђеним одговорима и пагинацијом
  • Друштвени фид са креирањем објава, реакцијама и прилозима медија
  • Режим живог ћаскања са аутоматским скроловањем и раздвајачима по датуму
  • Ажурирања у реалном времену преко WebSocket-а (нови коментари, гласови, присутност)
  • Једнократно пријављивање (Simple SSO за тестирање, Secure SSO за продукцију)
  • Уређивање богатог текста са подебљаним, курзивом, кодом и @поменама
  • Гласање са прилагодљивим стиловима (стрелице горе/доле или срца)
  • Модерацијске радње: пријављивање, причвршћивање, закључавање, блокирање
  • Свеобухватно прилагођавање тема са предефинисаним подешавањима и потпуном прилагодбом
  • Прилагођена дугмад на алатној траци за коментаре и креирање објава у фиду
  • Отпремање слика
  • Подршка за ЕУ регион
  • Присуство корисника (индикатори онлајн/офлајн)
  • Филтрирање фида по таговима
  • Подршка за локализацију

Захтеви Internal Link


  • iOS 16+ ili macOS 14+
  • Swift 5.9+
  • SwiftUI

Инсталација Internal Link

Dodajte FastCommentsUI u svoj projekat koristeći Swift Package Manager.

U Xcode-u: File > Add Package Dependencies, zatim unesite URL repozitorijuma.

Ili ga dodajte u vaš Package.swift:

dependencies: [
    .package(url: "https://github.com/fastcomments/fastcomments-ios.git", from: "1.0.0")
]

Zatim dodajte product u svoj target:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "FastCommentsUI", package: "fastcomments-ios")
    ]
)

Uvezite oba modula gde je potrebno:

import FastCommentsUI
import FastCommentsSwift

Брзи почетак Internal Link

Минимална подешавања за приказ widget-а за коментаре:

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()
            }
    }
}

Zамијените "demo" вашим FastComments tenant ID-јем. urlId идентификује страницу или нит на којој су коментари смештени.



Аутентификација (SSO) Internal Link

FastComments подржава три режима аутентификације:

  1. Анонимно -- без SSO токена; корисници добијају идентитете засноване на сесији
  2. Једноставан SSO -- клијентски токен за демое и тестирање (није безбедно)
  3. Безбедан 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 такође подржава опционална поља:

  • id -- ID корисника (подразумевано email ако није подешено)
  • displayName -- одвојено име за приказ
  • displayLabel -- прилагођена ознака приказана поред имена (нпр. "VIP")
  • websiteUrl -- линк на име корисника
  • locale -- код локала
  • isProfileActivityPrivate -- сакрити активност профила (подразумевано true)

Безбедан SSO

У продукцији, ваш бекенд генерише потписани SSO токен користећи ваш API тајни кључ. 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 {
            // Преузми токен са вашег бекенда
            let token = try? await fetchSSOTokenFromYourBackend()
            // Направи нову конфигурацију са токеном, или је подеси пре учитавања
            isLoadingToken = false
            try? await sdk.load()
        }
    }
}

SecureSSOUserData подржава додатна поља:

  • optedInNotifications -- опција пријаве за примање обавештења путем е-поште
  • displayLabel -- прилагођена ознака
  • displayName -- име за приказ
  • websiteUrl -- URL веб сајта
  • groupIds -- чланства у групама
  • isAdmin -- админ привилегије
  • isModerator -- привилегије модератора
  • isProfileActivityPrivate -- приватност активности профила


Коментари у нитима Internal Link

Основна употреба

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)
Style Appearance
._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
        // извор је .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  // Најрелевантније


Уживо ћаскање Internal Link

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 -- активира се када се кликне на име корисника или аватар


Друштвени фид Internal Link

Feed систем је посебан SDK (FastCommentsFeedSDK) са својим приказом.

Учитавање и приказивање фида

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()
            }
    }
}

Приказ фида аутоматски садржи повлачење за освежавање и бесконачно скроловање.

Креирање објава

Користите 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 да прикажете коментаре за објаву у фиду. Он интерно креира инстанцу FastCommentsSDK користећи конфигурацију feed SDK-а:

.sheet(item: $commentsPost) { post in
    CommentsSheet(post: post, feedSDK: sdk, onUserClick: { context, userInfo, source in
        // Обради кликање на корисника
    })
}

Напомена: FeedPost мора да имплементира Identifiable за .sheet(item:). Додајте ово проширење:

extension FeedPost: @retroactive Identifiable {}

Филтрирање фида по таговима

Имплементирајте протокол TagSupplier да бисте филтрирали објаве у фиду по таговима:

struct TeamTagSupplier: TagSupplier {
    func getTags(currentUser: UserSessionInfo?) -> [String]? {
        guard let user = currentUser else { return nil }
        return ["team:\(user.id ?? "")", "public"]
    }
}

sdk.tagSupplier = TeamTagSupplier()

Вратите nil за нефилтрирани глобални фид.

Сачување и враћање стања фида

Сачувајте стање пагинације преко догађаја животног циклуса приказа:

let state = sdk.savePaginationState()
// Later...
sdk.restorePaginationState(state)

Брисање објава

sdk.onPostDeleted = { postId in
    print("Post \(postId) was deleted")
}

Прилагођавање тема Internal Link

Предлошци тема

Четири уграђена пресета су доступна:

// Системске подразумеване вредности
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


Прилагођена дугмад на алатној траци Internal Link

Дугмад траке алата за коментаре

Имплементирајте протокол CustomToolbarButton да бисте додали дугмад у траку алата за унос коментара:

struct EmojiButton: CustomToolbarButton {
    let id = "emoji"
    let iconSystemName = "face.smiling"       // Име SF симбола
    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()

Дугмад траке алата за фид

Имплементирајте 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: { }
)

Или их подесите глобално на SDK за фид:

sdk.globalFeedToolbarButtons = [HashtagButton()]

Модерација Internal Link

Радње доступне свим корисницима

  • Пријави/Уклони пријаву -- пријавити коментар на преглед
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)

Све модерацијске радње такође су доступне преко контекстног менија коментара у корисничком интерфејсу. Администраторске радње се појављују само када је тренутни корисник администратор сајта (постављено преко SSO isAdmin ознаке или конфигурације контролне табле).



Ажурирања у реалном времену Internal Link

Након позива sdk.load(), SDK аутоматски претплаћује се на WebSocket догађаје за конфигурисани urlId. Следећи догађаји се обрађују:

  • Нови коментари, измјене и брисања
  • Гласови (нови и уклоњени)
  • Промјене стања: закачење (pin), закључавање, пријављивање (flag) и блокирање
  • Присуство корисника (приступ/напуштање)
  • Отварање/затварање нити
  • Додијељивања значки
  • Ажурирања конфигурације сервера

Контрола живог приказа

Подразумјевано, нови коментари од других корисника се појављују одмах:

sdk.showLiveRightAway = true   // Подразумјевано: прикажи одмах

Поставите ово на false да бисте кеширали нове коментаре иза дугмета "N нових коментара", омогућавајући кориснику да одабере када ће их открити:

sdk.showLiveRightAway = false

Присуство корисника

Онлајн/офлајн индикатори се аутоматски појављују на аватарима корисника када сервер омогући праћење присуства. На клијенту није потребна додатна конфигурација.



Пагинација Internal Link

Величина странице

// Коментари: подразумевано 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-у.



Стање и опсервабилност Internal Link

Обa FastCommentsSDK и FastCommentsFeedSDK су класе ObservableObject са @Published својствима. Можете посматрати ове у вашим SwiftUI приказима за реактивна ажурирања интерфејса.

FastCommentsSDK Објављена својства

Својство Тип Опис
commentCountOnServer Int Укупни број коментара на серверу
newRootCommentCount Int Буферирани нови коментари (када је showLiveRightAway false)
currentUser UserSessionInfo? Тренутни аутентификовани корисник
isSiteAdmin Bool Да ли је тренутни корисник админ сајта
isClosed Bool Да ли је нит коментара затворена
hasBillingIssue Bool Да ли постоји проблем са плаћањем
isLoading Bool Да ли је мрежни захтев у току
hasMore Bool Да ли постоје додатне странице коментара
blockingErrorMessage String? Грешка која спречава рад корисничког интерфејса
warningMessage String? Упозоравајућа порука која не блокира рад
isDemo Bool Да ли се покреће у демо режиму
commentsVisible Bool Прекидач видљивости коментара
toolbarEnabled Bool Да ли је трака за форматирање приказана

FastCommentsFeedSDK Објављена својства

Својство Тип Опис
feedPosts [FeedPost] Тренутно учитани постови фида
hasMore Bool Да ли постоје додатне странице
currentUser UserSessionInfo? Тренутни аутентификовани корисник
blockingErrorMessage String? Блокирајућа порука о грешци
isLoading Bool Да ли је мрежни захтев у току
newPostsCount Int Број нових постова од последњег учитавања

Дрво коментара

Дрво коментара је доступно преко sdk.commentsTree:

// Плосна листа видљивих чворова за рендеровање
sdk.commentsTree.visibleNodes

// Претрага коментара по ID
sdk.commentsTree.commentsById["comment-id"]


ЕУ регион Internal Link


Да бисте користили ЕУ центар података, подесите поље region у вашем config:

let config = FastCommentsWidgetConfig(
    tenantId: "YOUR_TENANT_ID",
    urlId: "my-page",
    region: "eu"
)

Овим се усмјеравају сви API захтјеви и WebSocket везе на eu.fastcomments.com.



Чишћење Internal Link

Када завршите са инстанцом SDK-а (нпр. када се приказ затвара), позовите cleanup() да затворите WebSocket везу и откажете позадинске задатке:

sdk.cleanup()

За приказе којима управља SwiftUI-јев @StateObject, ово се обично позива у .onDisappear или када се приказ уништава.

Отпремање слика Internal Link

Коментари

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")
])


Помене корисника Internal Link


Pretražite korisnike radi automatskog dovršavanja @mention:

let results = try await sdk.searchUsers(query: "jan")
// Vraća [UserSearchResult] sa userId, username, avatar, itd.

Ugrađeni CommentInputBar automatski podržava dovršavanje @mention.



Уређивање и брисање коментара Internal Link


Измјени

try await sdk.editComment(commentId: commentId, newText: "Updated text")

Сервер поново рендерује HTML. Локални коментар се аутоматски ажурира.

Обриши

try await sdk.deleteComment(commentId: commentId)

Брисање коментара такође уклања његове потомке из локалног стабла.

Обе радње су доступне преко контекстног менија коментара у корисничком интерфејсу када је тренутни корисник аутор коментара (или администратор сајта).



Обрада грешака Internal Link

SDK metode bacaju FastCommentsError, koji implementira LocalizedError:

do {
    try await sdk.load()
} catch let error as FastCommentsError {
    print(error.translatedError ?? error.reason ?? "Unknown error")
} catch {
    print(error.localizedDescription)
}

FastCommentsError properties:

  • code -- kod greške iz API-ja
  • reason -- opis greške na engleskom jeziku
  • translatedError -- lokalizovana poruka o grešci koju pruža server

Blocking errors are also surfaced automatically via sdk.blockingErrorMessage, which the built-in views display to the user.



Локализација Internal Link


Proslijedite kod lokaliteta u konfiguraciju da biste lokalizovali stringove koje server pruža:

let config = FastCommentsWidgetConfig(
    tenantId: "YOUR_TENANT_ID",
    urlId: "my-page",
    locale: "fr_fr"
)

Stringovi korisničkog interfejsa na klijentskoj strani koriste lokalizaciju zasnovanu na iOS bundle-u.



Пример апликације Internal Link

Repozitorijum sadrži kompletan primjer aplikacije u ExampleApp/ sa demonstracijama:

  • Ugnježdeni komentari sa SSO i prilagođenim temama
  • Društveni feed sa kreiranjem objava i filtriranjem po oznakama
  • Razgovor uživo
  • Jednostavni i sigurni SSO tokovi
  • Prilagođena dugmad na alatnoj traci (komentari i feed)

Потребна помоћ?

Ако наиђете на било какве проблеме или имате питања у вези iOS библиотеке, молимо вас:

Доприноси

Доприноси су добродошли! Посетите GitHub репозиторијум за смернице о доприносу.