FastComments.com

Add Comments to Your iOS App

Това е официалната iOS библиотека за FastComments.

Вградете живи коментари, чат и уиджети за ревюта в iOS приложението си.

Репозитория

Вижте в GitHub


Функции Internal Link

  • Разклонени дървета от коментари с вложени отговори и пагинация
  • Социална емисия със създаване на публикации, реакции и прикачени медии
  • Режим на жив чат с автоматично превъртане и разделители за дати
  • Актуализации в реално време чрез WebSocket (нови коментари, гласове, присъствие)
  • Еднократно влизане (Simple SSO за тестване, Secure SSO за продукция)
  • Редактиране на богато форматиран текст с удебелен, курсив, код и @споменавания
  • Гласуване с конфигурируеми стилове (стрелки нагоре/надолу или сърца)
  • Модераторски действия: маркиране, закрепване, заключване, блокиране
  • Разширено тематизиране с предварително зададени стилове и пълна персонализация
  • Персонализирани бутони в лентата с инструменти за коментари и създаване на публикации в емисията
  • Качване на изображения
  • Поддръжка за региона на ЕС
  • Присъствие на потребители (индикатори за онлайн/офлайн)
  • Филтриране на емисията по етикети
  • Поддръжка за локализация

Изисквания Internal Link


  • iOS 16+ или macOS 14+
  • Swift 5.9+
  • SwiftUI

Инсталиране Internal Link

Добавете FastCommentsUI към вашия проект, като използвате Swift Package Manager.

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

Заменете "demo" с вашия FastComments tenant ID. urlId идентифицира страницата или нишката, където се съхраняват коментарите.



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

FastComments поддържа три режима на удостоверяване:

  1. Анонимен -- няма SSO токен; потребителите получават идентичности, базирани на сесия
  2. Simple SSO -- клиентски токен за демота и тестове (не е сигурно)
  3. 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 -- потребителско ID (по подразбиране е email, ако не е зададено)
  • displayName -- отделно показвано име
  • displayLabel -- персонализиран етикет, показван до името (например "VIP")
  • websiteUrl -- връзка към името на потребителя
  • locale -- код на локала
  • isProfileActivityPrivate -- скриване на активността от профила (по подразбиране true)

Secure SSO

В продукция вашият бекенд генерира подписан SSO токен, използвайки вашия API секрет. iOS приложението извлича този токен от вашия сървър и го подава към конфигурацията.

On your backend (using the FastComments Swift SDK or any language):

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()
// Върнете този токен на вашето iOS приложение чрез вашето API

In your iOS app:

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)
Стил Външен вид
._0 Бутони със стрелки нагоре/надолу с нетен брой
._1 Един бутон със сърце и брой

Обратни повиквания за събития

Използвайте модификаторни callback-и за обработка на потребителските взаимодействия:

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 supports these callbacks:

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

Изгледът на емисията включва автоматично издърпване за опресняване и безкрайно превъртане.

Създаване на публикации

Използвайте 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")

// Проверете състоянието на реакцията
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 Symbol
    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()

Бутони в лентата с инструменти за емисията

Имплементирайте 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()]

Модерация 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. Следните събития се обработват:

  • Нови коментари, редакции и изтривания
  • Гласувания (нови и премахнати)
  • Промени в състоянието на закрепване, заключване, маркиране и блокиране
  • Присъствие на потребители (влизане/напускане)
  • Отваряне/затваряне на нишки
  • Присъждане на значки
  • Актуализации на конфигурацията на сървъра

Управление на живото показване

По подразбиране новите коментари от други потребители се появяват веднага:

sdk.showLiveRightAway = true   // По подразбиране: показва се веднага

Задайте това на false, за да буферирате новите коментари зад бутон "N нови коментара", позволявайки на потребителя да избере кога да ги разкрие:

sdk.showLiveRightAway = false

Присъствие на потребителите

Индикаторите за онлайн/офлайн се появяват автоматично върху аватарите на потребителите, когато сървърът активира проследяването на присъствията. Не е необходима допълнителна конфигурация на клиента.



Пагинация Internal Link


Размер на страницата

// Коментари: по подразбиране 30
sdk.pageSize = 50

// Фийд: по подразбиране 10
feedSDK.pageSize = 20

Зареждане на още коментари

Потребителският интерфейс автоматично показва контролите за странициране. Можете също да задействате странициране програмно:

// Зареди следващата страница
try await sdk.loadMore()

// Зареди всички останали (деактивирано ако има >2000 коментара поради производителност)
try await sdk.loadAll()

// Проверка на състоянието
sdk.hasMore            // Дали има още страници
sdk.shouldShowLoadAll()
sdk.getCountRemainingToShow()

Странициране на вложени коментари

Вложените отговори се зареждат лениво. Когато потребителят разшири нишка, първите 5 вложени отговора се зареждат. Ако има още, се появява контрол "зареди още отговори". Това се обработва автоматично от потребителския интерфейс.



Състояние и наблюдаемост Internal Link

И двете 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 във вашата конфигурация:

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


Търсене на потребители за автоматичното довършване при @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)

Изтриването на коментар също премахва неговите наследници от локалното дърво.

И двете действия са налични чрез контекстното меню на коментара в интерфейса, когато текущият потребител е авторът на коментара (или администратор на сайта).



Обработка на грешки 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 -- локализирано съобщение за грешка, предоставено от сървъра

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



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

Предайте код на локал в конфигурацията, за да локализирате низовете, предоставени от сървъра:

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

Низовете на потребителския интерфейс на клиентската страна използват локализация, базирана на iOS bundle.

Примерно приложение Internal Link

Хранилището съдържа пълно примерно приложение в ExampleApp/ с демонстрации на:

  • Вложени коментари с SSO и персонализирани теми
  • Социална емисия със създаване на публикации и филтриране по тагове
  • Чат на живо
  • Прости и сигурни SSO потоци
  • Персонализирани бутони на лентата с инструменти (коментари и емисия)

Нужда от помощ?

Ако срещнете проблеми или имате въпроси относно iOS библиотеката, моля:

Принос

Приноси са добре дошли! Моля, посетете GitHub хранилището за указания относно приноса.