Keeping Swift style consistent with SwiftLint

foreword

Coding style can be a controversial topic and sparks some heated discussions among developers. Using a tool to enforce a set of code style rules can be very helpful in avoiding some of the debate and ensuring code style consistency is maintained throughout a project. SwiftLint can be easily integrated into Xcode projects to flag code style conflicts as warnings or errors at compile time.


Integrate SwiftLint with Xcode

You can get SwiftLint on Github . It can be installed in a variety of ways, for example, directly downloading the SwiftLint.pkg package, or using the HomeBrew command line.

brew install swiftlint

Once SwiftLint is installed, it can be integrated into an Xcode project by adding a (run phase) run script Build Phaseunder the (build phase) main app target. Run PhaseClick +the button, select "New Run Script Phase", and add the following script. The statement needs to be added on silicon Macs (with M1 chips) export, because HomeBrewthe binaries for /opt/homebrew/bin.

Translator's Note: Not necessarily in this directory, you can click here for details .

export PATH="$PATH:/opt/homebrew/bin"
if which swiftlint > /dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi

Add run scripts in Xcode to integrate SwiftLint

Add run scripts in Xcode to integrate SwiftLint


SwiftLint's rule violations

The good news is that newly created Xcode projects do not violate SwiftLint's default rules. Once you know SwiftLint, it's best to add it to every project right from the start. Text viewAdd a space after the newly created iOS App . A warning is now generated when compiling the code.

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!") 

            Text("second line")

            Spacer()
        }
    }
}

This code violates the trailing_whitespacerules, it is on by default.

+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
| identifier                               | opt-in | correctable | enabled in your config | kind        | analyzer | configuration |
+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
| trailing_whitespace                      | no     | yes         | yes                    | style       | no       | warning, i... |

Swift is warning about an extra space after a line

Swift 正在警告在一行之后有一个额外的空格


SwiftLint 的规则

SwiftLint 包含了200多条规则,并且 Swift 社区仍在不间断的贡献更多的规则。查看 SwiftLint 规则的一种方法是在终端中运行swiftlint rules命令(此种方式需要安装swiftlint)。这将会显示规则以及规则的一系列属性,比如是否可选,是否可纠正。

以下是 SwiftLint 0.46.5的默认规则:

