概要
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
のみでは自動修正のみで、静的解析が行われないことに注意が必要です。
if which swiftlint >/dev/null; then
swiftlint autocorrect --format
swiftlint
else
echo "SwiftLint does not exist, download from https://github.com/realm/SwiftLint"
fi
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
コマンドの説明をすると、以下のようなことをしています。
- awkコマンドで|を区切り文字として表を分割し、opt-inがyesのもののみ抽出
- trコマンドで半角スペースを除去
- sedコマンドで行頭に箇条書き用の - を付与
- 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に欄に記載し、無効化しています。無効化する際は、備忘のため無効化した理由をコメントで記載しています。
# 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