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

Swiftの静的解析ツール「SwiftLint」のセットアップ方法

「SwiftLint」とは?

Swift用の静的解析ツールです。
1行あたりの文字数やコロンの両側のスペースの有無など、非常に細かくチェックしてくれるのが特徴です。

環境

  • OS:macOS Mojave 10.14
  • Xcode:10.1 (10B61)
  • Swift:4.2.1
  • SwiftLint:0.30.1

セットアップ

インストール

Mint

2020/01/31現在、私はMintからインストールしています。
理由は、その他の方法よりバージョン管理が簡単なためです。

Mintfile
+ realm/SwiftLint@0.38.0
$ mint bootstrap

Homebrew

Homebrewからもインストールできます。

$ brew install swiftlint

ソースコードのビルド

ソースコードをビルドしてインストールすることもできます。

$ git clone https://github.com/realm/SwiftLint.git
$ cd SwiftLint/
$ git submodule update --init --recursive; make install

ビルド時に静的解析を行うようにする

Xcodeで対象のプロジェクトファイルを開く>TARGETS
→対象のターゲットを選択>[Build Phases]タブ
→左上にある[+]をクリック>New Run Script Phase
Run Scriptに以下のスクリプトを記述すると、ビルド時に自動で修正してから静的解析されるようになります。

if which mint >/dev/null; then
  mint run swiftlint swiftlint autocorrect --format
  mint run swiftlint swiftlint
else
  echo "warning: Mint not installed, download from https://github.com/yonaskolb/Mint"
fi

Mintを使っている
スクリーンショット 2020-04-07 10.54.15.png

Mintを使っていない
スクリーンショット 2019-02-11 22.41.44.png

Mintを使っていない場合、 mint run swiftlint を外し、if文の条件を変更する必要があります。

ルールの詳細設定

プロジェクトのルートディレクトリに .swiftlint.yml を作成し、その中にルールの詳細を記述します。
設定の書き方は公式ページに詳しく載っています。

参考になるかわかりませんが、私の設定ファイルも公開します。(随時更新)

.swiftlint.yml
# デフォルト有効で無効にするルール
disabled_rules:
  #- block_based_kvo
  #- class_delegate_protocol
  #- closing_brace
  #- closure_parameter_position
  #- colon
  #- comma
  #- compiler_protocol_init
  #- control_statement
  #- custom_rules
  #- cyclomatic_complexity
  #- deployment_target
  #- discarded_notification_center_observer
  #- discouraged_direct_init
  #- duplicate_imports
  #- dynamic_inline
  #- empty_enum_arguments
  #- empty_parameters
  #- empty_parentheses_with_trailing_closure
  #- file_length
  #- for_where
  #- force_cast
  #- force_try
  #- function_body_length
  #- function_parameter_count
  #- generic_type_name
  #- identifier_name
  #- implicit_getter
  #- inert_defer
  #- is_disjoint
  #- large_tuple
  #- leading_whitespace
  #- legacy_cggeometry_functions
  #- legacy_constant
  #- legacy_constructor
  #- legacy_hashing
  #- legacy_nsgeometry_functions
  #- line_length
  #- mark
  #- multiple_closures_with_trailing_closure
  #- nesting
  #- no_fallthrough_only
  #- notification_center_detachment
  #- opening_brace
  #- operator_whitespace
  #- private_over_fileprivate
  #- private_unit_test
  #- protocol_property_accessors_order
  #- redundant_discardable_let
  #- redundant_objc_attribute
  #- redundant_optional_initialization
  #- redundant_set_access_control
  #- redundant_string_enum_value
  #- redundant_void_return
  #- return_arrow_whitespace
  #- shorthand_operator
  #- statement_position
  #- superfluous_disable_command
  #- switch_case_alignment
  #- syntactic_sugar
  #- todo
  #- trailing_comma
  #- trailing_newline
  #- trailing_semicolon
  #- trailing_whitespace
  #- type_body_length
  #- type_name
  #- unneeded_break_in_switch
  #- unused_closure_parameter
  #- unused_control_flow_label
  #- unused_enumerated
  #- unused_optional_binding
  #- unused_setter_value
  #- valid_ibinspectable
  #- vertical_parameter_alignment
  #- vertical_whitespace
  #- void_return
  #- weak_computed_property
  #- weak_delegate
  #- xctfail_message

