FastComments.com

Добавление комментариев в ваше iOS-приложение

Это официальная библиотека iOS для FastComments.

Встраивайте виджеты живых комментариев, чата и отзывов в ваше iOS-приложение.

Репозиторий

Просмотреть на GitHub


Возможности Internal Link

  • Древовидные комментарии с вложенными ответами и пагинацией
  • Социальная лента с созданием постов, реакциями и медиа-вложениями
  • Режим живого чата с автопрокруткой и разделителями по датам
  • Обновления в реальном времени через WebSocket (новые комментарии, голоса, присутствие)
  • Единый вход (Simple SSO для тестирования, Secure SSO для продакшена)
  • Редактор с расширенным форматированием: жирный, курсив, код и @mentions
  • Голосование с настраиваемыми стилями (стрелки вверх/вниз или сердца)
  • Действия модерации: flag, pin, lock, block
  • Всеобъемлющее оформление с предустановками и полной кастомизацией
  • Пользовательские кнопки на панели инструментов для создания комментариев и постов в ленте
  • Загрузка изображений
  • Поддержка региона ЕС
  • Присутствие пользователей (индикаторы онлайн/оффлайн)
  • Фильтрация ленты по тегам
  • Поддержка локализации

Требования Internal Link

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

Установка Internal Link

Добавьте FastCommentsUI в ваш проект с помощью Swift Package Manager.

В Xcode: Файл > Добавить зависимости пакета, затем введите 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. Простой 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 -- идентификатор пользователя (по умолчанию 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()
// Верните этот токен в ваше iOS-приложение через ваш 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()
            // Создайте новый config с токеном или установите его до загрузки
            isLoadingToken = false
            try? await sdk.load()
        }
    }
}

SecureSSOUserData поддерживает дополнительные поля:

  • optedInNotifications -- подписка на уведомления по email
  • 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Одна кнопка в виде сердца с отображением количества

Обработчики событий

Используйте колбэки в стиле модификаторов для обработки взаимодействий пользователя:

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

Система фида — отдельный 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.loadIfNeeded()
            }
    }
}

Просмотр фида автоматически поддерживает pull-to-refresh и бесконечную прокрутку. Используйте loadIfNeeded() при повторном входе в жизненный цикл экрана, чтобы существующий или восстановленный фид не сбрасывался на страницу 1.

Создание постов

Используйте 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")

// Проверить состояние реакции
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)
try? await sdk.loadIfNeeded()

Если ваш экран временно исчезает, представление фида автоматически ставит на паузу live-обновления и возобновляет их при повторном появлении без очистки загруженных постов. Вызывайте sdk.cleanup() только когда вы действительно закончите с экземпляром SDK.

Удаление постов

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

Настройка темы Internal Link

Предустановки темы

Доступны четыре встроенные предустановки:

// System defaults
sdk.theme = FastCommentsTheme.default

// Cards with shadows and large rounded corners
sdk.theme = FastCommentsTheme.modern

// Flat, no shadows, small corner radius, no thread lines
sdk.theme = FastCommentsTheme.minimal

// Set all action colors to a single brand color
sdk.theme = FastCommentsTheme.allPrimary(.indigo)

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

var theme = FastCommentsTheme()
theme.commentStyle = .flat    // Flat list with dividers (default)
theme.commentStyle = .card    // Rounded cards with shadows
theme.commentStyle = .bubble  // Chat bubble style

Цвета

Все свойства цвета необязательны. Незаполненные значения возвращаются к разумным системным значениям по умолчанию.

var theme = FastCommentsTheme()

// Brand colors
theme.primaryColor = .indigo
theme.primaryLightColor = .indigo.opacity(0.6)
theme.primaryDarkColor = Color(red: 0.2, green: 0.1, blue: 0.5)

// Backgrounds
theme.commentBackgroundColor = Color(.secondarySystemGroupedBackground)
theme.containerBackgroundColor = Color(.systemGroupedBackground)

// Action buttons
theme.actionButtonColor = .indigo
theme.replyButtonColor = .indigo
theme.toggleRepliesButtonColor = .indigo.opacity(0.8)
theme.loadMoreButtonTextColor = .indigo

// Votes
theme.voteActiveColor = .red
theme.voteCountColor = .primary
theme.voteCountZeroColor = .secondary
theme.voteDividerColor = Color(.separator)

// Links
theme.linkColor = .indigo
theme.linkColorPressed = .indigo.opacity(0.5)

// Dialogs
theme.dialogHeaderBackgroundColor = .indigo
theme.dialogHeaderTextColor = .white

