RepoPilotOpen in app →

badoo/Chatto

A lightweight framework to build chat applications, made in Swift

Healthy

Healthy across the board

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5w ago
  • 8 active contributors
  • Distributed ownership (top contributor 46% of recent commits)
Show 3 more →
  • MIT licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/badoo/chatto)](https://repopilot.app/r/badoo/chatto)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/badoo/chatto on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: badoo/Chatto

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/badoo/Chatto shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 5w ago
  • 8 active contributors
  • Distributed ownership (top contributor 46% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live badoo/Chatto repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/badoo/Chatto.

What it runs against: a local clone of badoo/Chatto — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in badoo/Chatto | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 67 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>badoo/Chatto</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of badoo/Chatto. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/badoo/Chatto.git
#   cd Chatto
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of badoo/Chatto and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "badoo/Chatto(\\.git)?\\b" \\
  && ok "origin remote is badoo/Chatto" \\
  || miss "origin remote is not badoo/Chatto (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "Chatto/sources/ChatController/BaseChatViewController.swift" \\
  && ok "Chatto/sources/ChatController/BaseChatViewController.swift" \\
  || miss "missing critical file: Chatto/sources/ChatController/BaseChatViewController.swift"
test -f "Chatto/sources/Chat Items/ChatItemProtocolDefinitions.swift" \\
  && ok "Chatto/sources/Chat Items/ChatItemProtocolDefinitions.swift" \\
  || miss "missing critical file: Chatto/sources/Chat Items/ChatItemProtocolDefinitions.swift"
test -f "Chatto/sources/ChatController/ChatMessages/ChatMessageCollectionAdapter.swift" \\
  && ok "Chatto/sources/ChatController/ChatMessages/ChatMessageCollectionAdapter.swift" \\
  || miss "missing critical file: Chatto/sources/ChatController/ChatMessages/ChatMessageCollectionAdapter.swift"
test -f "Chatto/sources/ChatController/Collaborators/ChatCollectionViewLayout.swift" \\
  && ok "Chatto/sources/ChatController/Collaborators/ChatCollectionViewLayout.swift" \\
  || miss "missing critical file: Chatto/sources/ChatController/Collaborators/ChatCollectionViewLayout.swift"
test -f "Chatto/sources/ChatController/ChatMessages/New/CollectionUpdateProvider.swift" \\
  && ok "Chatto/sources/ChatController/ChatMessages/New/CollectionUpdateProvider.swift" \\
  || miss "missing critical file: Chatto/sources/ChatController/ChatMessages/New/CollectionUpdateProvider.swift"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 67 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~37d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/badoo/Chatto"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Chatto is a lightweight Swift framework for building chat applications with efficient UICollectionView-based message rendering, background layout calculation, and bidirectional pagination support. It solves the core problem of rendering thousands of chat messages performantly by calculating collection view changes and layouts off the main thread, with companion framework ChattoAdditions providing text/photo bubbles and an extensible input bar. Two-framework monorepo: Chatto/sources contains core chat controller (BaseChatViewController → ChatMessagesViewController), chat items protocol system (ChatItemProtocolDefinitions), and collection view layout logic (ChatCollectionViewLayoutModelFactory, CollectionUpdateProvider). ChattoAdditions (mentioned but separate target) extends with concrete cell implementations. Tests mirror source structure under Chatto/Tests with unit tests for each major component.

👥Who it's for

iOS developers building chat applications in Swift who need a production-ready foundation with built-in pagination, keyboard handling, and custom message cell support without building UICollectionView management from scratch.

🌱Maturity & risk

Production-ready and actively maintained: CocoaPods version 4.1.0 released for Swift 5, Swift Package Manager support added, comprehensive test coverage (Tests/ directory with unit tests for ChatController, ChatItemPresenter, CollectionChanges), CI/CD via GitHub Actions (.github/workflows/swift.yml), and codecov integration. Last activity appears current based on Swift 5 support and modern build configuration.

Low-to-moderate risk for active projects: single organization (Badoo) maintains it, but no visible GitHub stars/watchers count in provided data. Monolithic Swift codebase (1M+ LOC) with tight coupling between BaseChatViewController and ChatMessagesViewController may create refactoring friction. No breaking changes documented in CHANGELOG.md snippet, but deprecation path for future Swift versions unknown.

Active areas of work

Active Swift 5 maintenance with CocoaPods/SPM/Carthage distribution. GitHub Actions CI enabled (.github/workflows/swift.yml). No specific open PRs visible in file list, but contribution guidelines exist (.github/CONTRIBUTING.md), indicating ongoing community engagement.

🚀Get running

git clone https://github.com/badoo/Chatto.git && cd Chatto && pod install && open Chatto.xcworkspace (for CocoaPods) OR use .swiftpm/xcode/package.xcworkspace for SPM. For bleeding-edge: include :git => 'https://github.com/badoo/Chatto', :branch => 'master' in Podfile.

Daily commands: Open Chatto.xcworkspace in Xcode and run scheme 'Chatto' (iOS target) or 'ChattoAdditions' for the companion framework. Unit tests: Xcode Test navigator or xcodebuild test -workspace Chatto.xcworkspace -scheme Chatto -configuration Debug.

🗺️Map of the codebase

  • Chatto/sources/ChatController/BaseChatViewController.swift — Core view controller orchestrating the entire chat UI, manages message display, input handling, and layout—every contributor must understand this entry point
  • Chatto/sources/Chat Items/ChatItemProtocolDefinitions.swift — Defines the protocol contract for chat items (models, presenters, cells)—foundational abstraction that all message types depend on
  • Chatto/sources/ChatController/ChatMessages/ChatMessageCollectionAdapter.swift — Bridges data model changes to UICollectionView updates; handles batch insertions/deletions that power the reactive message display
  • Chatto/sources/ChatController/Collaborators/ChatCollectionViewLayout.swift — Custom UICollectionViewLayout computing bubble sizes and positioning; critical for performance and correct message layout
  • Chatto/sources/ChatController/ChatMessages/New/CollectionUpdateProvider.swift — Calculates UICollectionView changes (insertions, deletions, moves) from diff data; key to efficient incremental updates
  • ChattoAdditions/sources/Chat Items/BaseMessage/BaseMessagePresenter.swift — Template presenter for common message patterns (text, photo); demonstrates the extension pattern for custom message types
  • Chatto/sources/ChatItemCompanionCollection.swift — Registry mapping chat item models to their presenter/cell implementations; enables pluggable message type support

🛠️How to make changes

Add a Custom Message Type

  1. Create a model conforming to ChatItem protocol (or extend BaseMessage if using ChattoAdditions) (Chatto/sources/Chat Items/ChatItemProtocolDefinitions.swift)
  2. Create a presenter subclassing BaseChatItemPresenter, implementing layout and cell binding (Chatto/sources/Chat Items/BaseChatItemPresenter.swift)
  3. Create a UICollectionViewCell subclass for rendering the message view (ChattoAdditions/sources/Chat Items/TextMessages/TextMessagePresenter.swift)
  4. Register the ChatItemCompanion in your ChatItemCompanionCollection instance (Chatto/sources/ChatItemCompanionCollection.swift)
  5. Pass companions to BaseChatViewController via its data source or configuration (Chatto/sources/ChatController/BaseChatViewController.swift)

Style Messages and Input Bar

  1. For message styling, create a style object conforming to the presenter's style protocol (e.g., MessageCollectionViewCellStyle) (ChattoAdditions/sources/Chat Items/BaseMessage/Styling)
  2. Pass the style to the presenter's initializer or configuration (ChattoAdditions/sources/Chat Items/BaseMessage/BaseMessagePresenter.swift)
  3. For input bar, implement ChatInputBarPresenterProtocol and configure animations via SpringChatInputBarAnimation (Chatto/sources/ChatController/InputBar/ChatInputBarPresentingController.swift)

Optimize Collection View Performance

  1. Ensure presenters implement the sizing protocol correctly and cache height calculations (Chatto/sources/Chat Items/BaseChatItemPresenter.swift)
  2. Use CollectionUpdateProvider to batch changes instead of reloading entire collection (Chatto/sources/ChatController/ChatMessages/New/CollectionUpdateProvider.swift)
  3. Configure ChatLayoutConfiguration with appropriate section insets and inter-item spacing (Chatto/sources/ChatController/ChatLayoutConfiguration.swift)
  4. Monitor and profile using the ChatCollectionViewLayout's metrics (Chatto/sources/ChatController/Collaborators/ChatCollectionViewLayout.swift)

🔧Why these technologies

  • UICollectionView with custom UICollectionViewLayout — Provides efficient cell recycling and custom layout control necessary for right-aligned/left-aligned message bubbles with variable heights
  • Protocol-based presenter pattern (ChatItemPresenter) — Decouples message models from their UI representation, enabling extensibility without modifying core framework code
  • Observable + SerialTaskQueue for reactive updates — Ensures data changes trigger UI updates serially, preventing race conditions and maintaining consistent state
  • KeyboardTracker for keyboard frame observation — Enables responsive input bar positioning and scroll adjustments without manual KVO or notifications boilerplate

⚖️Trade-offs already made

  • Custom UICollectionViewLayout vs. UITableView

    • Why: Messages require arbitrary horizontal positioning (bubbles aligned left/right) and complex sizing based on content and sender
    • Consequence: More complex layout code but greater flexibility; UITableView constraints would force linear vertical layout only
  • Synchronous batch collection updates vs. diffable data source (iOS 13+)

    • Why: Framework targets broader iOS versions; provides fine-grained control over insertion/deletion order
    • Consequence: Manual changeset computation is verbose but predictable; diffable data source unavailable for older iOS targets
  • Companion pattern (model + presenter + cell) vs. single cell class

    • Why: Separates concerns and allows multiple cell implementations per model type (e.g., compact vs. detailed)
    • Consequence: Three classes per message type but clear single responsibility; enables style variants without duplicating logic

🚫Non-goals (don't propose these)

  • Real-time synchronization or multiplayer conflict resolution
  • End-to-end encryption or security features
  • Push notifications or background message delivery
  • Media upload/download or file transfer—

🪤Traps & gotchas

No environment variables required, but UICollectionView performance depends on background thread layout calculation completing before scroll events—slow model binding can cause main thread stalls. Memory footprint grows with message count even with 'contention' optimization mentioned in README; pagination window size must be tuned per deployment target. SwiftLint config (.swiftlint.yml) enforces line length and complexity rules; PRs failing linting won't merge. SPM and CocoaPods versions may drift if not coordinated during releases.

🏗️Architecture

💡Concepts to learn

  • Off-main-thread layout calculation — Chatto's core performance optimization: CollectionUpdateProvider and ChatCollectionViewLayoutModelFactory pre-compute cell sizes and diffs on background threads to prevent UI jank when rendering thousands of messages
  • UICollectionView diffing & change calculation — CollectionChangesTests.swift validates Chatto's custom diffing algorithm for batch updates (inserts/deletes/reloads) rather than reloadData(), critical for smooth pagination animations
  • Presenter pattern (Model-View-Presenter) — BaseChatItemPresenter decouples message model logic from cell display, enabling extensibility: new message types require only a new presenter, not modifying core framework
  • Bidirectional pagination (top & bottom loading) — Chatto supports loading older messages upward and new messages downward in the same UICollectionView without full reload, using scroll position tracking and message count contention
  • Serial task queuing — SerialTaskQueue (tested in SerialTaskQueueTests.swift) ensures layout calculations and view updates happen in order despite async execution, preventing race conditions in fast-scrolling scenarios
  • Protocol-oriented collection view adapter — ChatMessagesCollectionProtocol abstracts UICollectionView details (data source, delegate) behind a protocol, allowing Chatto to swap implementations or inject test doubles
  • Message count contention — Chatto mentions this optimization for 'fast pagination and rotation with thousands of messages'—likely limits in-memory message count while maintaining scroll position, trading memory for performance
  • MessageKit/MessageKit — Direct competitor: modern Swift chat framework with similar UICollectionView foundation but includes UI components out-of-box vs. Chatto's minimalist approach
  • AsyncDisplayKit/Texture — Predecessor inspiration for off-main-thread layout calculation; Chatto applies this pattern specifically to chat UIs
  • badoo/SByteBuffer — Badoo-maintained sibling framework; may share architectural patterns or be used for message serialization in Chatto extensions
  • realm/realm-swift — Common companion for Chatto projects needing local message persistence and reactive updates to chat data models
  • RxSwift/RxSwift — Frequently paired with Chatto for reactive binding of messages and input events (SerialTaskQueue in Chatto suggests similar concurrency patterns)

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add unit tests for ChatCollectionViewLayout and layout configuration edge cases

ChatCollectionViewLayout.swift is a critical component for chat UI rendering, but there's no dedicated test file for it despite existing tests for ChatMessagesViewController. Edge cases like dynamic type changes, orientation changes, and extreme message sizes should be tested to prevent layout regressions.

  • [ ] Create Chatto/Tests/ChatController/ChatCollectionViewLayoutTests.swift (file exists but may be incomplete)
  • [ ] Add tests for layout invalidation on orientation changes
  • [ ] Add tests for ChatLayoutConfiguration edge cases (zero heights, extreme sizes)
  • [ ] Add tests for scroll position preservation during layout updates
  • [ ] Verify test coverage includes ChatCollectionViewLayoutModelFactory.swift

Add comprehensive integration tests for NewChatMessageCollectionAdapter with CollectionUpdateProvider

The new message collection adapter (Chatto/sources/ChatController/ChatMessages/New/) is a recent addition with multiple interdependent classes (NewChatMessageCollectionAdapter.swift, CollectionUpdateProvider.swift, NewChatMessageCollectionAdapterConfiguration.swift), but there are no visible integration tests validating their combined behavior, especially around collection update batching and animation.

  • [ ] Create Chatto/Tests/ChatController/NewChatMessageCollectionAdapterTests.swift
  • [ ] Test CollectionUpdateProvider correctly batches updates for insertions/deletions/moves
  • [ ] Test NewChatMessageCollectionAdapter configuration options affect update delivery
  • [ ] Add tests for edge cases: empty updates, duplicate UIDs, out-of-order operations
  • [ ] Verify interaction between NewChatMessageCollectionAdapter and ChatCollectionViewLayout

Add missing tests for CellPanGestureHandler and ChatPanGestureRecogniserHandler gesture recognition logic

Two gesture handler classes (CellPanGestureHandler.swift and ChatPanGestureRecogniserHandler.swift) in the Collaborators folder have no corresponding test files, yet they handle critical user interaction logic. This creates risk for gesture recognition bugs and swipe gesture regressions.

  • [ ] Create Chatto/Tests/ChatController/CellPanGestureHandlerTests.swift
  • [ ] Create Chatto/Tests/ChatController/ChatPanGestureRecogniserHandlerTests.swift
  • [ ] Test gesture state transitions (began → changed → ended/cancelled)
  • [ ] Test velocity thresholds and drag distance calculations
  • [ ] Test interaction with ReplyFeedbackGenerator.swift for haptic feedback
  • [ ] Add tests for concurrent gesture handling edge cases

🌿Good first issues

  • Add unit tests for ChatMessagesViewModel (referenced in file list but no dedicated test file found—create Chatto/Tests/ChatController/ChatMessagesViewModelTests.swift covering data source mutations and layout invalidation)
  • Document the ChatItemCompanion registration API with inline code examples in a new Chatto/sources/Chat Items/README.md, as the protocol-registration pattern is non-obvious for new presenters
  • Create an example SwiftUI wrapper around BaseChatViewController in Examples/ showing how to bridge UIKit Chatto into modern Swift concurrency (async/await support is missing)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 9235560 — Merge pull request #796 from badoo/bugfix/Remove-extra-space-between-scope-and-accessor (jrcmramos-bumble)
  • a2d4032 — Remove extra space between scope and accessor (jrcmramos-bumble)
  • fcbcbad — Merge pull request #794 from rmenezes/wrong_text_calculation_attributed_string (mike-bumble)
  • 6651359 — Bumped simulator to fix CI tests (rmenezes)
  • 941a6f2 — Merge branch 'badoo:master' into wrong_text_calculation_attributed_string (rmenezes)
  • 2858ca7 — Add option to pass original NSAttributedString instead of creating a internal one for text calculation (rmenezes)
  • 28e0eaa — Fix cancellation support (wiruzx)
  • e7b929a — Use NSEC_PER_SEC (wiruzx)
  • 61d7634 — Rename asyncStream() to values (wiruzx)
  • 3e9456d — Add explicit self (wiruzx)

🔒Security observations

The Chatto framework demonstrates reasonable security posture as an open-source Swift framework. No critical vulnerabilities were identified in the visible codebase structure. The main concerns are moderate: lack of visible dependency lock files which could introduce supply chain risks, and limited visibility into security automation in the CI/CD pipeline. The codebase follows standard iOS development practices with SwiftLint integration. As a UI framework handling chat data, it should be reviewed for proper input validation and secure data handling when used in production applications, though the framework itself is primarily a UI abstraction layer with lower inherent injection risks than data-handling code.

  • Medium · Missing Dependency Lock File or Version Pinning — Dependency management configuration (Chatto.podspec, ChattoAdditions.podspec). The repository uses CocoaPods and Carthage for dependency management, but no Podfile.lock or Cartfile.resolved is visible in the provided file structure. This could allow transitive dependency vulnerabilities to be introduced during installation if package versions are not strictly pinned. Fix: Ensure Podfile.lock and Cartfile.resolved are committed to version control. Use exact version pinning for all dependencies and regularly audit them with tools like pod outdated or carthage outdated.
  • Low · Potential Information Disclosure in Public Repository — .codecov.yml, .github/workflows/swift.yml. The repository is publicly available on GitHub (badoo/Chatto). While this is intentional for open-source projects, the presence of .codecov.yml and build configuration files could expose CI/CD pipeline details that might aid attackers in reconnaissance. Fix: Review CI/CD workflow files for exposed credentials or sensitive information. Use GitHub secrets for any sensitive values. Ensure build logs do not contain secrets.
  • Low · No Security Policy or SECURITY.md — Repository root. There is no visible SECURITY.md or security policy file in the provided file structure. This makes it unclear how security vulnerabilities should be reported. Fix: Create a SECURITY.md file following GitHub's security policy guidelines to guide researchers on responsible disclosure of vulnerabilities.
  • Low · Limited Visibility into SwiftLint Configuration — .swiftlint.yml, Chatto/.swiftlint.yml, ChattoAdditions/.swiftlint.yml. While .swiftlint.yml files are present, their content is not shown. SwiftLint can enforce security-related rules, but misconfiguration could allow unsafe patterns. Fix: Review SwiftLint configurations to ensure they enable security-focused rules such as force_unwrapping, force_cast, and implicitly_unwrapped_optional detection.
  • Low · No Apparent Static Analysis Integration — .github/workflows/swift.yml. No evidence of integrated static analysis tools (SAST) beyond SwiftLint in the CI/CD pipeline visible in the provided structure. Swift-specific security issues may not be caught automatically. Fix: Integrate additional static analysis tools such as Swift Package Manager security scanning, or commercial SAST tools designed for Swift code analysis into the CI/CD pipeline.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · badoo/Chatto — RepoPilot