A command-line tool for analyzing the health of Swift and iOS projects. SwiftHealth produces a comprehensive 0-100 health score based on code metrics, Git practices, and project structure.
The Problem: Teams need a quick, objective way to assess technical debt and code quality in Swift projects. Existing solutions are either too heavyweight (SonarQube requires infrastructure), too narrow (only analyze code, not Git practices), or too opaque (you can't explain how the score is calculated).
The Solution: SwiftHealth is a single binary that analyzes your project in seconds and produces:
- A 0-100 health score with clear scoring bands (π’ Excellent, π‘ Good, π Fair, π΄ Poor)
- Actionable diagnostics with improvement hints
- JSON output for CI/CD integration
- Full transparency into how the score is calculated
Key Differentiators:
- Git-aware: Analyzes commit quality, branch strategy, merge patterns - not just code
- Fast: Pure Swift implementation, no external dependencies beyond Git
- Transparent: Every metric has a clear normalization algorithm you can inspect
- CI-friendly: JSON output, exit codes, configurable thresholds
- Zero setup: No servers, no databases, just
swifthealth analyze
brew install nemanjavlahovic/tap/swifthealthOr tap first, then install:
brew tap nemanjavlahovic/tap
brew install swifthealthTo install the latest development version from main:
brew install --HEAD nemanjavlahovic/tap/swifthealth# Download the latest release (universal binary for Intel + Apple Silicon)
curl -L -o swifthealth https://github.com/nemanjavlahovic/swifthealth/releases/latest/download/swifthealth-universal
# Verify checksum (recommended)
curl -L -o checksums.txt https://github.com/nemanjavlahovic/swifthealth/releases/latest/download/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
# Make executable and move to PATH
chmod +x swifthealth
sudo mv swifthealth /usr/local/bin/
# Verify installation
swifthealth --versionApple Silicon only:
curl -L -o swifthealth https://github.com/nemanjavlahovic/swifthealth/releases/latest/download/swifthealth-arm64
chmod +x swifthealth
sudo mv swifthealth /usr/local/bin/# Clone and build
git clone https://github.com/nemanjavlahovic/swifthealth.git
cd swifthealth
swift build -c release
# Copy binary to PATH
cp .build/release/swifthealth /usr/local/bin/# Analyze current directory
swifthealth analyze
# Analyze specific project
swifthealth analyze --path ~/Projects/MyApp
# JSON output for CI
swifthealth analyze --format json --json-out report.json
# HTML report with embedded charts
swifthealth analyze --html-out report.html
# Fail if score below threshold
swifthealth analyze --fail-under 80
# Include build analysis (requires Xcode build log)
swifthealth analyze --build-log ~/Library/Developer/Xcode/DerivedData/MyApp-xxx/Logs/Build/*.xcactivitylog
# Include Xcode warnings from xcresult bundle
swifthealth analyze --xcresult ~/path/to/Test.xcresult
# Analyze binary size
swifthealth analyze --app-path ~/path/to/MyApp.app
# Offline mode (skip network calls for dependency checking)
swifthealth analyze --offline
# View health score history and trends
swifthealth history --count 20 --chart βββββββββββ ββββββββββββββββββββββββββ βββββββββββ ββββββ βββ ββββββββββββ βββ
βββββββββββ ββββββββββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββ βββ
βββββββββββ ββ ββββββββββββ βββ ββββββββββββββ βββββββββββ βββ ββββββββ
βββββββββββββββββββββββββββ βββ ββββββββββββββ βββββββββββ βββ ββββββββ
ββββββββββββββββββββββββ βββ βββ ββββββββββββββ ββββββββββββββ βββ βββ
ββββββββ ββββββββ ββββββ βββ βββ ββββββββββββββ ββββββββββββββ βββ βββ
π₯ v0.2.0 β’ Project Health Analyzer
π /Users/dev/MyProject
π git, spm
π Running analyzers...
π Git Analysis
ββββββββββββββββββββββββββββββββββββββββ
Last Commit Recency: 2.50 days
Active Contributors (30 days): 3 count
Commit Message Quality: 78.5%
Conventional Commits: 85.0% percent
Branch Strategy: trunk-based
Total Branches: 4 count
Merge Strategy: rebase-heavy
π Code Analysis
ββββββββββββββββββββββββββββββββββββββββ
Total Code Files: 43 files
Total Lines of Code: 6857 lines
Comment Density: 14.1%
Average File Size: 159 lines/file
Swift Percentage: 100.0%
π¦ Dependency Analysis
ββββββββββββββββββββββββββββββββββββββββ
SPM Dependencies: 6 count
SPM Lockfile Age: 5.05 days
Potentially Outdated Dependencies: 0 count
β±οΈ Build Time Analysis
ββββββββββββββββββββββββββββββββββββββββ
DerivedData Size: 1250.00 MB
π¦ Size Analysis
ββββββββββββββββββββββββββββββββββββββββ
SPM Release Build Size: 12.50 MB
SPM Debug Build Size: 45.20 MB
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π₯ HEALTH SCORE β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β 0 10 20 30 40 50 60 70 80 90 100 β
β ββββββΌβββββΌβββββΌβββββΌβββββΌβββββΌβββββΌβββββΌβββββΌβββββ€ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β π΄ Poor π Fair π‘ Good π’ Excellent β
β β² β
β 85/100 β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
~ +3.2% from last run
{
"tool": {
"name": "swifthealth",
"version": "0.2.0"
},
"project": {
"root": "/Users/dev/MyProject",
"detected": ["git", "spm"]
},
"score": 85,
"scoreNormalized": 0.85,
"band": "green",
"metrics": [
{
"id": "git.recency",
"title": "Last Commit Recency",
"category": "git",
"value": {"type": "double", "value": 2.50},
"unit": "days"
},
{
"id": "build.derivedDataSize",
"title": "DerivedData Size",
"category": "build",
"value": {"type": "double", "value": 1250.00},
"unit": "MB"
}
// ... more metrics
],
"diagnostics": [...],
"timestamp": "2025-12-23T10:30:00Z"
}- Commit Recency: Days since last commit (fresher = healthier)
- Active Contributors: Unique contributors in last 30 days
- Commit Message Quality: Analyzed with 5 quality signals
- Conventional Commits: Percentage following conventional format
- Branch Strategy: Detects git-flow, trunk-based, feature-branch patterns
- Merge Strategy: Analyzes merge vs rebase patterns for clean history
- Commit Frequency: Tracks velocity and trends
- Lines of Code: Total LOC excluding comments and blanks
- Comment Density: Sweet spot is 10-20% (too few or too many both penalized)
- File Size: Ideal 50-200 lines per file (encourages modularity)
- Language Breakdown: Swift vs Objective-C percentage
- File Count: Total source files across project
- Warnings: Count of SwiftLint warnings with top offender rules
- Errors: Count of SwiftLint errors (heavily penalized)
- Graceful Degradation: Works even if SwiftLint not installed
- Configuration Detection: Finds .swiftlint.yml automatically
- SPM: Parses Package.resolved for Swift Package Manager deps
- CocoaPods: Parses Podfile.lock for CocoaPods deps
- Carthage: Parses Cartfile.resolved for Carthage deps
- Online Outdated Detection: Fetches latest versions from GitHub with rate limit handling
- Offline Mode: Use
--offlineto skip network calls - Multi-Manager: Handles projects using multiple dependency managers
- Build Log Parsing: Analyzes Xcode
.xcactivitylogfiles from DerivedData - Slow File Detection: Identifies files that take longest to compile
- Clean vs Incremental: Tracks clean and incremental build times
- Usage:
swifthealth analyze --build-log /path/to/build.xcactivitylog
- xcresult Parsing: Extracts warnings from
.xcresultbundles - Warning Categories: Groups warnings by type (deprecation, unused, etc.)
- Actionable Items: Links warnings to specific files and lines
- Usage:
swifthealth analyze --xcresult /path/to/Test.xcresult
- App Bundle Analysis: Analyzes
.appbundles for size breakdown - Framework Detection: Identifies embedded frameworks and their sizes
- Asset Catalogs: Reports asset catalog sizes
- Usage:
swifthealth analyze --app-path /path/to/MyApp.app
- Automatic Tracking: Scores are saved to
~/.swifthealth/history/after each run - Trend Visualization: ASCII charts show score changes over time
- JSON Export: Export history for external analysis
- Usage:
swifthealth history --count 20 --chart
- Beautiful Reports: Generate self-contained HTML reports with embedded CSS
- SVG Charts: Interactive score history visualization
- Shareable: Single file, no external dependencies
- Usage:
swifthealth analyze --html-out report.html
SwiftHealth uses weighted normalization to calculate the health score:
-
Each metric is normalized to [0.0, 1.0] using domain-specific algorithms:
- Git Recency: Exponential decay after threshold (7 days perfect, 30 days degraded)
- Contributors: Discrete scoring (1 = 0.5, 5+ = 1.0)
- Branch Count: 2-10 ideal, penalizes extremes
- Comment Density: 10-20% sweet spot, penalizes under/over-commenting
- File Size: 50-200 lines optimal, penalizes very large files
- Lint Warnings: Linear decay 0-50 warnings, exponential beyond 200
- Lint Errors: Steep penalty (even 1 error = 0.7 score)
- Outdated Deps: Percentage-based with 10% warn, 30% fail thresholds
- Lockfile Age: Fresh (<30 days) = 1.0, stale (>90 days) exponential decay
-
Each metric has a configurable weight (default weights sum to 1.0):
- Git Recency: 15%
- Git Contributors: 10%
- Dependency Outdated: 35%
- Lint Warnings: 15%
- Lint Errors: 15%
- Code LOC: 10%
-
Final score = weighted average Γ 100
Score Bands:
- π’ Excellent (80-100): Production-ready, well-maintained
- π‘ Good (60-79): Solid foundation, minor improvements needed
- π Fair (40-59): Technical debt present, needs attention
- π΄ Poor (0-39): Significant issues, immediate action required
Create .swifthealthrc.json in your project root:
{
"weights": {
"git.recency": 0.15,
"git.contributors30d": 0.10,
"deps.outdated": 0.35,
"lint.warnings": 0.15,
"lint.errors": 0.15,
"code.loc": 0.10
},
"thresholds": {
"git.recency.days.warn": 7,
"git.recency.days.fail": 30,
"deps.outdated.warnPct": 0.10,
"deps.outdated.failPct": 0.30,
"lint.warnings.warn": 50,
"lint.warnings.fail": 200,
"lint.errors.warn": 1,
"lint.errors.fail": 10
},
"ci": {
"failUnder": 80
}
}Generate default config:
swifthealth initSwiftHealth includes a comprehensive GitHub Actions workflow example with:
- Automatic health checks on PRs and pushes
- PR comments showing score with emoji indicators
- Artifact storage for historical tracking
- Optional health gate to block PRs that degrade score
Quick Setup:
# Copy the example workflow to your project
cp .github/workflows/health-check.yml.example .github/workflows/health-check.ymlOr create your own minimal workflow:
name: Health Check
on: [push, pull_request]
jobs:
health:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for git metrics
- name: Install SwiftHealth
run: brew install nemanjavlahovic/tap/swifthealth
- name: Run Health Check
run: swifthealth analyze --format json --json-out health.json --fail-under 70
- name: Upload Report
uses: actions/upload-artifact@v4
with:
name: health-report
path: health.jsonConfiguration:
- Set
HEALTH_THRESHOLDvariable in repository settings (default: 70) - Uncomment the
health-gatejob in the example to block PRs that drop score by >5 points
#!/bin/bash
# .git/hooks/pre-commit
score=$(swifthealth analyze --format json | jq -r '.score')
if [ "$score" -lt 60 ]; then
echo "β Health score ($score) is below minimum (60)"
echo "Run 'swifthealth analyze --verbose' for details"
exit 1
fi
echo "β
Health score: $score"SwiftHealth is built with a modular, protocol-oriented architecture using Swift Package Manager:
SwiftHealth/
βββ Sources/
β βββ Core/ # Shared models and utilities
β β βββ Models/ # Metric, Config, AnalyzerResult
β β βββ Config/ # ConfigLoader with validation
β β βββ History/ # HistoryManager, HistoryEntry
β β βββ Scoring/ # ScoreEngine with normalization
β βββ Analyzers/ # Analysis implementations
β β βββ GitAnalyzer/ # Git metrics via Process API
β β βββ CodeAnalyzer/ # LOC counting, file scanning
β β βββ BuildAnalyzer/ # Xcode build time analysis
β β βββ SizeAnalyzer/ # Binary size analysis
β β βββ XcodeAnalyzer/ # Xcode warnings from xcresult
β β βββ DependencyAnalyzer/ # SPM, CocoaPods, Carthage + online checking
β βββ SwiftHealthCLI/ # CLI entry point
β βββ Commands/ # analyze, history, init commands
β βββ Progress/ # Animated progress indicators
β βββ Renderers/ # TTY, JSON, and HTML output
βββ Package.swift
- Protocol-Oriented Design:
Analyzerprotocol enables easy extension - Value Types: Structs for all data models (immutability, thread-safety)
- Async/await: Modern concurrency for running multiple analyzers
- Process API: Direct Git command execution (no libgit2 dependency)
- Codable Everywhere: Automatic JSON serialization with custom CodingKeys
- Weighted Scoring: Pluggable normalization algorithms per metric type
import Core
public struct TestCoverageAnalyzer: Analyzer {
public let id = "test-coverage"
public init() {}
public func analyze(_ context: ProjectContext, _ config: Config) async -> AnalyzerResult {
// 1. Detect test artifacts
// 2. Parse coverage reports
// 3. Return metrics
let metrics = [
Metric(
id: "tests.coverage",
title: "Test Coverage",
category: .testing,
value: .percent(0.85)
)
]
return AnalyzerResult(metrics: metrics, diagnostics: [])
}
}Then add normalization in ScoreEngine.swift:
case "tests.coverage":
return normalizeTestCoverage(metric)- Git Analysis: Comprehensive commit quality, branch strategy, frequency tracking
- Code Analysis: LOC counting, comment density, file size metrics
- SwiftLint Integration: Warnings/errors with graceful degradation
- Dependency Analysis: SPM, CocoaPods, Carthage with outdated detection
- JSON Output: CI/CD ready with per-metric scores
- Weighted Scoring: Configurable weights and thresholds
- Build Performance: Analyze build times from Xcode
.xcactivitylogfiles (v0.2.0) - Online Outdated Detection: GitHub API integration for real-time version checking (v0.2.0)
- Historical Trends: Track score over time with ASCII charts (v0.2.0)
- HTML Reports: Beautiful self-contained reports with SVG charts (v0.2.0)
- Xcode Warnings: Parse
.xcresultbundles for warnings analysis (v0.2.0) - Binary Size Analysis: Analyze
.appbundles for size breakdown (v0.2.0)
- Test Coverage Analysis: Parse .xcresult bundles for coverage data
- Xcode Extension: Run SwiftHealth directly in Xcode
- GitHub Action: Official action for easy CI integration
- Baseline Support: Compare against a baseline score for regressions
Contributions welcome! Areas of interest:
- New analyzers (test coverage, test analysis, build metrics)
- Enhanced scoring algorithms
- Platform support (Linux, Windows)
- Performance optimizations
MIT License - see LICENSE file for details