FastComments.com

Add Comments to Your iOS App

์ด๊ฒƒ์€ FastComments ๊ณต์‹ iOS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

iOS ์•ฑ์— ์‹ค์‹œ๊ฐ„ ๋Œ“๊ธ€, ์ฑ„ํŒ… ๋ฐ ๋ฆฌ๋ทฐ ์œ„์ ฏ์„ ์ž„๋ฒ ๋“œํ•˜์„ธ์š”.

๋ฆฌํฌ์ง€ํ† ๋ฆฌ

GitHub์—์„œ ๋ณด๊ธฐ


๊ธฐ๋Šฅ Internal Link

  • ์ค‘์ฒฉ๋œ ๋‹ต๊ธ€๊ณผ ํŽ˜์ด์ง•์„ ๊ฐ–๋Š” ์Šค๋ ˆ๋“œํ˜• ๋Œ“๊ธ€ ํŠธ๋ฆฌ
  • ๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ, ๋ฆฌ์•ก์…˜, ๋ฏธ๋””์–ด ์ฒจ๋ถ€๋ฅผ ์ง€์›ํ•˜๋Š” ์†Œ์…œ ํ”ผ๋“œ
  • ์ž๋™ ์Šคํฌ๋กค ๋ฐ ๋‚ ์งœ ๊ตฌ๋ถ„์ž๊ฐ€ ์žˆ๋Š” ๋ผ์ด๋ธŒ ์ฑ„ํŒ… ๋ชจ๋“œ
  • WebSocket์„ ํ†ตํ•œ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ(์ƒˆ ๋Œ“๊ธ€, ํˆฌํ‘œ, ์ ‘์† ์ƒํƒœ)
  • ์‹ฑ๊ธ€ ์‚ฌ์ธ์˜จ(Simple SSO๋Š” ํ…Œ์ŠคํŠธ์šฉ, Secure SSO๋Š” ์šด์˜์šฉ)
  • ๊ตต๊ฒŒ, ์ดํƒค๋ฆญ, ์ฝ”๋“œ, @๋ฉ˜์…˜์„ ์ง€์›ํ•˜๋Š” ๋ฆฌ์น˜ ํ…์ŠคํŠธ ํŽธ์ง‘
  • ์œ„/์•„๋ž˜ ํ™”์‚ดํ‘œ ๋˜๋Š” ํ•˜ํŠธ ๋“ฑ ์„ค์ • ๊ฐ€๋Šฅํ•œ ์Šคํƒ€์ผ์˜ ํˆฌํ‘œ
  • ๋ชจ๋”๋ ˆ์ด์…˜ ์ž‘์—…: ์‹ ๊ณ , ๊ณ ์ •, ์ž ๊ธˆ, ์ฐจ๋‹จ
  • ํ”„๋ฆฌ์…‹๊ณผ ์™„์ „ํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ์ œ๊ณตํ•˜๋Š” ์ข…ํ•ฉ์ ์ธ ํ…Œ๋งˆ
  • ๋Œ“๊ธ€ ๋ฐ ํ”ผ๋“œ ๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑ์„ ์œ„ํ•œ ๋งž์ถค ํˆด๋ฐ” ๋ฒ„ํŠผ
  • ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
  • EU ๋ฆฌ์ „ ์ง€์›
  • ์‚ฌ์šฉ์ž ์ ‘์† ์ƒํƒœ(์˜จ๋ผ์ธ/์˜คํ”„๋ผ์ธ ํ‘œ์‹œ)
  • ํƒœ๊ทธ ๊ธฐ๋ฐ˜ ํ”ผ๋“œ ํ•„ํ„ฐ๋ง
  • ํ˜„์ง€ํ™” ์ง€์›

์š”๊ตฌ์‚ฌํ•ญ Internal Link


  • iOS 16 ์ด์ƒ ๋˜๋Š” macOS 14 ์ด์ƒ
  • Swift 5.9 ์ด์ƒ
  • SwiftUI

์„ค์น˜ Internal Link

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(
    name: "YourApp",
    dependencies: [
        .product(name: "FastCommentsUI", package: "fastcomments-ios")
    ]
)

ํ•„์š”ํ•œ ๊ณณ์—์„œ ๋‘ ๋ชจ๋“ˆ์„ ์ž„ํฌํŠธํ•˜์„ธ์š”:

