前言
代码风格可能是一个有争议的话题,并且在开发人员之间引发一些激烈的讨论。使用工具强制执行一套代码风格规则对于避免一些争论,以及确保在整个项目中保持代码风格的一致性非常有帮助。SwiftLint 可以很容易的整合进 Xcode 项目中,以便在编译时将代码风格冲突标记为警告或者错误。
使用 Xcode 集成 SwiftLint
你可以在 Github上 获得SwiftLint。它可以使用多种方式安装,比如,直接下载 SwiftLint.pkg 包,或者使用HomeBrew命令行。安装 SwiftLint 后,可以通过在主 app target 的Build Phase(构筑阶段)下添加一个Run Phase(运行阶段)的运行脚本,来集成进 Xcode 项目。点击+号按钮,选择"New Run Script Phase",添加下面的脚本。在 silicon Macs (搭载M1芯片)上需要添加export语句,因为HomeBrew的二进制文件默认安装在/opt/homebrew/bin目录之下。- 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
复制代码
在Xcode添加运行脚本以集成SwiftLint
在 Xcode 添加运行脚本以集成 SwiftLint
<hr/>SwiftLint 的规则冲突
好消息是,新建的 Xcode 项目没有违反 SwiftLint 的默认规则。一旦你知道了 SwiftLint,最好从一开始就立即将其添加到每个项目中。在刚才新建的 iOS App 的Text view后面添加一个空格。现在,编译代码时会生成警告。- struct ContentView: View {
- var body: some View {
- VStack {
- Text(&#34;Hello, world!&#34;)
- Text(&#34;second line&#34;)
- Spacer()
- }
- }
- }
复制代码 这段代码违反了trailing_whitespace规则,它默认是开启的。- +------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
- | identifier | opt-in | correctable | enabled in your config | kind | analyzer | configuration |
- +------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
- | trailing_whitespace | no | yes | yes | style | no | warning, i... |
复制代码
Swift正在警告在一行之后有一个额外的空格
Swift 正在警告在一行之后有一个额外的空格
<hr/>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 |
- +------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
复制代码 <hr/>在终端运行 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 &#39;swiftlint help <subcommand>&#39; for detailed help.
复制代码 在(之前新建的项目)HelloSwiftLintApp(目录下)在终端运行swiftlint同样会显示违反了trailing_whitespace规则。- swiftlint
- Linting Swift files in current working directory
- Linting &#39;HelloSwiftLintApp.swift&#39; (1/2)
- Linting &#39;ContentView.swift&#39; (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.
复制代码 <hr/>自动修复 SwiftLint 冲突
从上面的规则列表可以看出,有一些规则是可以自动修正的。这是直接针对间距规则的,就像上面介绍的额外空格一样。只要可以进行 SwiftLint 分析,就可以进行 SwiftLint 自动修正。
在终端运行swiftlint --fix就会自动修正那些可以被自动修正的 SwiftLint 冲突。- swiftlint --fix
- Correcting Swift files in current working directory
- Correcting &#39;HelloSwiftLintApp.swift&#39; (1/2)
- Correcting &#39;ContentView.swift&#39; (2/2)
- .../HelloSwiftLint/ContentView.swift:13:1 Corrected Trailing Whitespace
- Done inspecting 2 files for auto-correction!
复制代码 或者,可以将自动修复整合到 Xcode 的Build Phase。编辑&#34;Run Script Phase&#34;下的 SwiftLint 脚本。现在,在 Xcode 中编译代码时,添加尾随空格会自动删除。- export PATH=&#34;$PATH:/opt/homebrew/bin&#34;
- if which swiftlint > /dev/null; then
- swiftlint --fix && swiftlint
- else
- echo &#34;warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint&#34;
- fi
复制代码
<hr/>手动修复 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 的标识符冲突无法被修复
<hr/>一些规则的例外情况
在某些情况下,代码需要与某些外部API或数据源兼容。Open Weather API提供了如Read JSON with codeable in Swift中所描述的 JSON 数据。这些天气数据中main数据段含有一些有下划线的标识符,比如,feels_like 。用于 Swift 解码此 JSON 的结构体必须与 JSON 中的字段名称匹配,由于 SwiftLint 的”***identifier_name***”规则,Swift 代码会产生编译时错误。
天气数据的示例 JSON
- {
- &#34;coord&#34;: {
- &#34;lon&#34;: -122.08,
- &#34;lat&#34;: 37.39
- },
- &#34;weather&#34;: [
- {
- &#34;id&#34;: 800,
- &#34;main&#34;: &#34;Clear&#34;,
- &#34;description&#34;: &#34;clear sky&#34;,
- &#34;icon&#34;: &#34;01d&#34;
- }
- ],
- &#34;base&#34;: &#34;stations&#34;,
- &#34;main&#34;: {
- &#34;temp&#34;: 9.4,
- &#34;feels_like&#34;: 8.71,
- &#34;temp_min&#34;: 7.22,
- &#34;temp_max&#34;: 11.11,
- &#34;pressure&#34;: 1023,
- &#34;humidity&#34;: 100,
- &#34;sea_level&#34;: 100
- },
- &#34;visibility&#34;: 16093,
- &#34;wind&#34;: {
- &#34;speed&#34;: 1.5,
- &#34;deg&#34;: 350
- },
- &#34;clouds&#34;: {
- &#34;all&#34;: 1
- },
- &#34;dt&#34;: 1560350645,
- &#34;sys&#34;: {
- &#34;type&#34;: 1,
- &#34;id&#34;: 5122,
- &#34;message&#34;: 0.0139,
- &#34;country&#34;: &#34;US&#34;,
- &#34;sunrise&#34;: 1560343627,
- &#34;sunset&#34;: 1560396563
- },
- &#34;timezone&#34;: -25200,
- &#34;id&#34;: 420006353,
- &#34;name&#34;: &#34;Mountain View&#34;,
- &#34;cod&#34;: 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
- }
- }
复制代码
<hr/>不要急于禁用规则
偶尔会有一些 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 = &#34;feels_like&#34;
- case tempMin = &#34;temp_min&#34;
- case tempMax = &#34;temp_max&#34;
- case pressure
- case humidity
- }
- }
复制代码
使用CodingKeys来映射JSON变量好于禁用SwiftLint规则
使用 CodingKeys 来映射 JSON 变量好于禁用 SwiftLint 规则
<hr/>自定义 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(&#34;Hello, world!&#34;)
- // TODO: Remove this function when done
- let a = 24
- Text(&#34;second line \(a)&#34;)
- Spacer()
- }
- }
- }
复制代码
TODO注释没有造成警告,其他规则不受影响
TODO 注释没有造成警告,其他规则不受影响
在已有的规则上使用 SwiftLint 最简单的方法是:
- 安装 SwiftLint
- 通过编译阶段脚本,将 SwiftLint 整合进 Xcode 项目中
- 编译以评估所有警告和错误
- 添加.swiftlint.yml文件,并禁用冲突数最多的规则
- 一次启用一条规则并修复代码中的问题
<hr/>结论
对于任何 Swift 开发者来说,使用 SwiftLint 都是必要的。它有助于避免团队中关于代码样式的争论,以及建立代码风格的统一性。就我而言,它帮我摆脱了诸如创建单字符标识符等坏习惯。
将 SwiftLint 添加到已有的代码库可能比添加到新项目要复杂得多,因为它可能会显示数百个警告和错误。通过配置规则,并逐渐开启更多的规则,可以在现有项目中采用 SwiftLint。
SwiftLint 的自动修复冲突的能力非常强大,通过自动修复冲突可以显著解决数百个冲突的初始情况。只需要确保代码在进行大范围的自动更改之前已经纳入了版本控制,这样在出问题时就能很容易撤销。
<hr/>译者的一些补充
关于 SwiftLint 的安装
安装的方式有几种,原文介绍的是使用 homebrew 安装。译者比较推荐直接使用CocoaPods:- pod &#39;SwiftLint&#39;, &#39;0.46.5&#39;
复制代码 能清晰明了的指明项目使用了 SwiftLint ,同时也方便指定版本。
SwiftLint 不仅仅能帮助解决格式问题
SwiftLint 不仅仅能解决很多格式问题,它的功能还有很多。比如限制一个函数参数的个数,函数、文件最长多少行,使用更精简,更Swift 的函数等等。这能在很大程度上帮助我们写出高质量的代码。
很多团队伙伴在写代码时,一开始的函数,文件可能没那么臃肿。但是随着功能的增加,不断地往一个函数添加参数,不断修改函数的功能,不断往一个文件增加新的函数等等,各个地方开始变得臃肿。当 SwiftLint 告诉你函数参数个数超过了指定的个数,函数行数超过了最大值,文件超过了最大行数等等时,就应该认真考虑是不是该重构了。
在已有项目中添加 SwiftLint
在添加 SwiftLint 之前,最重要的是全体应该开一个简短的会。用来同步以及确定规则。让所有人充分表达意见,而不是一个人制定,有些人不赞同。将一些模棱两可的规则确定清楚之后,写入到 SwiftLint 配置当中,所有人都应该遵守。
译者的项目非常的庞大,刚添加 SwiftLint 的时候,警告加错误高达5000多了,根本改不过来。我采用的方法是分路径配置 SwiftLint。那些比较老的,不怎么会动的目录采用较为宽松的配置。新起一个目录,之后的新代码原则上都应该在此目录之下。此目录的配置会较为严格。
Hook git pre-commit在提交之前做规则检查,如有问题,直接报错,无法提交代码。这样就不会产生新的警告和错误,慢慢的再修改之前的代码,经过1年多,整个项目基本就覆盖全了。
另外,关于项目的警告也需要管理,很多时候有些成员写代码的时候无视警告,导致项目警告几百个,而且越来越多。这就导致警告失去了它原有的作用,译者项目的警告是0,能够很轻松的定位哪个地方发出了新的警告。同时代码 Review 也非常重要,有些成员为了避免麻烦,各个地方都直接 disable 了 SwiftLint 的规则,这是不应该的。
最后,永远记得你只是在使用的只是一个工具,而不是目的。请充分衡量成本和收益。 |