5
Help us understand the problem. What are the problem?

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:

参考リンク

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
5
Help us understand the problem. What are the problem?