Help us understand the problem. What is going on with this article?

SwiftLintの運用ノウハウ

概要

SwiftのLintツールの一つであるSwiftLintの運用ノウハウについて、自己流ですが公開します。本記事で説明するノウハウは個人アプリの開発にて実施している内容になります。

SwiftLintの運用に関し、主に以下の内容を説明します。

  • SwiftLintの活用方法
  • SwiftLintのバージョンアップへの追従
  • オプトインルールを全抽出するワンライナーコマンド
  • 設定ファイル(.swiftlint.yml)の内容

※執筆時のswiftLintバージョン: 0.30.1(最新)

また、SiwftLintのコマンド一覧については以下の記事にまとめています。
SwiftLintコマンドとそのコマンドオプションの一覧まとめ

SwiftLintの活用方法

SwiftLintはコンパイラよりもより詳細にソースコードの静的解析が行えるツールです。静的解析によって、より細かいwarningを出せるだけでなく、SwiftLintではautocorrectオプションによりソースコードの自動修正が可能です。また、--formatオプションを付与することで、自動インデント修正も行ってくれるため、ソースコードをきれいに保てます。

基本的なコマンド
# ソースコードの静的解析
$ swiftlint

# ソースコードの自動修正
$ swiftlint autocorrect

# ソースコードの自動修正(自動インデント修正含む)
$ swiftlint autocorrect --format

ビルド毎に静的解析および自動修正を行うため、以下のようなRun Scriptを作成して自動で実行しています。swiftlint autocorrectのみでは自動修正のみで、静的解析が行われないことに注意が必要です。

RunScript
if which swiftlint >/dev/null; then
    swiftlint autocorrect --format
    swiftlint
else
    echo "SwiftLint does not exist, download from https://github.com/realm/SwiftLint"
fi

Run Scriptの設定イメージ
image.png

SwiftLintのバージョンアップへの追従

SwiftLintではデフォルトで設定されるルールと設定しないと有効にならないオプトインルール(Opt-In Rules)があります。

SwiftLintのバージョンアップにより、オプトインルールが追加されることがありますが、基本的には全て設定ファイルに追記するようにしています。

SwiftLintのルールは以下のswiftlint rulesコマンドでチェックできますが、オプトインルールのみを抽出したかったりします。(表内のopt-inがyesのもの)

$ swiftlint rules
+------------------------------------------+--------+-------------+------------------------+-------------+----------+-----------------------------------------------------------------------------+
| identifier                               | opt-in | correctable | enabled in your config | kind        | analyzer | configuration                                                               |
+------------------------------------------+--------+-------------+------------------------+-------------+----------+-----------------------------------------------------------------------------+
| anyobject_protocol                       | yes    | yes         | yes                    | lint        | no       | warning                                                                     |
| array_init                               | yes    | no          | yes                    | lint        | no       | warning                                                                     |
| attributes                               | yes    | no          | no                     | style       | no       | warning, always_on_same_line: ["@IBAction", "@NSManaged"], always_on_lin... |
| block_based_kvo                          | no     | no          | yes                    | idiomatic   | no       | warning                                                                     |
| class_delegate_protocol                  | no     | no          | yes                    | lint        | no       | warning                                                                     |
| closing_brace                            | no     | yes         | yes                    | style       | no       | warning                                                                     |
| closure_body_length                      | yes    | no          | no                     | metrics     | no       | warning: 20, error: 100                                                     |
| closure_end_indentation                  | yes    | yes         | no                     | style       | no       | warning                                                                     |
| closure_parameter_position               | no     | no          | yes                    | style       | no       | warning                                                                     |
〜略〜

自分はSwiftLintのオプトインルールのみを全て抽出し、そのまま設定ファイル(.swiftlint.yml)へ設定するために、以下のワンライナーコマンドを利用しています。

オプトインルールを全抽出するワンライナーコマンド

$ swiftlint rules | awk -F "|" '$3 ~ "yes" { print $2 }' | tr -d ' ' | sed 's/^/  - /' | pbcopy

コマンドの説明をすると、以下のようなことをしています。

  1. awkコマンドで|を区切り文字として表を分割し、opt-inがyesのもののみ抽出
  2. trコマンドで半角スペースを除去
  3. sedコマンドで行頭に箇条書き用の - を付与
  4. pbcopyコマンドでクリップボードへコピー

出力結果

コマンド入力後、クリップボードに出力結果が保存されますので、そのまま設定ファイル(.swiftlint.yml)へ記述しています。

  - array_init
  - attributes
  - closure_end_indentation
  - closure_spacing
  - conditional_returns_on_newline
  - contains_over_first_not_nil
  - discouraged_object_literal
〜略〜

設定ファイル(.swiftlint.yml)

