Skip to content

synodic-studio/swift-quality-tools

Repository files navigation

Swift Quality Tools

Centralized Swift code quality tooling with smart configuration discovery. Provides unified interface for SwiftFormat, SwiftLint, and custom SwiftSyntax-based rules.

Overview

This package provides three command-line tools:

  • swiftformat-smart - SwiftFormat with intelligent config discovery
  • swiftlint-smart - SwiftLint with intelligent config discovery
  • swiftlintcustom-smart - Custom SwiftSyntax-based linting rules

All tools feature smart configuration discovery that walks up the directory tree to find project-specific configs, with fallback to shared configurations.

Installation

Build from Source

Use the automated build script to build all components:

cd ~/Developer/swift-quality-tools
./Scripts/build-all.sh

This builds:

  • Main package (swiftformat-smart, swiftlint-smart, swiftlintcustom-smart)
  • Custom rule engine

Manual build:

swift build -c release

Binaries will be available at:

~/Developer/swift-quality-tools/.build/release/
β”œβ”€β”€ swiftformat-smart
β”œβ”€β”€ swiftlint-smart
└── swiftlintcustom-smart

Prerequisites

  • SwiftFormat: brew install swiftformat
  • SwiftLint: brew install swiftlint
  • Swift 5.9+: For building the tools

Usage

swiftformat-smart

Format Swift code with automatic config discovery:

# Format current directory
swiftformat-smart

# Format specific file
swiftformat-smart Sources/MyFile.swift

# Format directory
swiftformat-smart Sources/

# Use explicit config
swiftformat-smart --config path/to/.swiftformat.yml

Config Discovery Order:

  1. --config parameter if provided
  2. .swiftformat.yml or .swiftformat in current directory (error if both)
  3. Walk up directories until config found
  4. Fall back to ~/Developer/swift-quality-tools/Configs/shared-swiftformat.yml

swiftlint-smart

Lint Swift code with automatic config discovery:

# Lint current directory
swiftlint-smart

# Lint specific file
swiftlint-smart Sources/MyFile.swift

# Lint directory
swiftlint-smart Sources/

# Use explicit config
swiftlint-smart --config path/to/.swiftlint.yml

Config Discovery Order:

  1. --config parameter if provided
  2. .swiftlint.yml or .swiftlint.yaml in current directory (error if both)
  3. Walk up directories until config found
  4. Fall back to ~/Developer/swift-quality-tools/Configs/shared-swiftlint.yml

swiftlintcustom-smart

Run custom SwiftSyntax-based rules with parallel processing:

# Check current directory (parallel - uses all CPU cores)
swiftlintcustom-smart

# Check specific file
swiftlintcustom-smart Sources/MyFile.swift

# Check directory
swiftlintcustom-smart Sources/

# Sequential mode (for debugging)
swiftlintcustom-smart --sequential Sources/

Performance:

  • ⚑ Parallel processing enabled by default (uses all available CPU cores)
  • πŸš€ 2-6x faster than sequential on multi-core systems
  • πŸ“ˆ Scales automatically with available hardware
  • πŸ”§ Use --sequential flag to disable for debugging

Custom Rules:

  • skimmable_body: SwiftUI View body properties limited to 15 lines maximum
  • no_group_body: SwiftUI View body properties must have exactly one top-level view (never Group)
  • one_top_level_view: SwiftUI View body must have exactly one top-level view
  • excessive_nesting: Indentation depth limited to 4 levels maximum for all functions, closures, and initializers

First Run: The custom rule engine auto-builds on first use if not already built.

Integration

Claude Code Hooks

The hook system at ~/.claude/hooks/ automatically uses these tools:

# ~/.claude/hooks/formatters/swift_formatter.py
SWIFT_TOOLS_PATH = Path.home() / "Developer" / "swift-quality-tools" / ".build" / "release"

When you edit Swift files in Claude Code, these tools run automatically.

Xcode Build Phases

Add to your Xcode project's Build Phases to show lint warnings in the IDE.

Note: SwiftFormat runs on edit via Claude Code hooks, not in build phases. Only linters (SwiftLint + custom rules) should be in build phases.

