一键导入
clean-code
Detects and removes unused code using Periphery. Use when cleaning up dead code, removing unused functions, or maintaining code hygiene.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Detects and removes unused code using Periphery. Use when cleaning up dead code, removing unused functions, or maintaining code hygiene.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Swift 6 concurrency patterns. Use when working with async/await, actors, MainActor isolation, or Sendable conformance.
Creates Features for dependency injection. Use when creating features, exposing public entry points, or wiring up dependencies.
Creates Repositories that abstract data access. Use when creating repositories, transforming DTOs to domain models, or implementing local-first caching. Supports remote-only, local-only, and cached (remote + local) repositories with CachePolicy.
Creates a new feature module with minimal viable structure. Use when bootstrapping a new feature from scratch, scaffolding the Tuist module, Container, Feature entry point, DeepLinkHandler, and initial screen with placeholder Text view. Includes all unit tests, mocks, stubs, and app integration. For adding domain/data layers afterward, use /datasource, /repository, /usecase. For enhancing views, use /view, /viewmodel, /navigator.
Creates Navigator for navigation. Use when setting up navigation, adding navigation to ViewModels, or testing navigation behavior.
Creates ViewModels with state management. Use when creating ViewModels, implementing ViewState pattern, or adding state management for features. Delegates to /usecase for domain use cases and to /feature for Container/Feature wiring.
| name | clean-code |
| description | Detects and removes unused code using Periphery. Use when cleaning up dead code, removing unused functions, or maintaining code hygiene. |
| invocation | user |
Automated dead code detection and removal using Periphery.
Execute the following steps in order:
If a recent build exists, reuse its index store to skip the build step:
# Find the index store from the most recent build
INDEX_STORE=$(find ~/Library/Developer/Xcode/DerivedData/Challenge-*/Index.noindex/DataStore -maxdepth 0 2>/dev/null | head -1)
# Skip build if index store exists, otherwise run full scan
if [ -n "$INDEX_STORE" ]; then
mise x -- periphery scan --skip-build --index-store-path "$INDEX_STORE"
else
mise x -- periphery scan
fi
Analyze the output to identify unused code. The scan retains:
CRITICAL: Before removing any code, search for tests and mocks that reference it.
For each unused item reported by Periphery:
# Search for references in Tests directories
grep -r "functionName\|ClassName" Libraries/**/Tests/
Check these locations:
Tests/Mocks/ - Mock implementations of protocolsTests/Data/ - Repository and DataSource testsTests/Domain/ - UseCase testsTests/Presentation/ - ViewModel testsIf references are found:
For each warning reported by Periphery:
Remove from production code:
Update related tests and mocks:
For ViewState Equatable removals:
Tests/Extensions/ (see below)After removing code, run SwiftLint to auto-correct formatting issues:
mise x -- swiftlint --fix --quiet
Build and execute all tests in the workspace:
xcodebuild test \
-workspace Challenge.xcworkspace \
-scheme "Challenge (Dev)" \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.1'
If tests fail:
Run Periphery again to confirm no unused code remains. The tuist test build from the previous step provides a fresh index store:
INDEX_STORE=$(find ~/Library/Developer/Xcode/DerivedData/Challenge-*/Index.noindex/DataStore -maxdepth 0 2>/dev/null | head -1)
mise x -- periphery scan --skip-build --index-store-path "$INDEX_STORE"
Expected output: * No unused code detected.
Periphery configuration is in .periphery.yml:
project: {AppName}.xcworkspace
schemes:
- "{AppName} (Dev)"
- "{AppName}UITests"
retain_public: true
retain_objc_annotated: true
retain_codable_properties: true
retain_swift_ui_previews: true
Why two schemes: {AppName} (Dev) covers the app and all SPM package dependencies. {AppName}UITests covers the native UI test target. Both are analyzed for dead code.
Why no exclude_tests: With --skip-build --index-store-path, Periphery cannot correctly identify SPM test targets from the pre-built index store, causing false positives. Instead, the two schemes explicitly define the analysis scope — only app, SPM source packages, and UI tests are analyzed. SPM test targets are not in any scheme, so they are not analyzed.
| Option | Description |
|---|---|
retain_public | Keep public declarations (for libraries) |
retain_objc_annotated | Keep ObjC-annotated declarations |
retain_codable_properties | Keep Codable properties even if unread |
retain_swift_ui_previews | Keep SwiftUI Preview providers |
When Periphery reports an unused protocol method:
grep -r "methodName" Libraries/**/Tests/Cache/storage methods might be unused if caching isn't implemented yet. Options:
{Name}MemoryDataSourceMock{Name}MemoryDataSourceTests.swiftreport_exclude if intentionally reservedCustom == implementations on ViewState enums may be reported as unused because SwiftUI doesn't require Equatable for view state. However, tests use these implementations for assertions like #expect(sut.state == .loaded(expected)).
When removing == from ViewState:
== function from production codeTests/Extensions/:// Tests/Extensions/MyViewState+Equatable.swift
@testable import MyModule
extension MyViewState: @retroactive Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.idle, .idle), (.loading, .loading):
true
case let (.loaded(lhsValue), .loaded(rhsValue)):
lhsValue == rhsValue
case let (.error(lhsError), .error(rhsError)):
lhsError.localizedDescription == rhsError.localizedDescription
default:
false
}
}
}
Note: Use @retroactive to silence the "conformance of imported type" warning.
See /testing skill for more details on Equatable extensions.
If a Domain Model property is unused: