brew install peripheryapp/periphery/periphery
mint install peripheryapp/periphery
scan
コマンドスキャン コマンドは Periphery の主な機能です。ガイド付きセットアップを開始するには、プロジェクト ディレクトリに移動して次を実行します。
periphery scan --setup
ガイド付きセットアップは Xcode および SwiftPM プロジェクトでのみ機能します。Bazel などの Apple 以外のビルド システムで Periphery を使用するには、「ビルド システム」を参照してください。
いくつかの質問に答えると、Periphery はフル スキャン コマンドを出力して実行します。
ガイド付きセットアップは入門のみを目的としており、Periphery に慣れたら、より高度なオプションをいくつか試すことができます。これらのオプションはすべて、 periphery help scan
で確認できます。
Periphery から一貫した結果を得るには、分析するために選択したビルド ターゲットの影響を理解することが重要です。たとえば、App、Lib、Test という 3 つのターゲットで構成されるプロジェクトを想像してください。 App ターゲットは Lib をインポートし、Tests ターゲットは App と Lib の両方をインポートします。 3 つすべてを--targets
オプションに指定すると、Periphery はプロジェクト全体を分析できるようになります。ただし、App と Lib のみを分析し、テストは分析しないことを選択した場合、Periphery は、テストによってのみ参照される未使用のコードのインスタンスをいくつか報告する可能性があります。したがって、Periphery が間違った結果を提供したと疑われる場合は、分析するために選択したターゲットを考慮することが重要です。
プロジェクトが 1 つ以上のスタンドアロン フレームワークで構成されており、そのフレームワークにそのインターフェイスを使用する何らかのアプリケーションが含まれていない場合は、 --retain-public
含めることによって、すべてのパブリック宣言が実際に使用されていると想定するように Periphery に指示する必要があります。オプション。
Objective-C と Swift が混在するプロジェクトの場合は、これが結果に与える影響について読むことを強くお勧めします。
プロジェクトに適切なオプションを決定したら、それらを YAML 構成ファイルに保存したい場合があります。これを実現する最も簡単な方法は、 --verbose
オプションを指定して Periphery を実行することです。出力の先頭近くに、以下の YAML 形式の設定を含む[configuration:begin]
セクションが表示されます。設定をコピーして、プロジェクト フォルダーのルートにある.periphery.yml
に貼り付けます。これで、単にperiphery scan
実行できるようになり、YAML 構成が使用されるようになります。
Peripheral は最初にプロジェクトをビルドします。 Xcode プロジェクトの場合、 --schemes
オプションで提供されるスキームはxcodebuild
を使用して構築されます。 Swift Package Manager プロジェクトの場合、 --targets
オプションで提供される個々のターゲットはswift build
を使用してビルドされます。 Swift コンパイラーは、インデックス作成中と呼ばれる手法を使用して、プロジェクトのソース コードの構造に関する情報を含むインデックス ストアを作成します。
プロジェクトが構築された後、Periphery はインデックス作成フェーズを実行します。 --targets
オプションで指定されたターゲットのメンバーであるすべてのソース ファイルについて、Periphery はその構造情報をインデックス ストアから取得し、プロジェクトの独自の内部グラフ表現を構築します。 Periphery はまた、各ファイルの抽象構文ツリー (AST) を分析して、インデックス ストアによって提供されない詳細を埋めます。
インデックス作成が完了すると、Periphery はグラフを分析して未使用のコードを特定します。このフェーズは、未使用コードの特定のシナリオを簡単に識別できるようにグラフを変更するいくつかのステップで構成されます。最後のステップでは、グラフをルートからたどって、参照されなくなった宣言を特定します。
Periphery の目的は、未使用の宣言のインスタンスを報告することです。宣言は、 class
、 struct
、 protocol
、 function
、 property
、 constructor
、 enum
、 typealias
、 associatedtype
などです。ご想像のとおり、Periphery は単純な参照されていない宣言、たとえば、クラスのどこでも使用されなくなったclass
を識別できます。コードベース。
ペリフェラルは、未使用のコードのより高度なインスタンスを識別することもできます。次のセクションでこれらについて詳しく説明します。
ペリフェラルは、未使用の関数パラメータを識別できます。未使用のパラメータのインスタンスは、オーバーライドされたメソッドのパラメータだけでなく、プロトコルとそれに準拠する宣言でも識別できます。これらの両方のシナリオについては、以下でさらに説明します。
プロトコル関数の未使用パラメータは、そのパラメータがすべての実装でも未使用である場合にのみ未使用として報告されます。
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. " )
}
}
ヒント
--retain-unused-protocol-func-params
オプションを使用すると、プロトコルおよび準拠する関数からの未使用のパラメーターをすべて無視できます。
プロトコルと同様に、オーバーライドされた関数のパラメーターは、基本関数およびすべてのオーバーライド関数でも使用されていない場合にのみ、未使用として報告されます。
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. " )
}
}
基本関数宣言を変更するアクセス権がないため、外部モジュール (Foundation など) で定義されたプロトコルまたはクラスの未使用のパラメーターは常に無視されます。
fatalError
呼び出すだけの関数の未使用パラメータも無視されます。このような関数は、多くの場合、サブクラスで必須の初期化子が実装されていません。
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 " )
}
}
オブジェクトによって準拠されるプロトコルは、それが存在型としても使用されるか、ジェネリック メソッド/クラスを特殊化するために使用されない限り、実際には使用されません。ペリフェラルは、そのような冗長プロトコルが 1 つのオブジェクトによって準拠されているか、複数のオブジェクトによって準拠されているかどうかを識別できます。
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 ( )
ここでは、 someMethod
の両方の実装が呼び出されているにもかかわらず、どの時点でもオブジェクトがMyProtocol
の型をとらないことがわかります。したがって、プロトコル自体は冗長であり、 MyClass1
またはMyClass2
それに準拠するメリットはありません。 MyProtocol
各冗長な適合性とともに削除し、各クラスにsomeMethod
だけを保持することができます。
オブジェクトの通常のメソッドやプロパティと同様に、プロトコルによって宣言された個々のプロパティやメソッドも未使用として識別できます。
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 ( )
ここでは、 MyProtocol
自体が使用されており、削除できないことがわかります。ただし、 unusedProperty
MyConformingClass
で呼び出されないため、Periphery は、 MyProtocol
内のunusedProperty
の宣言も未使用であることを識別でき、 unusedProperty
の未使用の実装とともに削除できます。
Periphery は、未使用の列挙型を識別できるだけでなく、個々の未使用の列挙型ケースも識別できます。生で表現できないプレーン列挙型、つまりString
、 Character
、 Int
または浮動小数点値型を持たない列挙型は、確実に識別できます。ただし、生の値型を持つ列挙型は本質的に動的である可能性があるため、使用されると想定する必要があります。
簡単な例でこれを明確にしてみましょう。
enum MyEnum : String {
case myCase
}
func someFunction ( value : String ) {
if let myEnum = MyEnum ( rawValue : value ) {
somethingImportant ( myEnum )
}
}
myCase
ケースへの直接の参照はないため、今後は必要なくなる可能性があると予想するのが合理的ですが、これが削除された場合、 someFunction
に"myCase"
の値が渡された場合にsomethingImportant
が呼び出されないことがわかります。
割り当てられているが一度も使用されていないプロパティは、次のように識別されます。
class MyClass {
var assignOnlyProperty : String // 'assignOnlyProperty' is assigned, but never used
init ( value : String ) {
self . assignOnlyProperty = value
}
}
場合によっては、これが意図された動作である可能性があるため、そのような結果を黙らせるために使用できるオプションがいくつかあります。
--retain-assign-only-property-types
を使用して、すべての割り当て専用プロパティをタイプ別に保持します。指定された型は、プロパティ宣言での使用法 (オプションの疑問符を除く) と正確に一致する必要があります (例: String
、 [String]
、 Set
)。 Periphery は推論されたプロパティ タイプを解決できないため、場合によっては、プロパティに明示的なタイプ アノテーションを追加する必要がある場合があります。--retain-assign-only-properties
を使用して、割り当てのみのプロパティ分析を完全に無効にします。public
としてマークされているものの、ホーム モジュールの外部から参照されていない宣言は、冗長なパブリック アクセシビリティを持つものとして識別されます。このシナリオでは、 public
アノテーションを宣言から削除できます。冗長なパブリック アクセシビリティを削除すると、次のような利点があります。
final
推論できます。 final
クラスはコンパイラによってより適切に最適化されます。この分析は--disable-redundant-public-analysis
で無効にできます。
ペリフェラルは、スキャンしたターゲット、つまり--targets
引数で指定されたターゲットの未使用のインポートを検出できます。 Swift ソース ファイルが利用できず、 @_exported
の使用が観察できないため、他のターゲットの未使用のインポートを検出できません。 @_exported
、ターゲットによってエクスポートされた宣言がインポートされたターゲットによって必ずしも宣言されなくなるようにターゲットのパブリック インターフェイスを変更するため、問題があります。たとえば、 Foundation
ターゲットは、他のターゲットの中でも特にDispatch
をエクスポートします。特定のソース ファイルがFoundation
をインポートし、 DispatchQueue
を参照するが、 Foundation
からの他の宣言を参照しない場合は、 DispatchQueue
タイプも使用できなくなるため、 Foundation
インポートを削除できません。したがって、誤検知を回避するために、Periphery はスキャンしたターゲットの未使用のインポートのみを検出します。
Periphery は Objective-C ファイルをスキャンできないため、Swift と Objective-C が混在するターゲットに対して誤検知が発生する可能性があります。したがって、大量の Objective-C を含むプロジェクトの未使用インポート検出を無効にするか、結果から混合言語ターゲットを手動で除外することをお勧めします。
型は動的に型付けされる可能性があるため、ペリフェラルは Objective-C コードを解析できません。
デフォルトでは、Periphery は、Objective-C ランタイムからアクセスできる宣言が使用中であることを想定しません。プロジェクトが Swift と Objective-C の混合である場合、 --retain-objc-accessible
オプションを使用してこの動作を有効にすることができます。 Objective-C ランタイムからアクセスできる Swift 宣言は、 @objc
または@objcMembers
で明示的に注釈が付けられた宣言、およびNSObject
直接または別のクラス経由で間接的に継承するクラスです。
あるいは、 --retain-objc-annotated
使用して、 @objc
または@objcMembers
で明示的に注釈が付けられた宣言のみを保持することもできます。 NSObject
継承する型は、明示的なアノテーションがない限り保持されません。このオプションを使用すると、未使用のコードがさらに見つかる可能性がありますが、宣言が実際に Objective-C コードで使用されている場合、結果の一部が正しくない可能性があることに注意してください。これらの誤った結果を解決するには、宣言に@objc
注釈を追加する必要があります。
Swift は、Periphery には表示されないCodable
型の追加コードを合成するため、合成されていないコードから直接参照されないプロパティに対して誤検知が発生する可能性があります。プロジェクトにそのような型が多数含まれている場合は、 --retain-codable-properties
を使用してCodable
型のすべてのプロパティを保持できます。あるいは、 --retain-encodable-properties
を使用して、 Encodable
タイプのプロパティのみを保持することもできます。
Periphery によってスキャンされていない外部モジュール内のプロトコルによってCodable
準拠が宣言されている場合、 --external-codable-protocols "ExternalProtocol"
を使用して、そのプロトコルをCodable
として識別するように Periphery に指示できます。
XCTestCase
継承するクラスは、そのテスト メソッドとともに自動的に保持されます。ただし、クラスが別のクラス ( UnitTestCase
など) を介して間接的にXCTestCase
を継承し、そのクラスが Periphery によってスキャンされないターゲットに存在する場合、 --external-test-case-classes UnitTestCase
オプションを使用して Periphery に指示する必要があります。 UnitTestCase
XCTestCase
サブクラスとして扱います。
プロジェクトに Interface Builder ファイル (ストーリーボードや XIB など) が含まれている場合、Periphery は未使用の宣言を識別するときにこれらを考慮します。ただし、Periphery は現在、未使用のクラスのみを識別します。この制限は、Periphery が Interface Builder ファイルをまだ完全に解析していないために存在します (問題 #212 を参照)。誤検知を回避するという Periphery の設計原則により、クラスが Interface Builder ファイル内で参照される場合、実際にはそうでない場合でも、そのすべてのIBOutlets
およびIBActions
が使用されると想定されます。このアプローチは、ペリフェラルが Interface Builder ファイルを解析できるようになったら、未使用のIBActions
とIBOutlets
正確に識別するように修正される予定です。
何らかの理由で、未使用のコードを保持しておきたい場合があります。ソース コードのコメント コマンドを使用すると、特定の宣言を無視し、結果から除外することができます。
コメント無視コマンドは、宣言とすべての子孫の宣言を無視するために、その宣言の上の行に直接配置できます。
// periphery:ignore
class MyClass { }
特定の未使用の関数パラメータを無視することもできます。
// periphery:ignore:parameters unusedOne,unusedTwo
func someFunc ( used : String , unusedOne : String , unusedTwo : String ) {
print ( used )
}
// periphery:ignore:all
コマンドをソース ファイルの先頭に配置すると、ファイルの内容全体を無視できます。コメントは、インポート ステートメントを含むすべてのコードの上に配置する必要があることに注意してください。
コメント コマンドでは、ハイフンに続く末尾のコメントもサポートされているため、同じ行に説明を含めることができます。
// periphery:ignore - explanation of why this is necessary
class MyClass { }
Xcode 経由でまったく同じコマンドを使用することになるため、Xcode 統合をセットアップする前に、まずターミナルで Periphery を動作させることを強くお勧めします。
プロジェクト ナビゲータでプロジェクトを選択し、ターゲット セクションの左下にある + ボタンをクリックします。 [クロスプラットフォーム]を選択し、 [集約]を選択します。 「次へ」をクリックします。
Periphery は、維持と開発に多大な労力を必要とする情熱的なプロジェクトです。 Periphery が役立つと思われる場合は、GitHub スポンサーを通じてスポンサーになることを検討してください。
以下の寛大なスポンサーに心より感謝いたします。
SaGa Corp は、金融関係者とその顧客向けに独自のテクノロジーを開発しています。
Emerge Tools は、モバイル アプリとそれを構築するチームを強化するために設計された革新的な製品スイートです。