import FastCommentsUI
import FastCommentsSwift

๋น ๋ฅธ ์‹œ์ž‘ Internal Link

๋Œ“๊ธ€ ์œ„์ ฏ์„ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•œ ์ตœ์†Œ ์„ค์ •:

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

Replace "demo" with your FastComments tenant ID. The urlId identifies the page or thread where comments are stored.



์ธ์ฆ(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 (์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์ด๋ฉ”์ผ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ)
  • displayName -- ๋ณ„๋„์˜ ํ‘œ์‹œ ์ด๋ฆ„
  • displayLabel -- ์ด๋ฆ„ ์˜†์— ํ‘œ์‹œ๋˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ ˆ์ด๋ธ”(์˜ˆ: "VIP")
  • websiteUrl -- ์‚ฌ์šฉ์ž ์ด๋ฆ„์— ๋Œ€ํ•œ ๋งํฌ
  • locale -- ๋กœ์ผ€์ผ ์ฝ”๋“œ
  • isProfileActivityPrivate -- ํ”„๋กœํ•„ ํ™œ๋™ ์ˆจ๊ธฐ๊ธฐ(๊ธฐ๋ณธ๊ฐ’: true)

๋ณด์•ˆ SSO

ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๋ฐฑ์—”๋“œ๊ฐ€ API ์‹œํฌ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ช…๋œ SSO ํ† ํฐ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. iOS ์•ฑ์€ ์ด ํ† ํฐ์„ ์„œ๋ฒ„์—์„œ ๊ฐ€์ ธ์™€์„œ config์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ์—์„œ (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()
            // ํ† ํฐ์œผ๋กœ ์ƒˆ ๊ตฌ์„ฑ(config)์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋กœ๋“œ ์ „์— ์„ค์ •ํ•˜์„ธ์š”
            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 ์ƒ/ํ•˜ ํ™”์‚ดํ‘œ ๋ฒ„ํŠผ๊ณผ ํ•ฉ๊ณ„ ์นด์šดํŠธ
._1 ํ•˜๋‚˜์˜ ํ•˜ํŠธ ๋ฒ„ํŠผ๊ณผ ์นด์šดํŠธ

์ด๋ฒคํŠธ ์ฝœ๋ฐฑ

๋ชจ๋””ํŒŒ์ด์–ด ์Šคํƒ€์ผ์˜ ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์„ ์ฒ˜๋ฆฌํ•˜์„ธ์š”:

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  // ๊ด€๋ จ์„ฑ ๋†’์€ ์ˆœ


์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ… 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

ํ”ผ๋“œ ์‹œ์Šคํ…œ์€ ์ž์ฒด ๋ทฐ๋ฅผ ๊ฐ€์ง„ ๋ณ„๋„์˜ 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()
            }
    }
}

ํ”ผ๋“œ ๋ทฐ๋Š” ์ž๋™์œผ๋กœ ํ’€ ํˆฌ ๋ฆฌํ”„๋ ˆ์‹œ(pull-to-refresh)์™€ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ

๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑ ํผ์„ ํ‘œ์‹œํ•˜๋ ค๋ฉด 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๋Š” ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ(optimistic updates)๋กœ ๋ฐ˜์‘์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค:

try await sdk.reactPost(postId: post.id, reactionType: "l")

// ๋ฐ˜์‘ ์ƒํƒœ ํ™•์ธ
let hasLiked = sdk.hasUserReacted(postId: post.id, reactType: "l")
let likeCount = sdk.getLikeCount(postId: post.id)

๊ฒŒ์‹œ๋ฌผ์˜ ๋Œ“๊ธ€ ์—ด๊ธฐ

ํ”ผ๋“œ ๊ฒŒ์‹œ๋ฌผ์˜ ๋Œ“๊ธ€์„ ํ‘œ์‹œํ•˜๋ ค๋ฉด CommentsSheet๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ feed SDK์˜ ๊ตฌ์„ฑ(config)์„ ์‚ฌ์šฉํ•˜์—ฌ FastCommentsSDK ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

.sheet(item: $commentsPost) { post in
    CommentsSheet(post: post, feedSDK: sdk, onUserClick: { context, userInfo, source in
        // ์‚ฌ์šฉ์ž ํด๋ฆญ ์ฒ˜๋ฆฌ
    })
}