# デフォルト無効で有効にするルール
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 # できる限りACLを省略したいため
  #- explicit_enum_raw_value # ローバリューを省略することもあるため
  - explicit_init
  #- explicit_self # 関数は `self.` を付けずに呼び出したいため
  #- explicit_top_level_acl # できる限り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 # VIPERで変数を `!` で定義したいため
  - joined_default_parameter
  - last_where
  - legacy_random
  #- let_var_whitespace # 空白行を設けたくないこともあるため
  - literal_expression_end_indentation
  - lower_acl_than_parent
  - missing_docs
  - modifier_order
  #- multiline_arguments # 引数は同じ行に2つ入れたいこともあるため
  #- multiline_arguments_brackets # 括弧で行を増やしたくないため
  #- multiline_function_chains # 関数の呼び出しは同じ行に2つ入れたいこともあるため
  #- multiline_literal_brackets # 括弧で行を増やしたくないため
  #- multiline_parameters # 引数は同じ行に2つ入れたいこともあるため
  #- multiline_parameters_brackets # 括弧で行を増やしたくないため
  - nimble_operator
  #- no_extension_access_modifier # エクステンションにACLを設定したいことがあるため
  - no_grouping_extension # グループ化するためにエクステンションを使わないため
  - nslocalizedstring_key
  #- number_separator # 数字を `_` で区切りたくないため
  #- object_literal # リテラルで生成したくないこともあるため
  - operator_usage_whitespace
  - overridden_super_call
  - override_in_extension
  - pattern_matching_keywords
  #- prefixed_toplevel_constant # 定数のプリフィックスに `k` を付けたくないため
  - 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_deinit # できる限りデイニシャライザを省略したいため
  - required_enum_case
  - single_test_class
  - sorted_first_last
  #- sorted_imports # インポート文をアルファベット順以外に並び替えたいこともあるため
  - static_operator
  - strict_fileprivate
  #- strong_iboutlet # `@IBOutlet` を `weak` で定義したいこともあるため
  - 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 # Switch文のケース間に空白行を設けたくないこともあるため
  #- vertical_whitespace_closing_braces # 中括弧を閉じる前に空白行を設けたいことがあるため
  #- vertical_whitespace_opening_braces # 中括弧を開く前に空白行を設けたいことがあるため
  - xct_specific_matcher
  - yoda_condition

# 対象のファイル・フォルダ
# デフォルトからフォルダ名を変更していない場合、プロジェクト名と同名のフォルダを指定すればいい
included:
  - {プロジェクト名}

# 対象外のファイル・フォルダ
excluded:
  - Pods
  - Generated

line_length:
  warning: 300
  error: 500

identifier_name:
  min_length:
    warning: 1 # `r` `g` `b` などを使いたいため

R.swiftやMockoloなどで自動生成したファイルをまとめて1つのフォルダ(例:「Generated」フォルダ)に格納すると、フォルダを指定するだけで静的解析の対象外にできるのでオススメです。

静的解析

ここまで設定したら、ビルドするたびに静的解析が行われます。
めちゃくちゃたくさんの警告とエラーが出ると思うので、コードを修正するか、ルールを緩和するかしましょう!

注意

included: で指定したフォルダが存在しない場合、以下のビルドエラーが発生します。

Command /bin/sh failed with exit code 1

特定の箇所のみルールを無効化

// swiftlint:disable:{next|this|previous} {ルール名} コメントを使うことで、特定の箇所のみ特定のルールを無効化することができます。
使い過ぎるとルールを設定した意味がなくなるので、デバッグやテストでしか使わないコードのみに適用するのがいいと思います。

// 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

おまけ

全ルール一覧のまとめ

SwiftLintの全ルールを簡単に翻訳した記事を投稿しました。
SwiftLintの全ルール一覧(Swift 4.2版)

どのルールを採用するかの参考になれば幸いです。

SwiftLintを既存PJに導入する方法

Otemachi.swift #3という勉強会に「既存PJにSwiftLintを導入したら大変だった話:sweat_smile:」というタイトルで登壇しました。
そのときに使ったスライドをSpeaker Deckに上げているので、こちらもよかったら参考にしてください。
https://speakerdeck.com/uhooi/setup-swiftlint

スライドだけだとわかりづらいので、私のツイートで補ってください。。
https://twitter.com/the_uhooi/status/1117787831710040064
https://twitter.com/the_uhooi/status/1115979648851034112

参考リンク

uhooi
iOSアプリ開発とSwiftが好きです✨ 趣味:テニス、アナログゲーム
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
No 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
ユーザーは見つかりませんでした