LoginSignup
5

More than 1 year has passed since last update.

posted at

updated at

SwiftLintのAnalyzeを使って高度な解析をする方法

はじめに

本記事は Swift/Kotlin愛好会 Advent Calendar 2020 の8日目の記事です。
SwiftLintのAnalyze機能を紹介します。

環境

  • OS:macOS Big Sur 11.0.1
  • Swift:5.3.1
  • Xcode:12.2 (12B45b)
  • SwiftLint:0.41.0

本記事で説明しないこと

SwiftLintの「Analyze」とは?

かんたんにいうと「ビルドログのASTを使って解析する」機能です。

Analyzeは実験的な機能であり、いつでも変更される可能性があるとのことです。

Analyzeの使い方

Analyzeの使い方を紹介します。

設定ファイルの作成

Analyze用のルールを設定ファイルの analyzer_rules に記述します。

.swiftlint.yml
analyzer_rules:
  #- explicit_self # 関数は `self.` を付けずに呼び出したいため
  - unused_declaration
  - unused_import

公式ドキュメントで「Analyzer rule: Yes」となっているルールが対象で、私が見た限りでは以下の3つのみでした。

ルール 説明 参考リンク
Explicit Self self. を明示的に書くべき https://qiita.com/uhooi/items/7f5d6cf2b240f60ba1ed#explicit-self
Unused Declaration 宣言した変数やクラスなどは使われるべき https://qiita.com/uhooi/items/8e9767c2e746f4171ded#unused-declaration
Unused Import インポートされたモジュールは使われるべき https://qiita.com/uhooi/items/7f5d6cf2b240f60ba1ed#unused-import

私は同一クラス内の関数を self. なしで呼び出したいため、下の2つのみ有効にしています。

Analyzeの実行

READMEに記載されている通り xcodebuild コマンドでログをファイルに出力し、それを --compiler-log-path オプションで渡します。

コマンドが長くなるので、私は Makefile に定義して make analyze で実行できるようにしました。
make build-debug はCIでビルドが通るかの確認にも使っているため、 swiftlint analyze に対して必要最小限にはなっていません。

Makefile
PRODUCT_NAME := UhooiPicBook # 製品名を適宜変更する
PROJECT_NAME := ${PRODUCT_NAME}.xcodeproj
SCHEME_NAME := ${PRODUCT_NAME}

TEST_SDK := iphonesimulator
TEST_CONFIGURATION := Debug
TEST_PLATFORM := iOS Simulator
TEST_DEVICE ?= iPhone 12 Pro Max
TEST_OS ?= 14.2
TEST_DESTINATION := 'platform=${TEST_PLATFORM},name=${TEST_DEVICE},OS=${TEST_OS}'

XCODEBUILD_BUILD_LOG_NAME := xcodebuild_build.log

.PHONY: analyze
analyze: # Analyze with SwiftLint
    $(MAKE) build-debug
    mint run swiftlint swiftlint analyze --autocorrect --compiler-log-path ./${XCODEBUILD_BUILD_LOG_NAME}

.PHONY: build-debug
build-debug: # Xcode build for debug
    set -o pipefail \
&& xcodebuild \
-sdk ${TEST_SDK} \
-configuration ${TEST_CONFIGURATION} \
-project ${PROJECT_NAME} \
-scheme ${SCHEME_NAME} \
-destination ${TEST_DESTINATION} \
build \
| tee ./${XCODEBUILD_BUILD_LOG_NAME} \
| bundle exec xcpretty --color

--autocorrect オプションを付けると自動で修正されるのでオススメです。

make analyze を実行します。

$ make analyze
# ...
# `make build-debug` のログは省略
# ...
mint run swiftlint swiftlint analyze --autocorrect --compiler-log-path ./xcodebuild_build.log
Loading configuration from '.swiftlint.yml'
Correcting Swift files at paths 
Collecting 'Debug.swift' (1/23)
Collecting 'ActivityRouter.swift' (2/23)
# ...
Correcting 'MonsterListInteractor.swift' (15/23)
/Users/uhooi/Documents/Repos/GitHub/uhooi/UhooiPicBook/UhooiPicBook/Repository/Spotlight/SpotlightRepository.swift:10:1 Corrected Unused Import
Correcting 'MonsterListViewController.swift' (16/23)
# ...
Correcting 'MonsterDetailViewController.swift' (22/23)
Correcting 'SceneDelegate.swift' (23/23)
Done correcting 23 files!

.../SpotlightRepository.swift:10:1 Corrected Unused Import のログからわかる通り、 SpotlightRepository.swift の10行1列目に未使用のモジュールがインポートされているので自動で削除されました。

git diff で確認します。

$ git diff
diff --git a/UhooiPicBook/Repository/Spotlight/SpotlightRepository.swift b/UhooiPicBook/Repository/Spotlight/SpotlightRepository.swift
index e435a9b..ba45ffb 100644
--- a/UhooiPicBook/Repository/Spotlight/SpotlightRepository.swift
+++ b/UhooiPicBook/Repository/Spotlight/SpotlightRepository.swift
@@ -7,7 +7,6 @@

 import CoreGraphics.CGGeometry
 import CoreSpotlight
-import MobileCoreServices

 /// @mockable
 protocol SpotlightRepository: AnyObject { // swiftlint:disable:this file_types_order

確かにインポート文が削除されています。

今回は一度実行済みなので1箇所のみ削除されましたが、初回は大量の import Foundationimport UIKit が削除されて気持ちいいです。

おまけ①: 使用しているインポートが削除される場合がある

先ほど削除された import MobileCoreServices ですが、実はないとビルドエラーになります。

理由としては、iOS 13以前で kUTTypeData の呼び出しに使っているためです。

SpotlightRepository.swift
    private func createAttributeSet(title: String, contentDescription: String, thumbnailData: Data?) -> CSSearchableItemAttributeSet {
        let attributeSet: CSSearchableItemAttributeSet
        if #available(iOS 14.0, *) {
            attributeSet = .init(contentType: .data)
        } else {
            attributeSet = .init(itemContentType: kUTTypeData as String) // !!!: ここでビルドエラーになる
        }
        // ...
        // 中略
        // ...
        return attributeSet
    }

xcodebuild-destination オプションでiOS 14.2を指定しているためだと思われます。
仕方ないので手動でインポート文を戻しました。

このようなケースはまれですが、自動修正する場合はコミット前にビルドが通るか確認しましょう。

おまけ②: Analyzeの実行タイミング

READMEに記載されている通り、Analyzeには時間がかかります。
そのため、Xcodeのビルドフェイズで毎回実行するのを避けています。

現在は手動実行しているのですが、それだと漏れる可能性があるため、いい方法を考え中です。

おわりに

これで未使用のインポート文を大量に削除できます!

以上、 Swift/Kotlin愛好会 Advent Calendar 2020 の8日目の記事でした。
明日はまだ埋まっていません。ぜひ こちら から参加しましょう!
@Sho-heikun さんが参加してくださりました!
ありがとうございます :relaxed:

参考リンク

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5