SkillAgentSearch skills...

Periphery

A tool to identify unused code in Swift projects.

Install / Use

/learn @peripheryapp/Periphery
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<h1 align="center"> <img src="assets/logo.png" alt="Periphery" height="60" /> <br> Periphery </h1> <h4 align="center">A tool to identify unused code in Swift projects.</h4> <p align="center"><q><i>Now I am become Delete, the destroyer of codes.</i></q></p> <p align="center"> <a href="https://github.com/peripheryapp/periphery/releases/latest"> <img src="https://img.shields.io/github/release/peripheryapp/periphery.svg?color=008DFF"/></a> <img src="https://img.shields.io/badge/platform-macOS%20|%20Linux-008DFF"> <a href="#sponsors-"> <img src="https://img.shields.io/github/sponsors/peripheryapp?logo=githubsponsors&color=db61a2"> </a> <br> <br> </p>

Contents

Installation

Homebrew

brew install periphery

Mint

mint install peripheryapp/periphery

Bazel

bazel_dep(name = "periphery", version = "<version>", dev_dependency = True)
use_repo(use_extension("@periphery//bazel:generated.bzl", "generated"), "periphery_generated")

See Bazel below for usage instructions.

How To Use

The scan Command

The scan command is Periphery's primary function. To begin a guided setup, change to your project directory and run:

periphery scan --setup

The guided setup will detect your project type and configure a few options. After answering a few questions, Periphery will print out the full scan command and execute it.

The guided setup is only intended for introductory purposes. Once you are familiar with Periphery, you can try some more advanced options, all of which can be seen with periphery help scan.

To get the most from Periphery, it’s important to understand how it works. Periphery first builds your project; it does this to generate the “index store”. The index store contains detailed information about the declarations (class, struct, func, etc.) in your project and their references to other declarations. Using this store, Periphery builds an in-memory graph of the relational structure of your project, supplementing it with additional information obtained by parsing each source file. Next, the graph is mutated to make it more suitable for detecting unused code, e.g., marking your project’s entry points. Finally, the graph is traversed from its roots to identify unreferenced declarations.

[!TIP] The index store only contains information about source files in the build targets compiled during the build phase. If a given class is only referenced in a source file that was not compiled, then Periphery will identify the class as unused. It's important to ensure you build all the targets you expect to contain references. For an Xcode project, this is controlled using the --schemes option. For a Swift package, all targets are built automatically.

If your project consists of one or more standalone frameworks that do not also contain some kind of application that consumes their interfaces, you need to tell Periphery to assume that all public declarations are used with the --retain-public option.

For projects that are mixed Objective-C & Swift, it's highly recommended you read about the implications this can have on your results.

Configuration

Once you've settled upon the appropriate options for your project, you may wish to persist them in a YAML configuration file. The simplest way to achieve this is to run Periphery with the --verbose option. Near the beginning of the output, you will see the [configuration:begin] section with your configuration formatted as YAML below. Copy & paste the configuration into .periphery.yml in the root of your project folder. You can now simply run periphery scan and the YAML configuration will be used.

Analysis

The goal of Periphery is to report instances of unused declarations. A declaration is a class, struct, protocol, function, property, constructor, enum, typealias, associatedtype, etc. As you'd expect, Periphery can identify simple unreferenced declarations, e.g., a class that is no longer used anywhere in your codebase.

Periphery can also identify more advanced instances of unused code. The following section explains these in detail.

Function Parameters

Periphery can identify unused function parameters. Instances of unused parameters can also be identified in protocols and their conforming declarations, as well as parameters in overridden methods. Both of these scenarios are explained further below.

Protocols

An unused parameter of a protocol function will only be reported as unused if the parameter is also unused in all implementations.

protocol Greeter {
    func greet(name: String)
    func farewell(name: String) // 'name' is unused
}

class InformalGreeter: Greeter {
    func greet(name: String) {
        print("Sup " + name + ".")
    }

    func farewell(name: String) { // 'name' is unused
      print("Cya.")
    }
}

[!TIP] You can ignore all unused parameters from protocols and conforming functions with the --retain-unused-protocol-func-params option.

Overrides

Similar to protocols, parameters of overridden functions are only reported as unused if they're also unused in the base function and all overriding functions.

class BaseGreeter {
    func greet(name: String) {
        print("Hello.")
    }

    func farewell(name: String) { // 'name' is unused
        print("Goodbye.")
    }
}

class InformalGreeter: BaseGreeter {
    override func greet(name: String) {
        print("Sup " + name + ".")
    }

    override func farewell(name: String) { // 'name' is unused
        print("Cya.")
    }
}

Foreign Protocols & Classes

Unused parameters of protocols or classes defined in foreign modules (e.g. Foundation) are always ignored since you do not have access to modify the base function declaration.

fatalError Functions

Unused parameters of functions that simply call fatalError are also ignored. Such functions are often unimplemented required initializers in subclasses.

class Base {
    let param: String

    required init(param: String) {
        self.param = param
    }
}

class Subclass: Base {
    init(custom: String) {
        super.init(param: custom)
    }

    required init(param: String) {
        fatalError("init(param:) has not been implemented")
    }
}

Protocols

A protocol that is conformed to by an object is not truly used unless it's also used as an existential type or to specialize a generic method/class. Periphery is able to identify such redundant protocols whether they are conformed to by one or even multiple objects.

protocol MyProtocol { // 'MyProtocol' is redundant
    func someMethod()
}

class MyClass1: MyProtocol { // 'MyProtocol' conformance is redundant
    func someMethod() {
        print("Hello from MyClass1!")
    }
}

class MyClass2: MyProtocol { // 'MyProtocol' conformance is redundant
    func someMethod() {
        print("Hello from MyClass2!")
    }
}

let myClass1 = MyClass1()
myClass1.someMethod()

let myClass2 = MyClass2()
myClass2.someMethod()

Here we can see that despite both implementations of someMethod being called, at no point does an object take on the type of MyProtocol. Therefore, the protocol itself is redundant, and there's no benefit from MyClass1 or MyClass2 conforming to it. We can remove MyProtocol along with each redundant conformance and just keep someMethod in each class.

Just like a normal method or property of an object, individual properties and methods declared by your protocol can also be identified as unused.

protocol MyProtocol {
    var usedProperty: String { get }
    var unusedProperty: String { get } // 'unusedProperty' is unused
}

class MyConformingClass: MyProtocol {
    var usedProperty: String = "used"
    var unusedProperty: String = "unused" // 'unusedProperty' is unused
}

class MyClass {
    let conformingClass: MyProtocol

    init() {
        conformingClass = MyConformingClass()
    }

    func perform() {
        print(conformingClass.usedProperty)
    }
}

let myClass = MyClass()
myClass.perform()

Here we can see that MyProtocol is itself used and cannot be removed. However, since unusedProperty is never called on MyConformingClass, Periphery can identify that the declaration of unusedProperty in MyProtocol is also unused and can be removed along with the unused implementation of unusedProperty.

Enumerations

Along with being able to identify unused enumerations, Periphery can also identify individual unused enum cases. Plain enums that are not raw representable, i.e., that don't have a String, Character, Int, or floating-point value type, can be reliably identified. However, enumerations that do have a raw value type can be dynamic, and therefore must be assumed to be used.

Let's clear this up with a quick example:

enum MyEnum: String {
    case myCase
}

func someFunction(
View on GitHub
GitHub Stars6.1k
CategoryDevelopment
Updated19h ago
Forks227

Languages

Swift

Security Score

100/100

Audited on Mar 31, 2026

No findings