์ฐธ๊ณ : FeedPost๊ฐ€ .sheet(item:)์— ์‚ฌ์šฉ๋˜๋ ค๋ฉด Identifiable์„ ์ค€์ˆ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์ต์Šคํ…์…˜์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

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         // ํˆฌํ‘œ ๋ณ€๊ฒฝ ์‹œ ์Šคํ”„๋ง ์• ๋‹ˆ๋ฉ”์ด์…˜

ํ…Œ๋งˆ ์ ์šฉ

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•:

// Via SwiftUI environment (recommended for view hierarchy)
FastCommentsView(sdk: sdk)
    .fastCommentsTheme(theme)

// Directly on the SDK
sdk.theme = theme

์‚ฌ์šฉ์ž ์ •์˜ ํˆด๋ฐ” ๋ฒ„ํŠผ Internal Link

Comment Toolbar Buttons

๋Œ“๊ธ€ ์ž…๋ ฅ ํˆด๋ฐ”์— ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด 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}"
    }

    // Optional overrides (default to 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 Toolbar Buttons

๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ ํผ์— ๋Œ€ํ•ด 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)

๋ชจ๋“  ์ค‘์žฌ ์ž‘์—…์€ UI์˜ ๋Œ“๊ธ€ ์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž ์ž‘์—…์€ ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์ดํŠธ ๊ด€๋ฆฌ์ž์ผ ๋•Œ๋งŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค(SSO์˜ isAdmin ํ”Œ๋ž˜๊ทธ ๋˜๋Š” ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ์œผ๋กœ ์„ค์ •).



์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ Internal Link

sdk.load()๋ฅผ ํ˜ธ์ถœํ•œ ํ›„, SDK๋Š” ๊ตฌ์„ฑ๋œ urlId์— ๋Œ€ํ•œ WebSocket ์ด๋ฒคํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด๋ฒคํŠธ๋“ค์ด ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค:

  • ์ƒˆ๋กœ์šด ๋Œ“๊ธ€, ์ˆ˜์ • ๋ฐ ์‚ญ์ œ
  • ํˆฌํ‘œ (์ƒˆ๋กœ์šด ํˆฌํ‘œ ๋ฐ ์ทจ์†Œ)
  • ๊ณ ์ •, ์ž ๊ธˆ, ์‹ ๊ณ  ๋ฐ ์ฐจ๋‹จ ์ƒํƒœ ๋ณ€๊ฒฝ
  • ์‚ฌ์šฉ์ž ์ ‘์†(์ž…์žฅ/ํ‡ด์žฅ)
  • ์Šค๋ ˆ๋“œ ์—ด๋ฆผ/๋‹ซํž˜
  • ๋ฐฐ์ง€ ์ˆ˜์—ฌ
  • ์„œ๋ฒ„ ๊ตฌ์„ฑ ์—…๋ฐ์ดํŠธ

์‹ค์‹œ๊ฐ„ ํ‘œ์‹œ ์ œ์–ด

๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ์ƒˆ ๋Œ“๊ธ€์€ ์ฆ‰์‹œ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค:

sdk.showLiveRightAway = true   // ๊ธฐ๋ณธ๊ฐ’: ์ฆ‰์‹œ ํ‘œ์‹œ

์ด๋ฅผ false๋กœ ์„ค์ •ํ•˜๋ฉด ์ƒˆ ๋Œ“๊ธ€์ด "N๊ฐœ์˜ ์ƒˆ ๋Œ“๊ธ€" ๋ฒ„ํŠผ ๋’ค์— ๋ฒ„ํผ๋˜์–ด ์‚ฌ์šฉ์ž๊ฐ€ ์–ธ์ œ ํ‘œ์‹œํ• ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

sdk.showLiveRightAway = false

์‚ฌ์šฉ์ž ์ ‘์†

์„œ๋ฒ„๊ฐ€ ์ ‘์† ์ƒํƒœ ์ถ”์ ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์˜จ๋ผ์ธ/์˜คํ”„๋ผ์ธ ํ‘œ์‹œ๊ธฐ๊ฐ€ ์‚ฌ์šฉ์ž ์•„๋ฐ”ํƒ€์— ์ž๋™์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์—์„œ ์ถ”๊ฐ€ ๊ตฌ์„ฑ์€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.