Recommended: Single Script Approach

Add a "Run Script" build phase with:

"${HOME}/Developer/swift-quality-tools/Scripts/xcode-lint.sh"

This script runs both linters and works around Xcode's subprocess output suppression by parsing and re-echoing warnings.

Known Limitation: Xcode's build phase sandbox suppresses subprocess output. The script works around this by running swiftlintcustom-smart in terminal mode, parsing the output, and re-echoing warnings directly.

Alternative: Direct Tool Invocation

If you prefer explicit control:

if [ -f "${HOME}/Developer/swift-quality-tools/.build/release/swiftlint-smart" ]; then
    "${HOME}/Developer/swift-quality-tools/.build/release/swiftlint-smart" "${SRCROOT}" || true
fi

if [ -f "${HOME}/Developer/swift-quality-tools/.build/release/swiftlintcustom-smart" ]; then
    "${HOME}/Developer/swift-quality-tools/.build/release/swiftlintcustom-smart" "${SRCROOT}"
fi

Auto-Detection: swiftlintcustom-smart automatically detects when running in Xcode (via XCODE_VERSION_ACTUAL environment variable) and formats violations as clickable warnings for the Issue Navigator.

This provides:

  • βœ… Clickable lint warnings in Xcode Issue Navigator
  • βœ… Consistent quality checks across all projects
  • βœ… Automatic config discovery per project
  • βœ… Auto-detection of Xcode environment (no flags needed)
  • βœ… Single script for easy maintenance

Manual Command Line

Add to your PATH for easy access:

# In ~/.zshrc
export PATH="$HOME/Developer/swift-quality-tools/.build/release:$PATH"

# Then use anywhere:
swiftformat-smart .
swiftlint-smart .
swiftlintcustom-smart .

Configuration Files

Shared Configs

Located in Configs/:

  • shared-swiftformat.yml - Global SwiftFormat rules (100+ rules, Swift 6.0 target)
  • shared-swiftlint.yml - Global SwiftLint rules (custom SwiftUI rules, performance optimizations)

Per-Project Configs

Place in project root to override shared configs:

  • .swiftformat.yml or .swiftformat
  • .swiftlint.yml or .swiftlint.yaml

The tools will automatically discover and use project-specific configs when present.

Custom Rules

SwiftSyntax-based rules in CustomRules/swiftlint-swiftsyntax-integration/:

Current Rules:

  1. skimmable_body - SwiftUI View body line count limit (15 lines max)
  2. no_group_body - Prohibit top-level Group in View bodies (unless it has modifiers)
  3. one_top_level_view - Enforce single top-level view in View bodies
  4. excessive_indentation - Physical indentation limit (16 spaces / 4 tabs max)
  5. stack_minimum_children - VStack/HStack/ZStack must have at least 2 children (ForEach allowed; if/else allowed if any branch has 2+ views)
  6. view_structure_order - Enforce View property ordering (DISABLED - has bugs)
  7. no_wrapper_body - Detect pointless wrapper body properties
  8. blank_line_import_separation - Enforce blank line between regular and @testable imports
  9. preview_required - Every file with View/ViewModifier/Shape must have at least one #Preview
  10. no_exported_import - Prohibit @_exported import (underscore prefix = internal Swift API, not stable)
  11. prefer_swift_testing - Prefer Swift Testing framework over XCTest (detects import XCTest and XCTAssert* calls)
  12. prefer_shorthand_optional_binding - Detect optional binding renames (if let foo = bar) - use shorthand with original name instead

To add new rules:

  1. Edit CustomRules/swiftlint-swiftsyntax-integration/rule-engine/test-custom-rule.swift
  2. Rebuild: ./Scripts/build-all.sh or manually:
    cd CustomRules/swiftlint-swiftsyntax-integration/rule-engine
    swift build

Suppressing Custom Rules

Custom rules support line-specific suppression using the swiftlintcustom: prefix:

// swiftlintcustom:disable:next excessive_nesting
var computed: String {
    // Deep nesting allowed here
}

// Or inline
func deep() { // swiftlintcustom:disable:this excessive_nesting
    // ...
}

