3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

マルチモジュール構成でSwiftLintのルールを使い分ける

Last updated at Posted at 2023-08-22

環境

  • Xcode 14.3.1
  • SwiftLint 0.52.4

はじめに

SwiftLintをプロジェクトに導入している場合、プロジェクト構成はルートディレクトリに .swiftlint.yml を配置し以下のようになっているかと思います。

ディレクトリ構成.txt
ProjectRoot
    ├ .swiftlint.yml
    ├ SampleProject
        ├ xxx.swift
        └ yyy.swift
    └ SampleProjectTests
        ├ xxxTest.swift
        └ yyyTest.swift

ルールの使い分け

なぜルールを使い分けたいのか

プロダクトの不具合を防ぐために force_unwrappingforce_try といったルールを適用しているケースを考えます。

.swiftlint.yml
opt_in_rules:
- force_unwrapping

# force_try はデフォルトで有効

テストコードはデコードしたりキャストしてもテストが成功するように記述しているはずなので強制アンラップや try! を書いても問題ないはずですが、プロダクトコードと同じルールが適用されてしまうと以下のように警告やエラーが表示されてしまいます。

それぞれの警告やエラーに対して // swiftlint:disable:this force_xxx とコメントを追加すればルールを無視することは可能ですが、テストを増やすたびにいちいちコメントを追加するのもとても面倒です。
プロダクトの安全性を高めるためのLintによってテストコードを書くことを阻害されるのは本末転倒です。

テストコードにのみ別のルールを割り当てる

ルートディレクトリとは別にテストコードのディレクトリに対して、テストコード用に新しく作った .swiftlint.yml を配置します
(以下、 SampleProjectTests/.swiftlint.yml と記載します)

ディレクトリ構成.txt
ProjectRoot
    ├ .swiftlint.yml
    ├ SampleProject
        ├ xxx.swift
        └ yyy.swift
    └ SampleProjectTests
        ├ .swiftlint.yml // <- 追加
        ├ xxxTest.swift
        └ yyyTest.swift

テストコードでのみ force_unwrapping と force_try を無効にするには、 SampleProjectTests/.swiftlint.yml 内で disabled_rules として指定することで無効にすることができます。

SampleProjectTests/.swiftlint.yml
disabled_rules:
    - force_unwrapping
    - force_try

SampleProjectTests/.swiftlint.ymlSampleProjectTests 配下の swift ファイル全てに適用されます。
ルートディレクトリに配置した yml ファイルと SampleProjectTests 配下に配置した yml ファイルは

  • 親: .swiftlint.yml
  • 子: SampleProjectTests/.swiftlint.yml

の親子関係になっており、 SampleProjectTests 配下には親子両方の yml ファイルで設定したルールが適用されます。親と子の設定が競合した場合、子の yml ファイルに設定したルールの設定が優先されます

ルール SampleProjectTests で有効か
親子ともに有効 有効
親子ともに無効 無効
親: 有効
子: 無効
無効
親: 無効
子: 有効
有効

実行方法

Xcode で SwiftLint を実行する場合も、コマンドライン上で SwiftLint を実行する場合、特にオプション等の追加は不要です。
Xcode上 では App Target > Build Phases > Run Script Phase で swiftlint コマンドを実行するだけ、コマンドライン上では swiftlint コマンドを実行するだけで自動的にルールが使い分けられます。

$ swiftlint

swiftlint のサブコマンド lint のオプションには --config がありますが、ディレクトリを入れ子構造にしている場合はオプションを使用する必要はなく、むしろ指定すると --config オプションで指定した yml ファイルの設定で上書きされてしまいます。

マルチモジュール構成でSwiftLintのルールを使い分ける

前述のディレクトリ構成に加え、 ModuleAModuleB を追加します。

ディレクトリ構成.txt
ProjectRoot
    ├ .swiftlint.yml
    ├ SampleProject
        ├ xxx.swift
        └ yyy.swift
    ├ SampleProjectTests
        ├ .swiftlint.yml
        ├ xxxTest.swift
        └ yyyTest.swift
    ├ ModuleA
        ├ Sources
            └ aaa.swift
        └ Tests
            └ aaaTest.swift
    └ ModuleB
        ├ Sources
            └ bbb.swift
        └ Tests
            └ bbbTest.swift

この時、テストコードは SampleProjectTests ディレクトリの他に ModuleA/TestsModuleB/Tests の中に含まれていますが、前述のテストコード用のルールは SampleProjectTests ディレクトリにのみ適用され、各モジュール配下のテストコードのディレクトリには適用されません。 ModuleA/TestsModuleB/Tests の中のテストコードでは force_unwrapping と force_try が有効になってしまいます。

そこで、 SampleProjectTests/.swiftlint.yml をコピーしたものを ModuleA/TestsModuleB/Tests にも配置することで、 ModuleA/TestsModuleB/Tests にもテストコード用のルールを適用することができます。

ディレクトリ構成(一部省略).txt
ProjectRoot
    ├ .swiftlint.yml
    ├ SampleProject
    ├ SampleProjectTests
        └ .swiftlint.yml
    ├ ModuleA
        ├ Sources
        └ Tests
            ├ .swiftlint.yml // <- SampleProjectTests/.swiftlint.yml をコピー&ペースト
            └ aaaTest.swift
    └ ModuleB
        ├ Sources
        └ Tests
            ├ .swiftlint.yml // <- SampleProjectTests/.swiftlint.yml をコピー&ペースト
            └ bbbTest.swift

テストコード用の .swiftlint.yml を一括管理する

上記のように SampleProjectTests/.swiftlint.yml を各モジュールにコピーしたあとにテストコード用のルールを変更しようとすると、前述のディレクトリ構成では

  • SampleProjectTests/.swiftlint.yml
  • ModuleA/Tests/.swiftlint.yml
  • ModuleB/Tests/.swiftlint.yml

3ファイルに同じ修正をする必要があります
テストコード用のルールを変更しようと思った時、毎回3ファイル全ての修正を忘れずにいられるかというとそんなことはないかと思います。
理想としては1つのファイルをアップデートしたら3ファイル全てに反映されるのが理想です。

そこで、 SwiftLint を実行する直前に上記3ファイルの中身が同じになるように1つのファイルを残り2箇所に複製する処理を入れます。

Xcode 上でもコマンドライン上でもコードが長くなるのを防ぐために、以下のようにシェルスクリプトとして切り出しそれを呼び出すようにします。

run_swiftlint.sh
# SampleProjectTests/.swiftlint.yml を各モジュールのテストディレクトリに複製する
TEST_YML_PASS=SampleProjectTests/.swiftlint.yml
cp ${TEST_YML_PASS} ModuleA/Tests
cp ${TEST_YML_PASS} ModuleB/Tests

# SwiftLintを実行する
swiftlint

こうすることで、 SampleProjectTests/.swiftlint.yml のみを修正することで全てのモジュールのテストコードにテストコード用のルールを適用することができます。

ここで、 ModuleA/TestsModuleB/Tests 内の yml ファイルを修正してしまうと SwiftLint の実行時に上書きされ内容が消えてしまうため、 yml ファイル内に SampleProjectTests 内のものを修正するようにコメントをしておくとわかりやすいです。

SampleProjectTests/.swiftlint.yml
# このファイルは各モジュールのテストディレクトリに複製されています。
# もし編集する際には `SampleProjectTests` のものを編集してください。
disabled_rules:
    - force_unwrapping
    - force_try

参考文献

3
0
0

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
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?