ํŽ˜์ด์ง€๋„ค์ด์…˜ Internal Link

Page Size

// ๋Œ“๊ธ€: ๊ธฐ๋ณธ๊ฐ’ 30
sdk.pageSize = 50

// ํ”ผ๋“œ: ๊ธฐ๋ณธ๊ฐ’ 10
feedSDK.pageSize = 20

Loading More Comments

The UI shows pagination controls automatically. You can also trigger pagination programmatically:

// ๋‹ค์Œ ํŽ˜์ด์ง€ ๋กœ๋“œ
try await sdk.loadMore()

// ๋ชจ๋“  ๋‚จ์€ ํ•ญ๋ชฉ ๋กœ๋“œ (์„ฑ๋Šฅ ์ƒ ๋Œ“๊ธ€์ด 2000๊ฐœ ์ดˆ๊ณผ์ธ ๊ฒฝ์šฐ ๋น„ํ™œ์„ฑํ™”๋จ)
try await sdk.loadAll()

// ์ƒํƒœ ํ™•์ธ
sdk.hasMore            // ๋” ๋งŽ์€ ํŽ˜์ด์ง€๊ฐ€ ์žˆ๋Š”์ง€
sdk.shouldShowLoadAll()
sdk.getCountRemainingToShow()

Child Comment Pagination

์ค‘์ฒฉ๋œ ๋‹ต๊ธ€์€ ์ง€์—ฐ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์Šค๋ ˆ๋“œ๋ฅผ ํ™•์žฅํ•˜๋ฉด ์ฒ˜์Œ 5๊ฐœ์˜ ์ž์‹์ด ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ํ•ญ๋ชฉ์ด ์žˆ์œผ๋ฉด "๋” ๋งŽ์€ ๋‹ต๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ" ์ปจํŠธ๋กค์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ UI์—์„œ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.



์ƒํƒœ ๋ฐ ๊ด€์ฐฐ์„ฑ Internal Link

Both FastCommentsSDK์™€ FastCommentsFeedSDK๋Š” @Published ์†์„ฑ์„ ๊ฐ€์ง„ ObservableObject ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. SwiftUI ๋ทฐ์—์„œ ์ด๋ฅผ ๊ด€์ฐฐํ•˜์—ฌ ๋ฐ˜์‘ํ˜• UI ์—…๋ฐ์ดํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

FastCommentsSDK Published ์†์„ฑ

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 ์†์„ฑ

Property Type Description
feedPosts [FeedPost] ํ˜„์žฌ ๋กœ๋“œ๋œ ํ”ผ๋“œ ๊ฒŒ์‹œ๋ฌผ
hasMore Bool ์ถ”๊ฐ€ ํŽ˜์ด์ง€๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€
currentUser UserSessionInfo? ํ˜„์žฌ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž
blockingErrorMessage String? ์ฐจ๋‹จ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€
isLoading Bool ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์ง„ํ–‰ ์ค‘์ธ์ง€ ์—ฌ๋ถ€
newPostsCount Int ๋งˆ์ง€๋ง‰ ๋กœ๋“œ ์ดํ›„ ์ƒˆ ๊ฒŒ์‹œ๋ฌผ ์ˆ˜

๋Œ“๊ธ€ ํŠธ๋ฆฌ

๋Œ“๊ธ€ ํŠธ๋ฆฌ๋Š” sdk.commentsTree๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// ๋ Œ๋”๋ง์„ ์œ„ํ•œ ํ‘œ์‹œ๋œ ๋…ธ๋“œ์˜ ํ‰ํƒ„(Flat) ๋ชฉ๋ก
sdk.commentsTree.visibleNodes

// ID๋กœ ๋Œ“๊ธ€ ์กฐํšŒ
sdk.commentsTree.commentsById["comment-id"]


EU ๋ฆฌ์ „ Internal Link


EU ๋ฐ์ดํ„ฐ ์„ผํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ์˜ region ํ•„๋“œ๋ฅผ ์„ค์ •ํ•˜์„ธ์š”:

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

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  API ์š”์ฒญ๊ณผ WebSocket ์—ฐ๊ฒฐ์ด eu.fastcomments.com์œผ๋กœ ๋ผ์šฐํŒ…๋ฉ๋‹ˆ๋‹ค.



์ •๋ฆฌ Internal Link