// Input bar
theme.inputBarBackgroundColor = Color(.systemBackground)
theme.inputBarBorderColor = Color(.separator)

// Other
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          // Subtle shadows on cards
theme.showThreadLine = true       // Vertical line connecting nested replies
theme.animateVotes = true         // Spring animation on vote changes

Применение тем

Два подхода:

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

// Directly on the 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}"
    }

    // Необязательные переопределения (по умолчанию 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


Действия, доступные всем пользователям

  • Flag/Unflag -- сообщить о комментарии для проверки
try await sdk.flagComment(commentId: commentId)
try await sdk.unflagComment(commentId: commentId)
  • Block/Unblock -- скрыть все комментарии пользователя (для конкретного просматривающего)
try await sdk.blockUser(commentId: commentId)
try await sdk.unblockUser(commentId: commentId)

Действия, доступные только администраторам

  • Pin/Unpin -- закрепить/открепить комментарий вверху ветки обсуждения
try await sdk.pinComment(commentId: commentId)
try await sdk.unpinComment(commentId: commentId)
  • Lock/Unlock -- запретить/разрешить новые ответы на комментарий
try await sdk.lockComment(commentId: commentId)
try await sdk.unlockComment(commentId: commentId)

Все действия модерации также доступны через контекстное меню комментария в интерфейсе. Действия администратора отображаются только когда текущий пользователь является администратором сайта (устанавливается через SSO isAdmin флаг или конфигурацию в панели управления).



Обновления в реальном времени Internal Link

После вызова sdk.load(), SDK автоматически подписывается на события WebSocket для настроенного urlId. Обрабатываются следующие события:

  • Новые комментарии, правки и удаления
  • Голоса (новые и удалённые)
  • Изменения состояний pin, lock, flag и block
  • Присутствие пользователей (вход/выход)
  • Открытие/закрытие треда
  • Вручение бейджей
  • Обновления конфигурации сервера

Управление отображением в реальном времени

По умолчанию новые комментарии от других пользователей появляются сразу:

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 представлениях для реактивных обновлений интерфейса.

Свойства @Published в FastCommentsSDK

СвойствоТипОписание
commentCountOnServerIntОбщее количество комментариев на сервере
newRootCommentCountIntБуферизованные новые комментарии (when showLiveRightAway is false)
currentUserUserSessionInfo?Текущий аутентифицированный пользователь
isSiteAdminBoolЯвляется ли текущий пользователь администратором сайта
isClosedBoolЗакрыта ли ветка комментариев
hasBillingIssueBoolНаличие проблемы с оплатой
isLoadingBoolИдёт ли сетевой запрос
hasMoreBoolСуществуют ли дополнительные страницы комментариев
blockingErrorMessageString?Ошибка, препятствующая функционированию интерфейса
warningMessageString?Неблокирующее предупреждение
isDemoBoolЗапущено ли в демонстрационном режиме
commentsVisibleBoolПереключатель видимости комментариев
toolbarEnabledBoolОтображается ли панель форматирования

Свойства @Published в FastCommentsFeedSDK

СвойствоТипОписание
feedPosts[FeedPost]В настоящее время загруженные записи ленты
hasMoreBoolСуществуют ли дополнительные страницы
currentUserUserSessionInfo?Текущий аутентифицированный пользователь
blockingErrorMessageString?Блокирующее сообщение об ошибке
isLoadingBoolИдёт ли сетевой запрос
newPostsCountIntКоличество новых записей с момента последней загрузки

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

Дерево комментариев доступно через 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()

Для представлений, управляемых @StateObject в SwiftUI, это обычно вызывают в .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 properties:

  • code -- код ошибки из API
  • reason -- описание ошибки на английском языке
  • translatedError -- локализованное сообщение об ошибке, предоставленное сервером

Ошибки блокировки также автоматически отображаются через sdk.blockingErrorMessage, которое встроенные представления показывают пользователю.

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


Укажите код локали в конфигурации, чтобы локализовать строки, предоставляемые сервером:

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

Строки пользовательского интерфейса на стороне клиента используют локализацию, основанную на iOS bundle.



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


Репозиторий включает полнофункциональное примерное приложение в ExampleApp/ с демонстрациями:

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

Нужна помощь?

Если вы столкнулись с какими-либо проблемами или у вас есть вопросы по iOS-библиотеке, пожалуйста:

Внесение вкладов

Вклады приветствуются! Пожалуйста, посетите репозиторий на GitHub для ознакомления с инструкциями по внесению вкладов.