Note: Use swiftlint: for standard rules and swiftlintcustom: for custom rules. This keeps SwiftLint's superfluous_disable_command safety check working for standard rules.

See docs/swiftlint-directive-support.md for full documentation.

Architecture

swift-quality-tools/
β”œβ”€β”€ Package.swift                    # Swift Package definition
β”œβ”€β”€ Scripts/                         # Build and test scripts
β”‚   β”œβ”€β”€ build-all.sh                 # Build all components
β”‚   └── test-tools.sh                # Validate installation
β”œβ”€β”€ Sources/
β”‚   β”œβ”€β”€ SwiftFormatSmart/            # swiftformat-smart executable
β”‚   β”œβ”€β”€ SwiftLintSmart/              # swiftlint-smart executable
β”‚   β”œβ”€β”€ SwiftLintCustomSmart/        # swiftlintcustom-smart executable
β”‚   └── SharedUtilities/             # Shared code
β”‚       β”œβ”€β”€ ConfigDiscovery.swift    # Smart config finding
β”‚       β”œβ”€β”€ ColoredOutput.swift      # Terminal colors
β”‚       β”œβ”€β”€ ProcessRunner.swift      # Process execution
β”‚       └── ErrorFormatter.swift     # Self-healing error messages
β”œβ”€β”€ Configs/                         # Shared configurations
β”‚   β”œβ”€β”€ shared-swiftformat.yml
β”‚   └── shared-swiftlint.yml
β”œβ”€β”€ CustomRules/                     # Custom SwiftSyntax rules
β”‚   └── swiftlint-swiftsyntax-integration/
β”‚       └── rule-engine/
β”‚           β”œβ”€β”€ Package.swift
β”‚           └── test-custom-rule.swift
└── .build/release/                  # Compiled binaries

Benefits

βœ… Smart Config Discovery - Automatically finds project configs, falls back to shared βœ… Centralized Tooling - Single source of truth for all Swift projects βœ… Xcode Integration - In-IDE warnings and errors βœ… Hook Integration - Auto-format on every edit in Claude Code βœ… Fast Native Binaries - No shell/Python overhead βœ… Type-Safe Swift - Robust error handling and CLI parsing βœ… Self-Healing Errors - Actionable error messages for LLM auto-repair βœ… Consistent Quality - Same rules across all projects

Development

Rebuilding After Changes

cd ~/Developer/swift-quality-tools
./Scripts/build-all.sh

Testing

Run the complete test suite (unit tests + integration tests):

./Scripts/run-tests.sh

Test individual components:

# Unit tests only (Swift Testing framework)
swift test

# Integration tests only (tool validation)
./Scripts/test-tools.sh

Manual testing:

# Test on this repo
./.build/release/swiftformat-smart Sources/
./.build/release/swiftlint-smart Sources/
./.build/release/swiftlintcustom-smart Sources/

# Test on other projects
cd ~/Developer/gravity-well
~/Developer/swift-quality-tools/.build/release/swiftformat-smart .

Test Coverage:

  • ErrorFormatter - Self-healing error message formatting
  • ConfigDiscovery - Smart config file discovery and validation
  • Integration - All three tools executable and working correctly

Error Messages

All tools provide self-healing error messages following the pattern:

🚨 [Tool] Error: [ErrorType]
Problem: [Clear description]
Context: [What was being attempted]
Fix: [Actionable suggestion]

This enables LLM auto-repair when errors occur in Claude Code hooks.

Migration from synodic-tools

This repo consolidates Swift quality tooling previously scattered in synodic-tools/:

  • βœ… Moved shared-swiftformat.yml β†’ Configs/
  • βœ… Moved shared-swiftlint.yml β†’ Configs/
  • βœ… Moved swiftlint-swiftsyntax-integration/ β†’ CustomRules/
  • βœ… Converted ZSH functions β†’ Swift binaries
  • βœ… Better separation of concerns (library vs tools)

License

Personal tooling for Bryan Costanza's Swift projects.

About

Custom SwiftSyntax linting rules for SwiftUI code quality

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors