Skip to content

What's new in Periphery 2.0

Ian Leitch edited this page Nov 10, 2020 · 7 revisions

Periphery is a tool to identify unused code in Swift projects.

Periphery 2.0 is a major update that fundamentally changes the method by which your source code is indexed. Previously Periphery used SourceKit's indexing feature. It got the job done, but was lacking in a few areas. First of all, using SourceKit is a pain - in order to index a file, you also need to provide all of the compiler flags necessary for the Swift compiler to parse and type-check the file. Obtaining these compiler flags was the tricky part - Periphery would perform a full clean build of your project, and then parse the xcodebuild log to extract the arguments. I think it's fair to say that was a giant hack.

So not only would Periphery perform a full clean build of your project on every run, but SourceKit would also sometimes crash. Though perhaps the worst problem is that the result were becoming increasingly more inaccurate with each Xcode release. The reason for this is that the indexing feature is no longer in active development. It's maintained to some degree, but only against a rather small set of tests in the Swift project - or so I'm told.

Thankfully Apple has been working on a replacement - the index store a.k.a Index-While-Building. As the name suggests, the index is now populated when your project is built, which eliminates the need for a background process to perform the indexing. Periphery can now use this index directly. It comes with some huge benefits too - Periphery still needs to build your project of course, but it no longer needs to perform a clean build each time, and can instead perform incremental builds. It's much faster, and the results are more accurate - we've been able to resolve a large number of inaccuracies in Periphery, many of them due to the switch to the index store.

Many thanks to @kateinoigakukun for doing a lot of this transition work!

Now let's take a look at the other major improvements.

Comment Commands

You can now instruct Periphery to ignore declarations using comments in your code. While keeping your project clean of unused code is an ideal, there are of course always instances where you need to keep some around.

An ignore comment command can be placed directly on the line above any declaration to ignore it, and all descendant declarations:

// periphery:ignore
class MyClass {}

You can also ignore specific unused function parameters:

// periphery:ignore:parameters unusedOne,unusedTwo
func someFunc(used: String, unusedOne: String, unusedTwo: String) {
    print(used)
}

The // periphery:ignore:all command can be placed at the top of the source file to ignore the entire contents of the file. Note that the comment must be placed above any code, including import statements.

Assign-only Properties

This feature was previously removed, but has been added back and enabled by default. Properties that are assigned but never used are identified as such, e.g:

class MyClass {
    var assignOnlyProperty: String // 'assignOnlyProperty' is assigned, but never used

    init(value: String) {
        self.assignOnlyProperty = value
    }
}

In some cases this may be the intended behavior, so to silence these results you can either disable this analysis technique entirely with --retain-assign-only-properties, or ignore individual properties using Comment Commands.

Swift Package Manager (SPM) Support

In part because of the switch to the index store, Periphery now supports analyzing SPM projects. Simply run periphery scan to build and analyze all of the targets in your Package.swift, or specify the ones you want with the --targets option.

Linux Support

Did you know that the SPM supports Linux? It does! And now so too does Periphery. Caveat: Xcode projects are still only supported on macOS. Perhaps Apple will port Xcode to Linux one day, but we're not holding our breath...

Easier To Use In CI

Again thanks to the index store, it's now trivial to use Periphery has part of your Continuous Integration workflow. If you'd like to use Periphery immediately after running your tests, you can use the --skip-build option, provided that your build & test steps also built all of the targets you wish to analyze.

For more complex setups, you may also benefit from the --index-store-path option if your index store exists in a non-standard location.

Bug Fixes

We've fixed a large number of false positives, checkout the CHANGELOG for a full list. If you've tried Periphery in the past but weren't satisfied with the results, now might be the time to give it another try.