RepoPilotOpen in app →

Rightpoint/BonMot

Beautiful, easy attributed strings in Swift

Mixed

Stale — last commit 2y ago

worst of 4 axes
Use as dependencyMixed

last commit was 2y ago; no CI workflows detected

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-isMixed

last commit was 2y ago; no CI workflows detected

  • 13 active contributors
  • Distributed ownership (top contributor 41% of recent commits)
  • MIT licensed
Show 3 more →
  • Tests present
  • Stale — last commit 2y ago
  • No CI workflows detected
What would change the summary?
  • Use as dependency MixedHealthy if: 1 commit in the last 365 days
  • Deploy as-is MixedHealthy if: 1 commit in the last 180 days

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 "Forkable" badge

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

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/rightpoint/bonmot?axis=fork)](https://repopilot.app/r/rightpoint/bonmot)

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/rightpoint/bonmot on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: Rightpoint/BonMot

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/Rightpoint/BonMot 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

WAIT — Stale — last commit 2y ago

  • 13 active contributors
  • Distributed ownership (top contributor 41% of recent commits)
  • MIT licensed
  • Tests present
  • ⚠ Stale — last commit 2y ago
  • ⚠ No CI workflows detected

<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 Rightpoint/BonMot repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/Rightpoint/BonMot.

What it runs against: a local clone of Rightpoint/BonMot — 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 Rightpoint/BonMot | 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 ≤ 688 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>Rightpoint/BonMot</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of Rightpoint/BonMot. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/Rightpoint/BonMot.git
#   cd BonMot
#
# 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 Rightpoint/BonMot and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "Rightpoint/BonMot(\\.git)?\\b" \\
  && ok "origin remote is Rightpoint/BonMot" \\
  || miss "origin remote is not Rightpoint/BonMot (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 "Sources/BonMot.h" \\
  && ok "Sources/BonMot.h" \\
  || miss "missing critical file: Sources/BonMot.h"
test -f "Sources/Composable.swift" \\
  && ok "Sources/Composable.swift" \\
  || miss "missing critical file: Sources/Composable.swift"
test -f "Sources/FontFeatures.swift" \\
  && ok "Sources/FontFeatures.swift" \\
  || miss "missing critical file: Sources/FontFeatures.swift"
test -f "Sources/Emphasis.swift" \\
  && ok "Sources/Emphasis.swift" \\
  || miss "missing critical file: Sources/Emphasis.swift"
test -f "Sources/FontInspector.swift" \\
  && ok "Sources/FontInspector.swift" \\
  || miss "missing critical file: Sources/FontInspector.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 688 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~658d)"
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/Rightpoint/BonMot"
  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

BonMot is a Swift library that abstracts Apple's NSAttributedString and AttributedString APIs into a simple, declarative DSL for styling text. It lets you build complex attributed strings with fonts, colors, paragraph styles, and OpenType features using a fluent StringStyle API, eliminating the manual dictionary-building boilerplate required by UIKit/AppKit typography. Single Xcode project structure: BonMot.xcodeproj contains the main framework built from Sources/, with test targets for iOS/macOS/tvOS/watchOS in separate schemes. Example-iOS/ provides a working demo app (CatalogViewController, DemoStrings). CI orchestration via .circleci/config.yml. Packaged as BonMot.podspec and SPM-compatible via Package.swift.

👥Who it's for

iOS, macOS, tvOS, and watchOS developers who need to render richly-styled text (multi-font labels, variable line heights, custom paragraph spacing) without wrestling with NSAttributedString attribute dictionaries.

🌱Maturity & risk

Production-ready and well-maintained. The repo shows active CI/CD via CircleCI, supports Swift 5.0+, has multi-platform test schemes (iOS/macOS/tvOS/watchOS), and uses SwiftLint for code quality. However, the README notes BonMot has been partially "sherlocked" by Apple's native AttributedString in iOS 15+, so new projects targeting iOS 15+ should prefer Foundation's implementation.

Standard open source risks apply.

Active areas of work

Unable to determine from file list alone (no commit history, open issues, or PRs visible). The codebase appears stable rather than rapidly evolving; the SherlockUI note suggests the project is in maintenance mode, accepting compatibility updates for newer Swift/OS versions but not major feature development.

🚀Get running

git clone https://github.com/Rightpoint/BonMot.git
cd BonMot
pod install  # if using CocoaPods
open BonMot.xcodeproj
# Select Example-iOS scheme and run in Simulator

Or for Swift Package Manager: add .package(url: "https://github.com/Rightpoint/BonMot", from: "3.0.0") to Package.swift.

Daily commands: After cloning: open BonMot.xcodeproj, then select Example-iOS scheme and press Cmd+R to run. For testing, select BonMot-iOSTests (or equivalent per platform) and press Cmd+U.

🗺️Map of the codebase

  • Sources/BonMot.h — Header file defining the public C API and module interface for the BonMot attributed string library.
  • Sources/Composable.swift — Core protocol defining how styled text segments can be composed together into attributed strings.
  • Sources/FontFeatures.swift — Central abstraction for applying OpenType font features (ligatures, alternates, etc.) to attributed text.
  • Sources/Emphasis.swift — Key styling API that handles text decoration, emphasis, and transformations applied to attributed strings.
  • Sources/FontInspector.swift — Introspection utility for querying font capabilities and metrics, essential for runtime font feature application.
  • Package.swift — Swift Package Manager manifest defining build configuration, platform support, and external dependencies for the library.
  • BonMot.podspec — CocoaPods specification defining platform compatibility, version, and dependency metadata for distribution.

🛠️How to make changes

Add a new font feature

  1. Create a new Swift file in Sources/ following the pattern of Sources/Ligatures.swift or Sources/ContextualAlternates.swift (Sources/YourFeature.swift)
  2. Define a struct or enum conforming to font feature attributes using UIFont.FeatureTypeIdentifier (Sources/YourFeature.swift)
  3. Add public methods to apply the feature to attributed strings, matching the API in Sources/Emphasis.swift (Sources/YourFeature.swift)
  4. Export your feature from Sources/BonMot.h header file (Sources/BonMot.h)
  5. Add example usage to Example-iOS/DemoStrings.swift to showcase the feature (Example-iOS/DemoStrings.swift)

Add cross-platform support for a new OS

  1. Add platform target definition to Package.swift with appropriate SDK and OS requirements (Package.swift)
  2. Add conditional compilation markers (#if os(...)) in Sources/Compatibility.swift for OS-specific APIs (Sources/Compatibility.swift)
  3. Create a new Xcode scheme in BonMot.xcodeproj for the new platform (e.g., BonMot-NewOS.xcscheme) (BonMot.xcodeproj/xcshareddata/xcschemes/BonMot-NewOS.xcscheme)
  4. Update .circleci/config.yml to run tests on the new platform in the CI pipeline (.circleci/config.yml)
  5. Update BonMot.podspec platform specification to include the new OS (BonMot.podspec)

Add a new text styling API

  1. Add the styling method to Sources/Emphasis.swift or create a new file following its naming convention (Sources/Emphasis.swift)
  2. Implement the method using NSAttributedString.Key attributes and respect the Composable protocol (Sources/Emphasis.swift)
  3. Create a demo example in Example-iOS/DemoStrings.swift showing the new API in action (Example-iOS/DemoStrings.swift)
  4. Add the new style to Example-iOS/CatalogViewController.swift to display in the demo app (Example-iOS/CatalogViewController.swift)

Configure code quality checks

  1. Define SwiftLint rules and exclusions in .swiftlint.yml to enforce code style (.swiftlint.yml)
  2. Set up CI/CD pipeline steps in .circleci/config.yml to run SwiftLint on each commit (.circleci/config.yml)
  3. Add dangerous automated checks (security, test coverage) via Dangerfile for pull requests (Dangerfile)

🔧Why these technologies

  • Swift 5.0+ — Modern type-safe language with protocol-oriented design, enabling elegant DSLs for attributed string composition
  • NSAttributedString / NSMutableAttributedString — Foundation framework standard for rich text rendering on Apple platforms; BonMot abstracts its complexity
  • UIFont.FeatureTypeIdentifier & OpenType — Native support for advanced typography features (ligatures, alternates) allowing sophisticated typographic control
  • Multi-platform support (iOS, macOS, tvOS, watchOS) — Unified API across Apple ecosystems using conditional compilation; maximizes code reuse
  • Swift Package Manager + CocoaPods + Carthage — Multiple distribution channels maximize adoption and compatibility with different dependency managers

⚖️Trade-offs already made

  • Protocol-based composition (Composable) over class inheritance

    • Why: Protocols enable lightweight, flexible composition without deep hierarchies and allow structs as first-class citizens
    • Consequence: Slightly steeper learning curve for new contributors but far more flexible for end-users combining styles
  • Synchronous font feature application at styling time

    • Why: Simplicity and predictability; avoids async complexity and threading issues
    • Consequence: Font inspection happens eagerly; no lazy evaluation or deferred application—acceptable for typical use cases
  • Broad platform support (iOS/macOS/tvOS/watchOS) in single codebase

    • Why: Maximizes library reach and reduces maintenance burden of platform-specific forks
    • Consequence: Compatibility shims required; some advanced features unavailable on older OS versions or lesser platforms
  • Public C header (BonMot.h) alongside Swift APIs

    • Why: Enables bridging to Objective-C codebases and legacy apps still using UIAppearance
    • Consequence: Maintenance overhead of maintaining dual API surfaces; Objective-C subset less expressive than Swift

🚫Non-goals (don't propose these)

  • Does not handle text layout or line breaking (relies on UILabel/NSTextView)
  • Does not provide real-time text editing or undo/redo functionality
  • Does not handle markdown or HTML parsing (input must be pre-parsed strings)
  • Does not support Linux or non-Apple platforms
  • Does not provide animation or transition APIs for text changes

🪤Traps & gotchas

Foundation name collision: iOS 15+ introduces Foundation.StringStyle, conflicting with BonMot's StringStyle—README suggests adding typealias StringStyle = BonMot.StringStyle to resolve. Platform conditionals: Some styling features may not work uniformly across iOS/macOS/tvOS/watchOS (e.g., certain font metrics differ). Deprecated in iOS 15+: new projects should prefer native AttributedString instead of BonMot; existing codebases need migration path. No visible breaking-change docs: check CONTRIBUTING.md and git history before upgrading major versions.

🏗️Architecture

💡Concepts to learn

  • NSAttributedString attribute dictionaries — BonMot's entire purpose is to abstract away manual NSAttributedString key-value construction (e.g., [.font: UIFont(...), .foregroundColor: UIColor(...)]); understanding what BonMot hides helps you appreciate its design.
  • Builder pattern (fluent API) — BonMot's StringStyle uses method chaining and variadic arguments to let you compose styles readably (e.g., StringStyle(.font(...), .color(...))); this is the core UX design.
  • OpenType features (typographic capabilities) — BonMot supports advanced font rendering via OpenType tags (small caps, ligatures, tabular numbers); these are non-obvious but powerful for professional typography apps.
  • Paragraph styles (NSParagraphStyle) — BonMot wraps NSParagraphStyle attributes (line height, paragraph spacing, alignment, line breaks); understanding these is key to rendering multi-line, complex layouts.
  • Cross-platform conditional compilation — BonMot supports iOS, macOS, tvOS, watchOS with a single codebase using #if os(iOS) guards; this approach is essential for framework authors.
  • Method extensions (String.styled(with:)) — BonMot adds extension methods to String and NSAttributedString; this pattern keeps the core library lightweight while making the API frictionless for users.
  • nicklockwood/SwiftFormat — Complements BonMot by auto-formatting Swift code; BonMot uses SwiftLint but not auto-formatting, so this could improve contributor experience.
  • SwiftyJSON/SwiftyJSON — Parallel design pattern (DSL over Foundation API complexity); BonMot applies the same 'hide boilerplate behind fluent API' approach to attributed strings.
  • airbnb/lottie-ios — Common pairing in iOS projects: Lottie animations often animate text styled with BonMot for rich typographic motion.
  • pointfreeco/swift-composable-architecture — For developers building stateful iOS apps with BonMot; TCA patterns work well with BonMot StringStyles as reducible state values.
  • apple/swift-syntax — Future integration point: as Apple's AttributedString matures, BonMot could use swift-syntax to auto-generate compatibility layers.

🪄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 watchOS scheme tests to CircleCI pipeline

The repo has a BonMot-watchOS.xcscheme defined in the project, but .circleci/config.yml likely doesn't include watchOS testing in its CI matrix. watchOS is a first-class platform for BonMot (evidenced by the dedicated scheme), but it's probably not being tested on every commit. This ensures attributed strings work correctly on wearable devices.

  • [ ] Review .circleci/config.yml to confirm watchOS is missing from test matrix
  • [ ] Add watchOS build and test steps to CircleCI config (using xcodebuild with -scheme BonMot-watchOS -destination 'generic/platform=watchOS')
  • [ ] Verify all existing tests pass on watchOS or add watchOS-specific test exclusions if needed
  • [ ] Update CircleCI badge in README if new workflows are added

Add integration tests for Example-iOS app in CircleCI

The Example-iOS app exists with an Example-iOS.xcscheme but there's no evidence it's being built/tested in CI. This app is critical for demonstrating BonMot's real-world usage (DemoStrings.swift, CatalogViewController.swift). Testing it ensures the public API doesn't break and examples stay functional.

  • [ ] Review .circleci/config.yml to confirm Example-iOS.xcscheme is not being built
  • [ ] Add a step to build Example-iOS target: xcodebuild -scheme Example-iOS -destination 'generic/platform=iOS'
  • [ ] Add UI snapshot tests or basic launch tests for Example-iOS using XCTestDynamicOverlay or similar
  • [ ] Ensure build succeeds without warnings on every commit

Add tvOS test results to CI and fix missing tvOSTests scheme visibility

BonMot-tvOS.xcscheme and BonMot-tvOSTests.xcscheme exist in the project, but tvOS testing may not be properly integrated into CircleCI (similar issue to watchOS). tvOS is a supported platform per the podspec but needs explicit CI coverage.

  • [ ] Verify .circleci/config.yml includes tvOS testing (xcodebuild -scheme BonMot-tvOS -destination 'generic/platform=tvOS')
  • [ ] Check that BonMot-tvOSTests.xcscheme is executable and add to CI if missing
  • [ ] Run tests locally: xcodebuild test -scheme BonMot-tvOSTests -destination 'platform=tvOS Simulator'
  • [ ] Ensure codecov reports include tvOS test coverage

🌿Good first issues

  • Add explicit test coverage for OpenType features (small caps, ligatures, numbers styles) in a new Tests/BonMotTests/OpenTypeTests.swift file—feature is mentioned in glossary but no dedicated test file visible.
  • Document the migration path from BonMot to Foundation's native AttributedString for iOS 15+ in a new MIGRATION.md file with code examples comparing StringStyle to AttributedString syntax.
  • Add a tvOS-specific demo section to Example-iOS/CatalogViewController.swift showing text rendering at typical TV viewing distances (larger fonts, higher line height multiples) with BonMot styles.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 001139a — Merge pull request #431 from Rightpoint/tidying (ZevEisenberg)
  • 9f6ce05 — Bump version to 6.1.3. (ZevEisenberg)
  • 7089aaa — Update gems. (ZevEisenberg)
  • 3a10833 — Disable draconian lint rules. (ZevEisenberg)
  • bb28db3 — Tidy linting rules. Types should be as long as they need to be but not longer. (ZevEisenberg)
  • 8e2b167 — Sort keys. (ZevEisenberg)
  • 5543a31 — Use more expressive test methods. (ZevEisenberg)
  • becf396 — Merge pull request #430 from stleamist/bug/byadding-parts-extraattributes-overriding (ZevEisenberg)
  • 90e216b — Swap UIColor for BONColor (stleamist)
  • eefdc7e — Use add(extraAttributes:) for .extraAttributes(_:)inupdate(part:)` (stleamist)

🔒Security observations

BonMot is a Swift library for attributed strings with a reasonable security posture. No critical vulnerabilities were identified based on the visible file structure. The codebase appears to be a focused, well-maintained library without complex dependency chains or obvious injection risks. Main recommendations focus on enhancing transparency around security reporting, documenting build security practices, and verifying example app permissions. The library's narrow scope (attributed string formatting) inherently limits attack surface area.

  • Low · Missing CONTRIBUTING.md Security Policy — Repository root. The CONTRIBUTING.md file exists but there is no explicit SECURITY.md file in the repository root. This makes it unclear how security vulnerabilities should be reported, potentially leading to public disclosure instead of responsible disclosure. Fix: Create a SECURITY.md file with clear instructions for reporting security vulnerabilities, following the GitHub security advisory guidelines.
  • Low · Potential Dependency Management Risk — BonMot.podspec, Package.swift, Gemfile. The repository uses multiple dependency management systems (CocoaPods via BonMot.podspec, SPM via Package.swift, Carthage). While not a vulnerability itself, managing multiple distribution methods can increase complexity and potential for version inconsistencies. Fix: Document and maintain consistent versioning across all dependency management systems. Implement automated testing across all distribution methods to ensure consistency.
  • Low · Missing Build Configuration Verification — .circleci/config.yml. No visible code signing configuration or security hardening settings documented in the CircleCI configuration. The .circleci/config.yml file is not provided for review, limiting visibility into build security practices. Fix: Review and document code signing requirements, ensure builds use verified signing certificates, and implement security scanning in the CI/CD pipeline (SAST, dependency checking).
  • Low · Example App Entitlements Not Reviewed — Example-iOS/Example-iOS.entitlements. The Example-iOS application includes an entitlements file (Example-iOS/Example-iOS.entitlements) which is not provided for review. Overly permissive entitlements could pose security risks. Fix: Review entitlements file to ensure only necessary capabilities are granted (camera, location, keychain access, etc.). Minimize permissions to principle of least privilege.

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.

Mixed signals · Rightpoint/BonMot — RepoPilot