Periphery — remove unused code more efficiently
Configuration and use experience
Periphery is an effective method for dealing with legacy code. It is simple to integrate and can be utilised immediately. But I had to spend some time figuring out how to configure it for optimal use. There were not enough articles on Medium to cover this.
Configuration
All configurations are stored to the .periphery.yml file. Because I integrated Periphery into the project, I included the configuration file alongside the scripts required to generate it.
Project (or workspace) reference configuration
- Workspace with multiple projects (xcworkspace + xcproj):
workspace: MyProject/MyProject.xcworkspace- For a regular Xcode project (without xcworkspace):
project: MyProject/MyProject.xcodeprojBuild setup
Because the creators of Periphery left the warning “It’s possible for the index store to become corrupt, or out of sync with the source file.”, set the build parameter:
clean_build: trueThere is room for experimenting among the fundamental settings, however until they accomplished the project’s first large-scale cleaning, they used the following:
retain_assign_only_properties: true
retain_objc_accessible: true
retain_unused_protocol_func_params: true
retain_public: trueYou may learn more about them here.
If you merely want the results of the analysis, utilise the quiet parameter. To get thorough information about the build and analysis process (as well as the presence of a new version of Periphery), use the verbose parameter. Use one of them; they are mutually exclusive.
verbose: true
quiet: trueYou can choose which modules the analysis will ignore. Here is how to specify an arbitrary number of modules.
report_exclude:
- '*/SomeModule.swift'
- '*/OtherModule.swift'For point settings, you can use comments in the code.
// periphery:ignore - Reason for ignoring
let temporaryUnusedVariable = ""You must configure schemes and targets. It is critical to update this list before beginning the scan; otherwise, Periphery will be unable to create a build project for analysis. You should also avoid including unnecessary targets in order to avoid receiving unnecessary information.
schemes:
- SomeServiceProject
targets:
- SomeServiceProjectTargetProject scan
After you have configured the configuration file, you can run a scan.
periphery scan --config scripts/.periphery.ymlIf you utilise a workspace, you may need to generate a project (establish dependencies) and build it (if resource creation is required).
Before you begin, I recommend to clear derived data; the DevCleaner for Xcode tool can help with this.
Schemes and Targets retrieval script
The script’s main purpose is to automate the definition of schemes and targets for updating the .yml configuration file.
Get Volodymyr Mudrytskyi’s stories in your inbox
Join Medium for free to get updates from this writer.
In short, the task can be separated into several stages.
- Create variables to hold lists.
declare -a DEBUG_SCHEMES_LIST
declare -a TARGETS_LIST2. Get a list of projects (workspace case).
WORKSPACE="./MyProject/MyProject.xcworkspace"
PROJECT_FILES=$(find "$(dirname "$WORKSPACE")" -name "*.xcodeproj" -not -path "*./MyProject/Pods/*")3. Go around the list of projects to define schemes.
for PROJECT in $PROJECT_FILES; do
DEBUG_SCHEMES=$(xcodebuild -list -workspace "$WORKSPACE" | awk -v project="$PROJECT_NAME" '/Schemes:/ {p=1; next} p && !/\* / && $1 ~ project {print $1}')4. Bypass the resulting schemes. If everything is appropriate, add them to the list. I utilise the list of exceptions to avoid analysing unnecessary schemes later.
EXCLUDE_SUBSTRINGS=("Pods" "Tests" "Workspace")
for SCHEME in $DEBUG_SCHEMES; do
for SCHEME in $DEBUG_SCHEMES; do
if [[ ! " ${DEBUG_SCHEMES_LIST[@]} " =~ " ${SCHEME} " ]]; then
SKIP_SCHEME=false
for SUBSTRING in "${EXCLUDE_SUBSTRINGS[@]}"; do
if [[ "$SCHEME" == *"$SUBSTRING"* ]]; then
SKIP_SCHEME=true
break
fi
done
if [ "$SKIP_SCHEME" == false ]; then
DEBUG_SCHEMES_LIST+=("$SCHEME")
fi
fi
done5. Define the project targets and carry them out in a similar manner as the schemes.
TARGETS=$(xcodebuild -project "$PROJECT" -list | sed -n '/Targets:/,/Build Configurations:/p' | sed '1d;$d')I also arrange outcomes, including the output of intermediate process comments, and use colour visualisation.
for SCHEME in "${sorted_debug_schemes[@]}"; do
printf "${YELLOW}- %s${NC}\n" "$SCHEME"
doneScan script. Custom output of results.
I don’t use the basic scanning capability. I use a script to further process the results of Periphery.
Removes the extra section of the file path — /Users/my_user/my_project_repo/
result="$(periphery scan --config scripts/.periphery.yml)"
current_dir=$(pwd)
result_stripped_of_absolute_path_prefix=$(echo "$result" | sed "s|$current_dir/||g")I highlight just the terms that relate to having the findings of the analysis; based on the format in which Periphery gives the result, for example file.swift:23:5:
filtered_out_result=$(echo "$result_stripped_of_absolute_path_prefix" | awk '/:[0-9]+:[0-9]+:/{ print }')I then reformat each line to highlight the module name, term number, and function/variable in solid colours. So that critical information is prioritised.
while IFS= read -r line; do
formatted_line=$(echo "$line" \
| sed -E "s|(\/)([^/]+)(\.swift)|\1${bold_black}\2${reset}\3|g" \
| sed -E "s|(:)([0-9]+)(:)|\1${bold_yellow}\2${reset}\3|g" \
| sed -E "s|'([^']+)'|${bold_blue}'\1'${reset}|g")
echo "$formatted_line"
done <<< "$output"To be able to work with data at any convenient time, I configured the storing of information in a separate file.
echo "$output" > "$file_path" 2>&1It is not tough to acquire the outcomes.
line_count=$(echo "$output" | wc -l)
echo "📌 total warnings: ${bold_blue}$line_count${reset}"Periphery generates derived data, and if you scan frequently, you will quickly run out of free space. I established where exactly these data are stored, and I do cleaning immediately after the analysis. I also work out numerous error choices.
derived_data_dir="$HOME/Library/Caches/com.github.peripheryapp"
if [ -d "$derived_data_dir" ]; then
echo "📔 derived data cleaning start"
if rm -rf "$derived_data_dir"; then
echo "🗑 derived data cleared.Result example with raw periphery scan
And script utilised scan
This script improves my experience with Periphery:
- I can better understand what needs to be worked out.
- I notice some problematic circumstances.
- I can postpone the assignment till later or analyse the results further.
- Cleaning derived data keeps everything clean.
For the future
After the project’s major garbage has been removed, it will be easy to go into greater depth about various parameters.
There is also a desire to convert scripts to Swift and build them into standalone executable files.
This is not a pointing path, but rather my personal experience with using Periphery for improving the project. If you have any other suggestions for configuring Periphery, I’d be delighted to read them.