設定ファイルでは、基本的に全てのオプトインルールを有効化した上で、無効にしたいもののみdisabled_rulesに欄に記載し、無効化しています。無効化する際は、備忘のため無効化した理由をコメントで記載しています。

.swiftlint.yml
# document: https://github.com/realm/SwiftLint

# 無効にするルール
disabled_rules:
- multiple_closures_with_trailing_closure # 複数のクロージャーの場合でも、trailing closureを利用したいため
- empty_enum_arguments # enumの引数を省略したいため

# opt-inルールの中で無効にするルール
- conditional_returns_on_newline # ガード文などは簡潔に一行で記述したいため
- discouraged_optional_collection # PHImageManagerの既存仕様のため
- explicit_enum_raw_value # 暗黙的なraw値で問題ないため
- explicit_type_interface # 型推論を利用したいため
- fatal_error_message # メッセージは不要なため
- file_header # ヘッダには特に決まりがないため
- lower_acl_than_parent # 対応不可のため
- no_extension_access_modifier # extension_access_modifierを優先するため
- no_grouping_extension # グルーピングにextensionを利用したいため
- strict_fileprivate # fileprivateを利用したいため
- switch_case_on_newline # caseと同じ行に記述したいため
- trailing_closure # RxSwiftのOnNextでwarningが出るため

# defaultルール以外にopt-inから採用するルール
opt_in_rules:
- anyobject_protocol
- array_init
- attributes
- closure_body_length
- closure_end_indentation
- closure_spacing
- collection_alignment
- conditional_returns_on_newline
- contains_over_first_not_nil
- convenience_type
- discouraged_object_literal
- discouraged_optional_boolean
- discouraged_optional_collection
- empty_count
- empty_string
- empty_xctest_method
- explicit_acl
- explicit_enum_raw_value
- explicit_init
- explicit_self
- explicit_top_level_acl
- explicit_type_interface
- extension_access_modifier
- fallthrough
- fatal_error_message
- file_header
- file_name
- first_where
- force_unwrapping
- function_default_parameter_at_end
- identical_operands
- implicit_return
- implicitly_unwrapped_optional
- joined_default_parameter
- last_where
- legacy_random
- let_var_whitespace
- literal_expression_end_indentation
- lower_acl_than_parent
- missing_docs
- modifier_order
- multiline_arguments
- multiline_arguments_brackets
- multiline_function_chains
- multiline_literal_brackets
- multiline_parameters
- multiline_parameters_brackets
- nimble_operator
- no_extension_access_modifier
- no_grouping_extension
- nslocalizedstring_key
- number_separator
- object_literal
- operator_usage_whitespace
- overridden_super_call
- override_in_extension
- pattern_matching_keywords
- prefixed_toplevel_constant
- private_action
- private_outlet
- prohibited_interface_builder
- prohibited_super_call
- quick_discouraged_call
- quick_discouraged_focused_test
- quick_discouraged_pending_test
- redundant_nil_coalescing
- redundant_type_annotation
- required_enum_case
- single_test_class
- sorted_first_last
- sorted_imports
- static_operator
- strict_fileprivate
- strong_iboutlet
- switch_case_on_newline
- toggle_bool
- trailing_closure
- unavailable_function
- unneeded_parentheses_in_closure_argument
- untyped_error_in_catch
- unused_import
- unused_private_declaration
- vertical_parameter_alignment_on_call
- vertical_whitespace_between_cases
- vertical_whitespace_closing_braces
- vertical_whitespace_opening_braces
- xct_specific_matcher
- yoda_condition

# Lint対象に追加するパス
included:
- MyLibrary

# Lint対象から除外するパス
excluded:
- Carthage
- Pods
- MyLibraryDemoTests

# 1行の文字列制限
line_length:
- 200   # warning
- 300   # error

# 型の行数制限
type_body_length:
- 400   # warning
- 600   # error

# 1ファイルの行数制限
file_length:
- 500   # warning
- 1000  # error

# メソッドの行数制限
function_body_length:
- 100   # warning
- 200   # error

type_name:
  min_length: 3
  max_length: 40

identifier_name:
  min_length: # only min_length
    error: 2 # only error
  excluded: # excluded via string array
    - id
    - URL
    - x
    - y
    - vc
    - on
  reporter: "xcode"

その他(局所的なルールの無効化)

その他、どうしても部分的に強制キャストを記述したい場合など、以下のようにコードの特定の行のみでSwiftLintを無効化するコメントを記載しています。

// swiftlint:disable:next force_cast
let noWarning = NSNumber() as! Int
let hasWarning = NSNumber() as! Int
let noWarning2 = NSNumber() as! Int // swiftlint:disable:this force_cast
let noWarning3 = NSNumber() as! Int
// swiftlint:disable:previous force_cast

参考

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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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