Sitemap
Volodymyr Mudrytskyi

Volodymyr Mudrytskyi

iOS Developer with Solid Experience in e-Commerce. I am passionate in optimising code and innovating when it makes sense. I enjoy reading.

Periphery — remove unused code more efficiently

Configuration and use experience

5 min readFeb 22, 2025

--

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.

Press enter or click to view image in full size

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.xcodeproj

Build 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: true

There 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: true

You 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: true

You 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:
- SomeServiceProjectTarget

Project scan

After you have configured the configuration file, you can run a scan.

periphery scan --config scripts/.periphery.yml

If 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.

  1. Create variables to hold lists.
declare -a DEBUG_SCHEMES_LIST
declare -a TARGETS_LIST

2. 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
done

5. 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"
done

Scan 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>&1

It 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

Press enter or click to view image in full size

And script utilised scan

Press enter or click to view image in full size

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.

--

--

Written by Volodymyr Mudrytskyi

iOS Developer with Solid Experience in e-Commerce. I am passionate in optimising code and innovating when it makes sense. I enjoy reading.