SDK ์ธ์Šคํ„ด์Šค ์‚ฌ์šฉ์„ ๋งˆ์ณค์„ ๋•Œ(์˜ˆ: ๋ทฐ๊ฐ€ ๋‹ซํžˆ๋Š” ๊ฒฝ์šฐ), WebSocket ์—ฐ๊ฒฐ์„ ๋‹ซ๊ณ  ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ์ทจ์†Œํ•˜๋ ค๋ฉด cleanup()์„ ํ˜ธ์ถœํ•˜์„ธ์š”:

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

@mention ์ž๋™์™„์„ฑ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž๋ฅผ ๊ฒ€์ƒ‰:

let results = try await sdk.searchUsers(query: "jan")
// [UserSearchResult]๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. userId, username, avatar ๋“ฑ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋‚ด์žฅ๋œ CommentInputBar๊ฐ€ @mention ์ž๋™์™„์„ฑ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.



๋Œ“๊ธ€ ํŽธ์ง‘ ๋ฐ ์‚ญ์ œ Internal Link

ํŽธ์ง‘

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

์„œ๋ฒ„๊ฐ€ HTML์„ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ๋Œ“๊ธ€์ด ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

์‚ญ์ œ

try await sdk.deleteComment(commentId: commentId)

๋Œ“๊ธ€์„ ์‚ญ์ œํ•˜๋ฉด ๊ทธ ๋Œ“๊ธ€์˜ ํ•˜์œ„ ๋Œ“๊ธ€๋“ค๋„ ๋กœ์ปฌ ํŠธ๋ฆฌ์—์„œ ํ•จ๊ป˜ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋‘ ์ž‘์—… ๋ชจ๋‘ ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ“๊ธ€ ์ž‘์„ฑ์ž(๋˜๋Š” ์‚ฌ์ดํŠธ ๊ด€๋ฆฌ์ž)์ธ ๊ฒฝ์šฐ UI์˜ ๋Œ“๊ธ€ ์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด๋ฅผ ํ†ตํ•ด ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.



์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ Internal Link


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๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ ๋…ธ์ถœ๋˜๋ฉฐ, ๋‚ด์žฅ ๋ทฐ๊ฐ€ ์ด๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.


ํ˜„์ง€ํ™” Internal Link


์„œ๋ฒ„ ์ œ๊ณต ๋ฌธ์ž์—ด์„ ํ˜„์ง€ํ™”ํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ์— ๋กœ์ผ€์ผ ์ฝ”๋“œ๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”:

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

ํด๋ผ์ด์–ธํŠธ ์ธก UI ๋ฌธ์ž์—ด์€ iOS ๋ฒˆ๋“ค ๊ธฐ๋ฐ˜์˜ ํ˜„์ง€ํ™”๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.



์˜ˆ์ œ ์•ฑ Internal Link

๋ ˆํฌ์ง€ํ† ๋ฆฌ์—๋Š” ExampleApp/์— ์ „์ฒด ์˜ˆ์ œ ์•ฑ์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ ๋‹ค์Œ์„ ์‹œ์—ฐํ•ฉ๋‹ˆ๋‹ค:

  • SSO ๋ฐ ์ปค์Šคํ…€ ํ…Œ๋งˆ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์Šค๋ ˆ๋“œํ˜• ๋Œ“๊ธ€
  • ๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ ๋ฐ ํƒœ๊ทธ ํ•„ํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•œ ์†Œ์…œ ํ”ผ๋“œ
  • ๋ผ์ด๋ธŒ ์ฑ„ํŒ…
  • ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ „ํ•œ SSO ํ๋ฆ„
  • ๋งž์ถค ํˆด๋ฐ” ๋ฒ„ํŠผ(๋Œ“๊ธ€ ๋ฐ ํ”ผ๋“œ)

๋„์›€์„ ๋ฐ›์œผ์‹œ๊ฒ ์–ด์š”?

iOS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ์งˆ๋ฌธ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ์„ ์ด์šฉํ•ด ์ฃผ์„ธ์š”:

๊ธฐ์—ฌํ•˜๊ธฐ

๊ธฐ์—ฌ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ๊ธฐ์—ฌ ์ง€์นจ์€ GitHub ์ €์žฅ์†Œ๋ฅผ ๋ฐฉ๋ฌธํ•˜์„ธ์š”.