Swift projects accumulate dead code just like any other language. Unused view controllers, abandoned model classes, deprecated protocol conformances, and leftover Objective-C bridging code all add up. Periphery is the tool that finds it.
What Periphery Detects
Periphery analyzes your entire Swift project and reports:
- Unused classes and structs -- types that are defined but never instantiated or referenced
- Unused functions and methods -- including private methods that nothing calls
- Unused protocols -- protocols that no type conforms to
- Unused properties -- stored and computed properties that are never read
- Unused enum cases -- enum members that are never matched
- Unused protocol conformances -- conformances where the protocol methods are never called through the protocol
- Unused imports --
importstatements for modules that are not used in the file
Installing Periphery
Install via Homebrew:
brew install peripheryapp/periphery/periphery
Or via Mint:
mint install peripheryapp/periphery
Running a Scan
For Xcode projects:
periphery scan --project MyApp.xcodeproj --schemes MyApp --targets MyApp
For Swift Package Manager:
periphery scan --spm
Periphery builds your project first (using xcodebuild or swift build), then analyzes the index store to find unused declarations.
Interpreting Results
Periphery outputs one finding per line:
/Sources/Models/LegacyUser.swift:3:1 - class 'LegacyUser' is unused
/Sources/Views/OldDashboard.swift:12:5 - function 'loadData()' is unused
/Sources/Protocols/Cacheable.swift:1:1 - protocol 'Cacheable' is unused
/Sources/Utils/StringExtensions.swift:8:5 - property 'isValidEmail' is unused
Each line includes the file path, line number, column, and a description of what is unused.
Common Finding Types
"class X is unused" -- the class exists but nothing creates an instance or references the type. Safe to remove if you confirm no dynamic instantiation (e.g., via NSClassFromString).
"function X is unused" -- the function is defined but never called. Check for @objc methods that might be called from Objective-C or Interface Builder.
"protocol X is unused" -- no type conforms to this protocol, or the protocol is conformed to but never used as a type constraint.
"property X is assigned but never read" -- a property is set but its value is never accessed. This is common with @State properties in SwiftUI that were part of an earlier design.
Handling False Positives
Swift's dynamic features can cause false positives:
- @objc methods called from Interface Builder or Objective-C code
- @IBAction and @IBOutlet connected in storyboards
- Codable conformances where properties are used by the encoder/decoder
- Main entry points like
@mainapp delegates
Periphery has built-in handling for many of these patterns. For remaining false positives, use comment annotations:
// periphery:ignore
class LegacyBridge {
// Called from Objective-C code
}
Using Periphery with CleanAI
CleanAI integrates Periphery directly. When you scan a Swift project with CleanAI:
- CleanAI detects the Xcode project or SPM package
- It runs Periphery with the appropriate configuration
- Results appear in the webview panel, grouped by file
- You can remove findings one-by-one or use Auto Clean (Safe)
This gives you Periphery's detection power with CleanAI's visual UI and safe removal workflow.
CI Integration
Add Periphery to your CI pipeline to prevent new dead code:
- name: Check for dead Swift code
run: |
periphery scan --project MyApp.xcodeproj \
--schemes MyApp --targets MyApp \
--format json | jq '.[] | .location' && exit 1 || exit 0
Getting Started
For a quick check, install Periphery and run periphery scan on your project. For ongoing dead code management with visual UI and safe removal, install CleanAI in VS Code or Cursor.