swiftlint rules
+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
| identifier                               | opt-in | correctable | enabled in your config | kind        | analyzer | configuration |
+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
| anonymous_argument_in_multiline_closure  | yes    | no          | no                     | idiomatic   | no       | warning       |
| anyobject_protocol                       | yes    | yes         | no                     | lint        | no       | warning       |
| array_init                               | yes    | no          | no                     | lint        | no       | warning       |
| attributes                               | yes    | no          | no                     | style       | no       | warning, a... |
| balanced_xctest_lifecycle                | yes    | no          | no                     | lint        | no       | warning       |
| block_based_kvo                          | no     | no          | yes                    | idiomatic   | no       | warning       |
| capture_variable                         | yes    | no          | no                     | lint        | yes      | 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: 2... |
| closure_end_indentation                  | yes    | yes         | no                     | style       | no       | warning       |
| closure_parameter_position               | no     | no          | yes                    | style       | no       | warning       |
| closure_spacing                          | yes    | yes         | no                     | style       | no       | warning       |
| collection_alignment                     | yes    | no          | no                     | style       | no       | warning, a... |
| colon                                    | no     | yes         | yes                    | style       | no       | warning, f... |
| comma                                    | no     | yes         | yes                    | style       | no       | warning       |
| comment_spacing                          | no     | yes         | yes                    | lint        | no       | warning       |
| compiler_protocol_init                   | no     | no          | yes                    | lint        | no       | warning       |
| computed_accessors_order                 | no     | no          | yes                    | style       | no       | warning, o... |
| conditional_returns_on_newline           | yes    | no          | no                     | style       | no       | warning, i... |
| contains_over_filter_count               | yes    | no          | no                     | performance | no       | warning       |
| contains_over_filter_is_empty            | yes    | no          | no                     | performance | no       | warning       |
| contains_over_first_not_nil              | yes    | no          | no                     | performance | no       | warning       |
| contains_over_range_nil_comparison       | yes    | no          | no                     | performance | no       | warning       |
| control_statement                        | no     | yes         | yes                    | style       | no       | warning       |
| convenience_type                         | yes    | no          | no                     | idiomatic   | no       | warning       |
| custom_rules                             | no     | no          | no                     | style       | no       | user-defin... |
| cyclomatic_complexity                    | no     | no          | yes                    | metrics     | no       | warning: 1... |
| deployment_target                        | no     | no          | yes                    | lint        | no       | warning, i... |
| discarded_notification_center_observer   | yes    | no          | no                     | lint        | no       | warning       |
| discouraged_assert                       | yes    | no          | no                     | idiomatic   | no       | warning       |
| discouraged_direct_init                  | no     | no          | yes                    | lint        | no       | warning, t... |
| discouraged_none_name                    | yes    | no          | no                     | idiomatic   | no       | warning       |
| discouraged_object_literal               | yes    | no          | no                     | idiomatic   | no       | warning, i... |
| discouraged_optional_boolean             | yes    | no          | no                     | idiomatic   | no       | warning       |
| discouraged_optional_collection          | yes    | no          | no                     | idiomatic   | no       | warning       |
| duplicate_enum_cases                     | no     | no          | yes                    | lint        | no       | error         |
| duplicate_imports                        | no     | no          | yes                    | idiomatic   | no       | warning       |
| duplicated_key_in_dictionary_literal     | no     | no          | yes                    | lint        | no       | warning       |
| dynamic_inline                           | no     | no          | yes                    | lint        | no       | error         |
| empty_collection_literal                 | yes    | no          | no                     | performance | no       | warning       |
| empty_count                              | yes    | no          | no                     | performance | no       | error, onl... |
| empty_enum_arguments                     | no     | yes         | yes                    | style       | no       | warning       |
| empty_parameters                         | no     | yes         | yes                    | style       | no       | warning       |
| empty_parentheses_with_trailing_closure  | no     | yes         | yes                    | style       | no       | warning       |
| empty_string                             | yes    | no          | no                     | performance | no       | warning       |
| empty_xctest_method                      | yes    | no          | no                     | lint        | no       | warning       |
| enum_case_associated_values_count        | yes    | no          | no                     | metrics     | no       | warning: 5... |
| expiring_todo                            | yes    | no          | no                     | lint        | no       | (approachi... |
| explicit_acl                             | yes    | no          | no                     | idiomatic   | no       | warning       |
| explicit_enum_raw_value                  | yes    | no          | no                     | idiomatic   | no       | warning       |
| explicit_init                            | yes    | yes         | no                     | idiomatic   | no       | warning       |
| explicit_self                            | yes    | yes         | no                     | style       | yes      | warning       |
| explicit_top_level_acl                   | yes    | no          | no                     | idiomatic   | no       | warning       |
| explicit_type_interface                  | yes    | no          | no                     | idiomatic   | no       | warning, e... |
| extension_access_modifier                | yes    | no          | no                     | idiomatic   | no       | warning       |
| fallthrough                              | yes    | no          | no                     | idiomatic   | no       | warning       |
| fatal_error_message                      | yes    | no          | no                     | idiomatic   | no       | warning       |
| file_header                              | yes    | no          | no                     | style       | no       | warning, r... |
| file_length                              | no     | no          | yes                    | metrics     | no       | warning: 4... |
| file_name                                | yes    | no          | no                     | idiomatic   | no       | (severity)... |
| file_name_no_space                       | yes    | no          | no                     | idiomatic   | no       | (severity)... |
| file_types_order                         | yes    | no          | no                     | style       | no       | warning, o... |
| first_where                              | yes    | no          | no                     | performance | no       | warning       |
| flatmap_over_map_reduce                  | yes    | no          | no                     | performance | no       | warning       |
| for_where                                | no     | no          | yes                    | idiomatic   | no       | warning       |
| force_cast                               | no     | no          | yes                    | idiomatic   | no       | error         |
| force_try                                | no     | no          | yes                    | idiomatic   | no       | error         |
| force_unwrapping                         | yes    | no          | no                     | idiomatic   | no       | warning       |
| function_body_length                     | no     | no          | yes                    | metrics     | no       | warning: 4... |
| function_default_parameter_at_end        | yes    | no          | no                     | idiomatic   | no       | warning       |
| function_parameter_count                 | no     | no          | yes                    | metrics     | no       | warning: 5... |
| generic_type_name                        | no     | no          | yes                    | idiomatic   | no       | (min_lengt... |
| ibinspectable_in_extension               | yes    | no          | no                     | lint        | no       | warning       |
| identical_operands                       | yes    | no          | no                     | lint        | no       | warning       |
| identifier_name                          | no     | no          | yes                    | style       | no       | (min_lengt... |
| implicit_getter                          | no     | no          | yes                    | style       | no       | warning       |
| implicit_return                          | yes    | yes         | no                     | style       | no       | warning, i... |
| implicitly_unwrapped_optional            | yes    | no          | no                     | idiomatic   | no       | warning, m... |
| inclusive_language                       | no     | no          | yes                    | style       | no       | warning, a... |
| indentation_width                        | yes    | no          | no                     | style       | no       | severity: ... |
| inert_defer                              | no     | no          | yes                    | lint        | no       | warning       |
| is_disjoint                              | no     | no          | yes                    | idiomatic   | no       | warning       |
| joined_default_parameter                 | yes    | yes         | no                     | idiomatic   | no       | warning       |
| large_tuple                              | no     | no          | yes                    | metrics     | no       | warning: 2... |
| last_where                               | yes    | no          | no                     | performance | no       | warning       |
| leading_whitespace                       | no     | yes         | yes                    | style       | no       | warning       |
| legacy_cggeometry_functions              | no     | yes         | yes                    | idiomatic   | no       | warning       |
| legacy_constant                          | no     | yes         | yes                    | idiomatic   | no       | warning       |
| legacy_constructor                       | no     | yes         | yes                    | idiomatic   | no       | warning       |
| legacy_hashing                           | no     | no          | yes                    | idiomatic   | no       | warning       |
| legacy_multiple                          | yes    | no          | no                     | idiomatic   | no       | warning       |
| legacy_nsgeometry_functions              | no     | yes         | yes                    | idiomatic   | no       | warning       |
| legacy_objc_type                         | yes    | no          | no                     | idiomatic   | no       | warning       |
| legacy_random                            | yes    | no          | no                     | idiomatic   | no       | warning       |
| let_var_whitespace                       | yes    | no          | no                     | style       | no       | warning       |
| line_length                              | no     | no          | yes                    | metrics     | no       | warning: 1... |
| literal_expression_end_indentation       | yes    | yes         | no                     | style       | no       | warning       |
| lower_acl_than_parent                    | yes    | no          | no                     | lint        | no       | warning       |
| mark                                     | no     | yes         | yes                    | lint        | no       | warning       |
| missing_docs                             | yes    | no          | no                     | lint        | no       | warning: o... |
| modifier_order                           | yes    | yes         | no                     | style       | no       | warning, p... |
| multiline_arguments                      | yes    | no          | no                     | style       | no       | warning, f... |
| multiline_arguments_brackets             | yes    | no          | no                     | style       | no       | warning       |
| multiline_function_chains                | yes    | no          | no                     | style       | no       | warning       |
| multiline_literal_brackets               | yes    | no          | no                     | style       | no       | warning       |
| multiline_parameters                     | yes    | no          | no                     | style       | no       | warning, a... |
| multiline_parameters_brackets            | yes    | no          | no                     | style       | no       | warning       |
| multiple_closures_with_trailing_closure  | no     | no          | yes                    | style       | no       | warning       |
| nesting                                  | no     | no          | yes                    | metrics     | no       | (type_leve... |
| nimble_operator                          | yes    | yes         | no                     | idiomatic   | no       | warning       |
| no_extension_access_modifier             | yes    | no          | no                     | idiomatic   | no       | error         |
| no_fallthrough_only                      | no     | no          | yes                    | idiomatic   | no       | warning       |
| no_grouping_extension                    | yes    | no          | no                     | idiomatic   | no       | warning       |
| no_space_in_method_call                  | no     | yes         | yes                    | style       | no       | warning       |
| notification_center_detachment           | no     | no          | yes                    | lint        | no       | warning       |
| nslocalizedstring_key                    | yes    | no          | no                     | lint        | no       | warning       |
| nslocalizedstring_require_bundle         | yes    | no          | no                     | lint        | no       | warning       |
| nsobject_prefer_isequal                  | no     | no          | yes                    | lint        | no       | warning       |
| number_separator                         | yes    | yes         | no                     | style       | no       | warning, m... |
| object_literal                           | yes    | no          | no                     | idiomatic   | no       | warning, i... |
| opening_brace                            | no     | yes         | yes                    | style       | no       | warning, a... |
| operator_usage_whitespace                | yes    | yes         | no                     | style       | no       | warning, l... |
| operator_whitespace                      | no     | no          | yes                    | style       | no       | warning       |
| optional_enum_case_matching              | yes    | yes         | no                     | style       | no       | warning       |
| orphaned_doc_comment                     | no     | no          | yes                    | lint        | no       | warning       |
| overridden_super_call                    | yes    | no          | no                     | lint        | no       | warning, e... |
| override_in_extension                    | yes    | no          | no                     | lint        | no       | warning       |
| pattern_matching_keywords                | yes    | no          | no                     | idiomatic   | no       | warning       |
| prefer_nimble                            | yes    | no          | no                     | idiomatic   | no       | warning       |
| prefer_self_in_static_references         | yes    | yes         | no                     | style       | no       | N/A           |
| prefer_self_type_over_type_of_self       | yes    | yes         | no                     | style       | no       | warning       |
| prefer_zero_over_explicit_init           | yes    | yes         | no                     | idiomatic   | no       | warning       |
| prefixed_toplevel_constant               | yes    | no          | no                     | style       | no       | warning, o... |
| private_action                           | yes    | no          | no                     | lint        | no       | warning       |
| private_outlet                           | yes    | no          | no                     | lint        | no       | warning, a... |
| private_over_fileprivate                 | no     | yes         | yes                    | idiomatic   | no       | warning, v... |
| private_subject                          | yes    | no          | no                     | lint        | no       | warning       |
| private_unit_test                        | no     | no          | yes                    | lint        | no       | warning: X... |
| prohibited_interface_builder             | yes    | no          | no                     | lint        | no       | warning       |
| prohibited_super_call                    | yes    | no          | no                     | lint        | no       | warning, e... |
| protocol_property_accessors_order        | no     | yes         | yes                    | style       | no       | warning       |
| quick_discouraged_call                   | yes    | no          | no                     | lint        | no       | warning       |
| quick_discouraged_focused_test           | yes    | no          | no                     | lint        | no       | warning       |
| quick_discouraged_pending_test           | yes    | no          | no                     | lint        | no       | warning       |
| raw_value_for_camel_cased_codable_enum   | yes    | no          | no                     | lint        | no       | warning       |
| reduce_boolean                           | no     | no          | yes                    | performance | no       | warning       |
| reduce_into                              | yes    | no          | no                     | performance | no       | warning       |
| redundant_discardable_let                | no     | yes         | yes                    | style       | no       | warning       |
| redundant_nil_coalescing                 | yes    | yes         | no                     | idiomatic   | no       | warning       |
| redundant_objc_attribute                 | no     | yes         | yes                    | idiomatic   | no       | warning       |
| redundant_optional_initialization        | no     | yes         | yes                    | idiomatic   | no       | warning       |
| redundant_set_access_control             | no     | no          | yes                    | idiomatic   | no       | warning       |
| redundant_string_enum_value              | no     | no          | yes                    | idiomatic   | no       | warning       |
| redundant_type_annotation                | yes    | yes         | no                     | idiomatic   | no       | warning       |
| redundant_void_return                    | no     | yes         | yes                    | idiomatic   | no       | warning       |
| required_deinit                          | yes    | no          | no                     | lint        | no       | warning       |
| required_enum_case                       | yes    | no          | no                     | lint        | no       | No protoco... |
| return_arrow_whitespace                  | no     | yes         | yes                    | style       | no       | warning       |
| self_in_property_initialization          | no     | no          | yes                    | lint        | no       | warning       |
| shorthand_operator                       | no     | no          | yes                    | style       | no       | error         |
| single_test_class                        | yes    | no          | no                     | style       | no       | warning       |
| sorted_first_last                        | yes    | no          | no                     | performance | no       | warning       |
| sorted_imports                           | yes    | yes         | no                     | style       | no       | warning       |
| statement_position                       | no     | yes         | yes                    | style       | no       | (statement... |
| static_operator                          | yes    | no          | no                     | idiomatic   | no       | warning       |
| strict_fileprivate                       | yes    | no          | no                     | idiomatic   | no       | warning       |
| strong_iboutlet                          | yes    | yes         | no                     | lint        | no       | warning       |
| superfluous_disable_command              | no     | no          | yes                    | lint        | no       | warning       |
| switch_case_alignment                    | no     | no          | yes                    | style       | no       | warning, i... |
| switch_case_on_newline                   | yes    | no          | no                     | style       | no       | warning       |
| syntactic_sugar                          | no     | yes         | yes                    | idiomatic   | no       | warning       |
| test_case_accessibility                  | yes    | yes         | no                     | lint        | no       | warning, a... |
| todo                                     | no     | no          | yes                    | lint        | no       | warning       |
| toggle_bool                              | yes    | yes         | no                     | idiomatic   | no       | warning       |
| trailing_closure                         | yes    | no          | no                     | style       | no       | warning, o... |
| trailing_comma                           | no     | yes         | yes                    | style       | no       | warning, m... |
| trailing_newline                         | no     | yes         | yes                    | style       | no       | warning       |
| trailing_semicolon                       | no     | yes         | yes                    | idiomatic   | no       | warning       |
| trailing_whitespace                      | no     | yes         | yes                    | style       | no       | warning, i... |
| type_body_length                         | no     | no          | yes                    | metrics     | no       | warning: 2... |
| type_contents_order                      | yes    | no          | no                     | style       | no       | warning, o... |
| type_name                                | no     | no          | yes                    | idiomatic   | no       | (min_lengt... |
| unavailable_function                     | yes    | no          | no                     | idiomatic   | no       | warning       |
| unneeded_break_in_switch                 | no     | no          | yes                    | idiomatic   | no       | warning       |
| unneeded_parentheses_in_closure_argument | yes    | yes         | no                     | style       | no       | warning       |
| unowned_variable_capture                 | yes    | no          | no                     | lint        | no       | warning       |
| untyped_error_in_catch                   | yes    | yes         | no                     | idiomatic   | no       | warning       |
| unused_capture_list                      | no     | no          | yes                    | lint        | no       | warning       |
| unused_closure_parameter                 | no     | yes         | yes                    | lint        | no       | warning       |
| unused_control_flow_label                | no     | yes         | yes                    | lint        | no       | warning       |
| unused_declaration                       | yes    | no          | no                     | lint        | yes      | severity: ... |
| unused_enumerated                        | no     | no          | yes                    | idiomatic   | no       | warning       |
| unused_import                            | yes    | yes         | no                     | lint        | yes      | severity: ... |
| unused_optional_binding                  | no     | no          | yes                    | style       | no       | warning, i... |
| unused_setter_value                      | no     | no          | yes                    | lint        | no       | warning       |
| valid_ibinspectable                      | no     | no          | yes                    | lint        | no       | warning       |
| vertical_parameter_alignment             | no     | no          | yes                    | style       | no       | warning       |
| vertical_parameter_alignment_on_call     | yes    | no          | no                     | style       | no       | warning       |
| vertical_whitespace                      | no     | yes         | yes                    | style       | no       | warning, m... |
| vertical_whitespace_between_cases        | yes    | yes         | no                     | style       | no       | warning       |
| vertical_whitespace_closing_braces       | yes    | yes         | no                     | style       | no       | N/A           |
| vertical_whitespace_opening_braces       | yes    | yes         | no                     | style       | no       | N/A           |
| void_return                              | no     | yes         | yes                    | style       | no       | warning       |
| weak_delegate                            | yes    | yes         | no                     | lint        | no       | warning       |
| xct_specific_matcher                     | yes    | no          | no                     | idiomatic   | no       | warning       |
| xctfail_message                          | no     | no          | yes                    | idiomatic   | no       | warning       |
| yoda_condition                           | yes    | no          | no                     | lint        | no       | warning       |
+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+

在终端运行 SwiftLint

SwiftLint 可以配置为一个仓库预提交的钩子,用以保证提交代码的风格一致。它也可以在终端中作为命令运行,只需在项目目录中运行swiftlint即可。运行swiftlint --help查看更多选项。

swiftlint --help
OVERVIEW: A tool to enforce Swift style and conventions.

USAGE: swiftlint <subcommand>

OPTIONS:
  --version               Show the version.
  -h, --help              Show help information.

SUBCOMMANDS:
  analyze                 Run analysis rules
  docs                    Open SwiftLint documentation website in the default web browser
  generate-docs           Generates markdown documentation for all rules
  lint (default)          Print lint warnings and errors
  rules                   Display the list of rules and their identifiers
  version                 Display the current version of SwiftLint

  See 'swiftlint help <subcommand>' for detailed help.

在(之前新建的项目)HelloSwiftLintApp(目录下)在终端运行swiftlint同样会显示违反了trailing_whitespace规则。

swiftlint
Linting Swift files in current working directory
Linting 'HelloSwiftLintApp.swift' (1/2)
Linting 'ContentView.swift' (2/2)
...HelloSwiftLint/ContentView.swift:13:1: warning: Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
Done linting! Found 1 violation, 0 serious in 2 files.

自动修复 SwiftLint 冲突

从上面的规则列表可以看出,有一些规则是可以自动修正的。这是直接针对间距规则的,就像上面介绍的额外空格一样。只要可以进行 SwiftLint 分析,就可以进行 SwiftLint 自动修正。

在终端运行swiftlint --fix就会自动修正那些可以被自动修正的 SwiftLint 冲突。

swiftlint --fix
Correcting Swift files in current working directory
Correcting 'HelloSwiftLintApp.swift' (1/2)
Correcting 'ContentView.swift' (2/2)
.../HelloSwiftLint/ContentView.swift:13:1 Corrected Trailing Whitespace
Done inspecting 2 files for auto-correction!

或者,可以将自动修复整合到 Xcode 的Build Phase。编辑"Run Script Phase"下的 SwiftLint 脚本。现在,在 Xcode 中编译代码时,添加尾随空格会自动删除。

export PATH="$PATH:/opt/homebrew/bin"
if which swiftlint > /dev/null; then
  swiftlint --fix && swiftlint
else
  echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi


手动修复 SwiftLint 规则冲突

并非所有的规则冲突都可以自动修复。对于 SwiftLint 分析生成的警告以及错误,有很多种处理方式。如果只有一到两个冲突,最好的办法是修复它们,然后继续。

处理 SwiftLint 冲突的一些选项:

1. 修改代码以符合 SwiftLint 规则
2. 在代码中添加特例,以忽略特定的规则冲突
3. 为项目定制 SwiftLint 规则
4. 忽略这些警告 -- 这不是一个好的选项

修复冲突是最好的方法,当 SwiftLint 从项目的一开始就被整合时,这可以很容易的被实现。

下面是我写的示例代码,它遍历了 vertices 数组,创建了一条路径。使用enumerated方法生成了索引以及数据项,使用单个字符n作为变量名会导致编译时错误,仅使用字符作为变量名会导致编译时警告。

for (n, pt) in vertices.enumerated() {
     n == 0 ? path.move(to: pt) : path.addLine(to: pt)
}

这些“**标识符名称”(identifier-name)**冲突不能被自动修复。为此类冲突创建一些例外可能会很有诱惑力,但是从长远来看,(此类规则)将有助于代码的可读性以及可维护性。

for (index, point) in vertices.enumerated() {
     index == 0 ? path.move(to: point) : path.addLine(to: point)
}

我发现关于enumerated苹果的示例代码存在着同样的问题!

SwiftLint的标识符冲突无法被修复

SwiftLint 的标识符冲突无法被修复


一些规则的例外情况

在某些情况下,代码需要与某些外部API或数据源兼容。Open Weather API提供了如Read JSON with codeable in Swift中所描述的 JSON 数据。这些天气数据中main数据段含有一些有下划线的标识符,比如,feels_like 。用于 Swift 解码此 JSON 的结构体必须与 JSON 中的字段名称匹配,由于 SwiftLint 的”***identifier_name***”规则,Swift 代码会产生编译时错误。

天气数据的示例 JSON

{
    "coord": {
        "lon": -122.08,
        "lat": 37.39
    },
    "weather": [
        {
            "id": 800,
            "main": "Clear",
            "description": "clear sky",
            "icon": "01d"
        }
    ],
    "base": "stations",
    "main": {
        "temp": 9.4,
        "feels_like": 8.71,
        "temp_min": 7.22,
        "temp_max": 11.11,
        "pressure": 1023,
        "humidity": 100,
        "sea_level": 100
    },
    "visibility": 16093,
    "wind": {
        "speed": 1.5,
        "deg": 350
    },
    "clouds": {
        "all": 1
    },
    "dt": 1560350645,
    "sys": {
        "type": 1,
        "id": 5122,
        "message": 0.0139,
        "country": "US",
        "sunrise": 1560343627,
        "sunset": 1560396563
    },
    "timezone": -25200,
    "id": 420006353,
    "name": "Mountain View",
    "cod": 200
}
struct WeatherRawData: Codable {
    var name: String
    var timezone: Double
    var weather: [WeatherData]
    var main: MainData
    var wind: WindData
    var clouds: CloudsData

    struct WeatherData: Codable {
        var id: Double
        var main: String
        var description: String
        var icon: String
    }

    struct MainData: Codable {
        var temp: Double
        var feels_like: Double
        var temp_min: Double
        var temp_max: Double
        var pressure: Double
        var humidity: Double
    }

    struct WindData: Codable {
        var speed: Double
        var deg: Double
    }

    struct CloudsData: Codable {
        var all: Double
    }
}

不是每个(冲突)都需要被抛出。在这种情况下,可以在出现问题的代码之前简单地禁用 SwiftLint 规则,然后重新启用该规则。显然,如果这些启用/禁用代码片段在代码中到处都是,那就不太好了。这种技术应该谨慎地被使用。如果发现需要在多个位置禁用同一规则,请考虑为整个项目禁用该规则。

struct WeatherRawData: Codable {
    var name: String
    var timezone: Double
    var weather: [WeatherData]
    var main: MainData
    var wind: WindData
    var clouds: CloudsData

    struct WeatherData: Codable {
        var id: Double
        var main: String
        var description: String
        var icon: String
    }

    // swiftlint:disable identifier_name
    struct MainData: Codable {
        var temp: Double
        var feels_like: Double
        var temp_min: Double
        var temp_max: Double
        var pressure: Double
        var humidity: Double
    }
    // swiftlint:enable identifier_name

    struct WindData: Codable {
        var speed: Double
        var deg: Double
    }

    struct CloudsData: Codable {
        var all: Double
    }
}


不要急于禁用规则

偶尔会有一些 SwiftLint 规则的特例,但是不要急于禁用规则。在上面的例子中,有一种更好的方法,可以使用CodingKeys将 Swift 变量名映射到 JSON 内容。与其注释 SwiftLint 规则,不如使用属性名feelsLike并指定feels_like的可选值来匹配JSON数据。

// swiftlint:disable identifier_name
    struct MainData: Codable {
        var temp: Double
        var feels_like: Double
        var temp_min: Double
        var temp_max: Double
        var pressure: Double
        var humidity: Double
    }
    // swiftlint:enable identifier_name
struct MainData: Codable {
    let temp: Double
    let feelsLike: Double
    let tempMin: Double
    let tempMax: Double
    let pressure: Double
    let humidity: Double

    enum CodingKeys: String, CodingKey {
        case temp
        case feelsLike = "feels_like"
        case tempMin = "temp_min"
        case tempMax = "temp_max"
        case pressure
        case humidity
    }
}

使用CodingKeys来映射JSON变量好于禁用SwiftLint规则

使用 CodingKeys 来映射 JSON 变量好于禁用 SwiftLint 规则


自定义 SwiftLint 规则

如果将 SwiftLint 添加到显示数百个问题的现有项目中,“修复所有冲突”的方法可能非常困难。在这种情况下,将 SwiftLint 配置添加到项目中可能更合适。这是一个YAML文件,在该文件中可以禁用规则,列出选择开启的规则,或者将规则仅限于此文件中的规则。这样, SwiftLint 就可以无限定制。有关更多详细信息,请参阅SwiftLint配置部分。

警告的一个例子是代码中存在 TODO 注释。SwiftLint 将这些 TODO 标记为警告,以表示这些地方还有未完成的工作。

TODO注释被SwiftLint默认编译成一个警告

TODO 注释被 SwiftLint 默认编译成一个警告

很多时候你既想合并代码时保留这些 TODO,也希望在编译时没有这些警告。可以在每个单独的TODO注释前面加disable/enable,也可以在.swiftlint.yml文件中来禁用整个整个项目的此规则。将下方的.swiftlint.yml文件添加到项目中,会允许项目编译而不生成 TODO 注释警告,其他规则不受影响。

disabled_rules: # rule identifiers to exclude from running
  - todo
struct ContentView: View {

    var body: some View {
        VStack {
            Text("Hello, world!")

            // TODO: Remove this function when done

            let a = 24
            Text("second line \(a)")

            Spacer()
        }
    }
}

TODO注释没有造成警告,其他规则不受影响

TODO 注释没有造成警告,其他规则不受影响

在已有的规则上使用 SwiftLint 最简单的方法是:

  1. 安装 SwiftLint
  2. 通过编译阶段脚本,将 SwiftLint 整合进 Xcode 项目中
  3. 编译以评估所有警告和错误
  4. 添加.swiftlint.yml文件,并禁用冲突数最多的规则
  5. 一次启用一条规则并修复代码中的问题

结论

对于任何 Swift 开发者来说,使用 SwiftLint 都是必要的。它有助于避免团队中关于代码样式的争论,以及建立代码风格的统一性。就我而言,它帮我摆脱了诸如创建单字符标识符等坏习惯。

将 SwiftLint 添加到已有的代码库可能比添加到新项目要复杂得多,因为它可能会显示数百个警告和错误。通过配置规则,并逐渐开启更多的规则,可以在现有项目中采用 SwiftLint。

SwiftLint 的自动修复冲突的能力非常强大,通过自动修复冲突可以显著解决数百个冲突的初始情况。只需要确保代码在进行大范围的自动更改之前已经纳入了版本控制,这样在出问题时就能很容易撤销。


译者的一些补充

关于 SwiftLint 的安装

安装的方式有几种,原文介绍的是使用 homebrew 安装。译者比较推荐直接使用**CocoaPods:**

pod 'SwiftLint', '0.46.5'

能清晰明了的指明项目使用了 SwiftLint ,同时也方便指定版本。

SwiftLint 不仅仅能帮助解决格式问题

SwiftLint 不仅仅能解决很多格式问题,它的功能还有很多。比如限制一个函数参数的个数,函数、文件最长多少行,使用更精简,更Swift 的函数等等。这能在很大程度上帮助我们写出高质量的代码。

很多团队伙伴在写代码时,一开始的函数,文件可能没那么臃肿。但是随着功能的增加,不断地往一个函数添加参数,不断修改函数的功能,不断往一个文件增加新的函数等等,各个地方开始变得臃肿。当 SwiftLint 告诉你函数参数个数超过了指定的个数,函数行数超过了最大值,文件超过了最大行数等等时,就应该认真考虑是不是该重构了。

在已有项目中添加 SwiftLint

Before adding SwiftLint, the most important thing is that everyone should have a brief meeting. Used to synchronize and determine rules. Let everyone fully express their opinions, not one person formulate, some people disagree. Once some ambiguous rules are identified, they are written into the SwiftLint configuration, and everyone should abide by them.

The translator's project is very large. When SwiftLint was first added, there were more than 5,000 warnings and errors, which could not be changed at all. The approach I take is to configure SwiftLint by path. Those older, less mobile directories have a more relaxed configuration. Create a new directory, in principle, all new codes should be under this directory. The configuration of this directory will be stricter.

Hook git pre-commitchecks the rules before submitting. If there is a problem, it will report an error directly and the code cannot be submitted. In this way, no new warnings and errors will be generated, and the previous code will be slowly modified. After more than a year, the entire project is basically covered.

In addition, the warnings about the project also need to be managed. In many cases, some members ignore the warnings when writing code, resulting in hundreds of project warnings, and more and more. This causes the warning to lose its original function. The warning of the translator project is 0, and it is easy to locate where a new warning is issued. At the same time, code review is also very important. In order to avoid trouble, some members directly disable the rules of SwiftLint in various places, which should not be done.

Finally, always remember that you are only using a tool, not an end. Please fully weigh the costs and benefits.

I am participating in the recruitment of the creator signing program of the Nuggets Technology Community, click the link to register and submit .

Guess you like

Origin juejin.im/post/7118946425202802701