SwiftLint Guidelines - beforetheshoes/Traveling-Snails GitHub Wiki
This document describes the SwiftLint configuration, security rules, and best practices for the Traveling Snails project.
SwiftLint is integrated into our project with a focus on security and modern Swift/SwiftUI patterns. Our configuration enforces:
- Security-first coding practices
- Modern iOS 18+ patterns
- Consistent code style
- Prevention of common Swift pitfalls
-
Rule: Prohibits all
print()
statements - Severity: Error (builds will fail)
- Reason: Prevents accidental logging of sensitive data
-
Solution: Use
Logger.shared.info()
,.debug()
,.warning()
, or.error()
instead
// ❌ WRONG - Will cause build failure
print("User data: \(userData)")
// ✅ CORRECT - Secure logging
Logger.shared.debug("Processing user request")
-
Rule: Prohibits deprecated
NavigationView
- Severity: Error
- Reason: Enforces modern iOS 16+ navigation patterns
-
Solution: Use
NavigationStack
instead
// ❌ WRONG - Will cause build failure
NavigationView {
ContentView()
}
// ✅ CORRECT - Modern pattern
NavigationStack {
ContentView()
}
-
Rule: Prohibits
@StateObject
and@ObservableObject
- Severity: Error
- Reason: Enforces modern iOS 17+ observation patterns
-
Solution: Use
@Observable
classes with@State
instead
// ❌ WRONG - Will cause build failure
@StateObject private var viewModel = ViewModel()
// ✅ CORRECT - Modern pattern
@State private var viewModel = ViewModel()
- Rule: Detects potential sensitive data in logging
- Severity: Warning
-
Triggers: Patterns like
trip.name
,user.
,password
,token
,secret
- Action: Manual review required
- Rule: Ensures error messages don't expose internal details
- Severity: Warning
-
Triggers: Keywords like
internal
,private
,debug
,stack
in error messages
- Rule: Detects SwiftData model arrays passed as parameters
- Severity: Warning
- Reason: Prevents infinite view recreation bugs
-
Solution: Use
@Query
directly in consuming views
// ❌ WRONG - Can cause infinite recreation
struct ActivityListView: View {
let activities: [Activity] // Parameter passing
}
// ✅ CORRECT - Direct querying
struct ActivityListView: View {
@Query private var activities: [Activity]
}
-
NavigationStack
overNavigationView
-
@Observable
over@StateObject
/@ObservableObject` -
Proper SwiftData usage with
@Query
and@Environment(\.modelContext)
-
L10n enum system over
NSLocalizedString
- Hardcoded string detection for UI elements
- Sorted imports
- Implicit returns in closures and computed properties
- Trailing commas in multi-line collections
- Proper file headers
- Consistent naming conventions
included:
- Traveling Snails # Main app code
- Traveling Snails Tests # Test code
excluded:
- .build # Build artifacts
- .swiftpm # Package manager
- Packages # Dependencies
- "*/Generated" # Generated code
- DerivedData # Xcode artifacts
- Error: Fails builds, must be fixed
- Warning: Shows in build output, recommended to fix
- Disabled: Rules that don't apply to our project
- Limit: 10 warnings maximum
- Reason: Encourages fixing issues promptly
- Override: Increase temporarily for large refactoring
# Initial setup (run once)
./Scripts/setup-swiftlint.sh
# Or manually
swift package resolve
# Check for violations
swift run swiftlint lint
# Auto-fix style issues (safe changes only)
swift run swiftlint --autocorrect
# Check specific file
swift run swiftlint lint "Traveling Snails/Views/SomeView.swift"
# Generate detailed report
swift run swiftlint lint --reporter json > swiftlint-report.json
-
Automatic: Add Run Script Phase via
./Scripts/setup-swiftlint.sh
-
Manual: Copy content from
Scripts/swiftlint-build-script.sh
- Build Integration: Runs before compilation, fails build on errors
-
Workflow:
.github/workflows/swiftlint.yml
- Triggers: All pushes and pull requests
-
Features:
- Security violation detection
- Automatic style corrections
- Report generation
- Artifact upload
The CI pipeline specifically checks for:
- Print statement violations (fails build)
- Sensitive data logging patterns
- High violation counts
- Critical security issues
// ❌ Violation
print("Debug info: \(data)")
// ✅ Fix
Logger.shared.debug("Debug info processed")
// ❌ Violation
NavigationView {
List { /* content */ }
}
// ✅ Fix
NavigationStack {
List { /* content */ }
}
// ❌ Violation
class ViewModel: ObservableObject {
@Published var data: String = ""
}
// ✅ Fix
@Observable
class ViewModel {
var data: String = ""
}
// ❌ Violation
struct TripDetailView: View {
let activities: [Activity]
}
// ✅ Fix
struct TripDetailView: View {
let trip: Trip
@Query private var activities: [Activity]
init(trip: Trip) {
self.trip = trip
self._activities = Query(
filter: #Predicate<Activity> { $0.trip?.id == trip.id }
)
}
}
// ❌ Violation
Text("Hello World")
// ✅ Fix
Text(L10n.helloWorld)
// swiftlint:disable:next rule_name
problematic_code()
// swiftlint:disable rule_name
// Multiple lines of code
// swiftlint:enable rule_name
// swiftlint:disable file_header
// For files that need custom headers
- Generated code: Always disable for auto-generated files
- Third-party code: Disable for external dependencies
- Temporary situations: During large refactoring (re-enable ASAP)
- False positives: When rule incorrectly flags valid code
- Edit
.swiftlint.yml
- Add to
custom_rules
section - Test with sample violations
- Update this documentation
- Change severity or configuration in
.swiftlint.yml
- Test impact on codebase
- Update team via documentation
- Consider migration period for breaking changes
- SwiftLint analyzes 156+ Swift files
- Build time impact: ~5-10 seconds
- CI/CD impact: ~30-60 seconds
- Memory usage: Minimal
- Use
.swiftlint.yml
exclusions for build directories - Consider separate configs for different targets
- Run incrementally during development
# Check installation
which swiftlint
swift run swiftlint version
# Reinstall if needed
brew install swiftlint
# OR
swift package resolve
- Usually about disabled rules with configurations
- Safe to ignore or fix by removing unused configs
- Check Build Phases for SwiftLint script
- Verify script path and permissions
- Check
.swiftlint.yml
syntax
- Use
swift run swiftlint --autocorrect
for style fixes - Address errors first, then warnings
- Consider temporary rule disabling during migration
- Check SwiftLint documentation: https://github.com/realm/SwiftLint
- Review project-specific rules in
.swiftlint.yml
- Ask team members for project-specific guidance
- Use
swift run swiftlint rules
to see all available rules
- Run SwiftLint early and often during development
- Use autocorrect for style issues before committing
- Address errors immediately - don't let them accumulate
- Understand why rules exist rather than blindly following them
- Suggest improvements when rules don't fit project needs
- Check for SwiftLint violations before approving PRs
- Ensure CI checks pass before merging
- Discuss rule violations rather than just fixing them
- Look for patterns that might need new rules
- Review rules quarterly to ensure they remain relevant
- Update SwiftLint version regularly for new features
- Monitor violation trends to identify training needs
- Keep documentation current as rules evolve
- CLAUDE.md - Development principles and patterns
- ARCHITECTURE.md - Overall project architecture
- SwiftData-Patterns.md - Data layer best practices
- Development-Workflow.md - General development workflow
This document is maintained as part of the Traveling Snails project documentation. Last updated: 2025-06-29