iOS-Swift语法静态分析配置|统一编码规范【Fastlane+SwiftLint】

2,497 阅读7分钟

Swiftlint 介绍

安装Fastlane

使用 Fastlane 上传 App 到蒲公英

www.pgyer.com/doc/view/fa…

Fastlane 是一款为 iOS 和 Android 开发者提供的自动化构建工具,它可以帮助开发者将 App 打包、签名、测试、发布、信息整理、提交 App Store 等工作完整的连接起来,实现完全自动化的工作流,如果使用得当,可以显著的提高开发者的开发效率。

为了让使用 Fastlane 的用户,可以将 Fastlane 的自动化工作流与蒲公英结合起来,我们为大家提供了 Fastlane 的蒲公英插件。该插件使用起来非常简单,安装该插件后,由 Fastlane 打包生成的 App 可以自动上传到蒲公英。

前置条件

  1. 开发者使用的是 mac 或 linux 系统
  2. 已经安装好 ruby、rubygems、bundler
  3. 已经安装了 Fastlane。如果没有安装,请参考:官方安装指南
  4. 开发者了解基本的 App 开发流程、终端的使用方法
  5. 本文使用环境为:macOS 10.13, Xcode 9.2, Fastlane 2.78.0, fastlane-plugin-pgyer 0.2.1

配置SwiftLint的lane

设置工程的Fastlane配置

给项目添加SwiftLint

  • 安装Fastlane
  • 配置Fastlane的Fastfile
    • 添加一个lane
    # This file contains the fastlane.tools configuration
    # You can find the documentation at https://docs.fastlane.tools
    #
    # For a list of all available actions, check out
    #
    #     https://docs.fastlane.tools/actions
    #
    # For a list of all available plugins, check out
    #
    #     https://docs.fastlane.tools/plugins/available-plugins
    #
    
    # Uncomment the line if you want fastlane to automatically update itself
    # update_fastlane
    
    default_platform(:ios)
    
    platform :ios do
        desc "Description of what the lane does"
    
        lane :lint do
        swiftlint(
          mode: 'lint',    # 运行 SwiftLint 的模式
          config_file: 'swiftlint.yml',    # SwiftLint 配置文件的路径(可选)
          ignore_exit_status: true    # 忽略 SwiftLint 的退出状态,以允许 Fastlane 继续执行
        )
        end
    end
    
    
    • 在工程主目录添加swiftlint文件:(swiftlint.yml) image.png
      • 可以在github上搜索、参考:一些大公司的Swiftlint规则设置,进行编写
      • 也可以直接使用以下的配置:
      # Enabled Rules
      
      opt_in_rules:
      
        - anyobject_protocol
      
        - array_init
      
        - attributes
      
        - block_based_kvo
      
        - capture_variable
      
        - class_delegate_protocol
      
        - closing_brace
      
        - closure_body_length
      
        - closure_end_indentation
      
        - closure_parameter_position
      
        - closure_spacing
      
        - collection_alignment
      
        - colon
      
        - comma
      
        - compiler_protocol_init
      
        - contains_over_filter_count
      
        - contains_over_filter_is_empty
      
        - contains_over_first_not_nil
      
        - contains_over_range_nil_comparison
      
        - control_statement
      
        - custom_rules
      
        - deployment_target
      
        - discarded_notification_center_observer
      
        - discouraged_direct_init
      
        - discouraged_none_name
      
        - discouraged_object_literal
      
        - duplicate_enum_cases
      
        - duplicate_imports
      
        - duplicated_key_in_dictionary_literal
      
        - dynamic_inline
      
        - empty_collection_literal
      
        - empty_count
      
        - empty_enum_arguments
      
        - empty_parameters
      
        - empty_parentheses_with_trailing_closure
      
        - empty_string
      
        - empty_xctest_method
      
        - enum_case_associated_values_count
      
        - expiring_todo
      
        - explicit_init
      
        - fallthrough
      
        - fatal_error_message
      
        - file_header
      
        - file_length
      
        - first_where
      
        - flatmap_over_map_reduce
      
        - for_where
      
        - force_cast
      
        - force_try
      
        - function_body_length
      
        - function_parameter_count
      
        - generic_type_name
      
        - ibinspectable_in_extension
      
        - identical_operands
      
        - identifier_name
      
        - implicit_getter
      
        - implicit_return
      
        - implicitly_unwrapped_optional
      
        - indentation_width
      
        - inert_defer
      
        - is_disjoint
      
        - joined_default_parameter
      
        - large_tuple
      
        - last_where
      
        - leading_whitespace
      
        - legacy_cggeometry_functions
      
        - legacy_constant
      
        - legacy_constructor
      
        - legacy_hashing
      
        - legacy_multiple
      
        - legacy_nsgeometry_functions
      
        - legacy_random
      
        - let_var_whitespace
      
        - line_length
      
        - literal_expression_end_indentation
      
        - mark
      
        - missing_docs
      
        - modifier_order
      
        - multiline_arguments
      
        - multiline_function_chains
      
        - multiline_literal_brackets
      
        - multiline_parameters
      
        - multiple_closures_with_trailing_closure
      
        - no_extension_access_modifier
      
        - no_fallthrough_only
      
        - no_space_in_method_call
      
        - notification_center_detachment
      
        - nslocalizedstring_require_bundle
      
        - nsobject_prefer_isequal
      
        - number_separator
      
        - operator_usage_whitespace
      
        - operator_whitespace
      
        - optional_enum_case_matching
      
        - orphaned_doc_comment
      
        - overridden_super_call
      
        - override_in_extension
      
        - prefer_self_type_over_type_of_self
      
        - prefer_zero_over_explicit_init
      
        - private_over_fileprivate
      
        - private_subject
      
        - private_unit_test
      
        - prohibited_interface_builder
      
        - prohibited_super_call
      
        - protocol_property_accessors_order
      
        - reduce_boolean
      
        - reduce_into
      
        - redundant_discardable_let
      
        - redundant_nil_coalescing
      
        - redundant_objc_attribute
      
        - redundant_optional_initialization
      
        - redundant_set_access_control
      
        - redundant_string_enum_value
      
        - redundant_void_return
      
        - return_arrow_whitespace
      
        - shorthand_operator
      
        - sorted_first_last
      
        - sorted_imports
      
        - statement_position
      
        - superfluous_disable_command
      
        - switch_case_alignment
      
        - syntactic_sugar
      
        - toggle_bool
      
        - trailing_comma
      
        - trailing_newline
      
        - trailing_semicolon
      
        - trailing_whitespace
      
        - type_body_length
      
        - type_contents_order
      
        - type_name
      
        - unavailable_function
      
        - unneeded_break_in_switch
      
        - unneeded_parentheses_in_closure_argument
      
        - unowned_variable_capture
      
        - untyped_error_in_catch
      
        - unused_capture_list
      
        - unused_closure_parameter
      
        - unused_control_flow_label
      
        - unused_declaration
      
        - unused_enumerated
      
        - unused_import
      
        - unused_optional_binding
      
        - unused_setter_value
      
        - valid_ibinspectable
      
        - vertical_parameter_alignment
      
        - vertical_parameter_alignment_on_call
      
        - vertical_whitespace
      
        - vertical_whitespace_between_cases
      
        - vertical_whitespace_closing_braces
      
        - void_return
      
        - weak_delegate
      
        - xctfail_message
      
        - yoda_condition
      
      
      
      
      # Disabled Rules
      
      disabled_rules:
      
        - balanced_xctest_lifecycle # don't require balanced test setup and teardown
      
        - conditional_returns_on_newline # principles encourage one-line if and guard statements where applicable
      
        - cyclomatic_complexity # we have many complex switches that have over 100 cases
      
        - discouraged_assert # we don't currently prefer assertionFailure() and preconditionFailure() over assert(false)
      
        - discouraged_optional_boolean # disabled b/c nil, false, and true are all valid cases
      
        - discouraged_optional_collection # disabled b/c it looks at function signatures as well
      
        - explicit_acl # implicit internal ACL is a nice convenience
      
        - explicit_enum_raw_value # implicit raw values is a nice convenience
      
        - explicit_self # principles encourage you to drop self when it is not needed
      
        - explicit_top_level_acl # implicit internal ACL is a nice convenience
      
        - explicit_type_interface # implied Int and String types is a nice convenience
      
        - extension_access_modifier # extension declarations should not have an ACL modifier
      
        - file_types_order # type ordering cannot be consistent in all cases
      
        - force_unwrapping # disabled b/c of guards and ternary use cases
      
        - function_default_parameter_at_end # closures are often after parameters with default values
      
        - lower_acl_than_parent # only makes sense to enable if `no_extension_access_modifier` is disabled
      
        - multiline_arguments_brackets # doesn't match coding standards for collection parameters without external name
      
        - multiline_parameters_brackets # doesn't match coding standards for multi-line methods and functions
      
        - nesting # principles encourage namespacing through nested types
      
        - nimble_operator # not using nimble
      
        - no_grouping_extension # principles encourage grouping type functionality into extensions
      
        - nslocalizedstring_key # don't use genstrings due to loctool
      
        - object_literal # don't use color or image literals
      
        - opening_brace # doesn't support our multi-line function formatting
      
        - pattern_matching_keywords # principles encourage consistency and ease of reading
      
        - prefixed_toplevel_constant # not a good Swift pattern to prefix with `k`
      
        - private_action # don't use storyboards or nibs
      
        - private_outlet # don't use storyboards or nibs
      
        - quick_discouraged_call # not using quick
      
        - quick_discouraged_focused_test # not using quick
      
        - quick_discouraged_pending_test # not using quick
      
        - raw_value_for_camel_cased_codable_enum # don't restrict codable usage for webservice payloads
      
        - required_deinit # useful for debugging, but too extreme to require for all codebases
      
        - required_enum_case # this rule makes no sense...10 gold stars to anyone that can explain it
      
        - single_test_class # principles encourage multiple testcase classes in same file
      
        - static_operator # structs and classes should use static functions in extensions, not protocols
      
        - strict_fileprivate # still valid use cases for fileprivate
      
        - strong_iboutlet # don't use storyboards or nibs
      
        - switch_case_on_newline # principles encourage single line cases where applicable
      
        - trailing_closure # enforces trailing closure use even when function is called on multiple lines
      
        - vertical_whitespace_opening_braces # doesn't match coding standards for MARKs in structs and classes
      
        - xct_specific_matcher # valid cases where equal is more readable with optional bool tests
      
      
      
      
      # Excluded Directories
      
      excluded:
      
        - Pods
      
        - Submodules
      
      
      
      
      reporter: "xcode"
      
      
      
      
      # Configurable Rules
      
      closure_body_length:
      
        warning: 100
      
        error: 140
      
      
      
      
      deployment_target:
      
        iOS_deployment_target: 10.0
      
        macOS_deployment_target: 10.12
      
        tvOS_deployment_target: 10.0
      
        watchOS_deployment_target: 3.0
      
      
      
      
      expiring_todo:
      
        approaching_expiry_threshold: 15
      
        date_format: "MM/dd/yyyy"
      
        date_delimiters:
      
          opening: "["
      
          closing: "]"
      
        date_separator: "/"
      
      
      
      
      file_header:
      
        required_pattern: |
      
          \/\/
      
          \/\/  .*?\.swift
      
          \/\/  [\w ]*
      
          \/\/
      
          \/\/  Created by ([a-zA-Z-]+ ){2,4}on \d{1,2}\/\d{1,2}\/\d{2}\.
      
          \/\/  Copyright © \d{4} Nike\. All rights reserved\.
      
          \/\/
      
      
      
      
      file_length:
      
        warning: 1000
      
        error: 1200
      
        ignore_comment_only_lines: true
      
      
      
      
      function_body_length:
      
        warning: 200
      
        error: 240
      
      
      
      
      function_parameter_count:
      
        warning: 8
      
        error: 12
      
      
      
      
      generic_type_name:
      
        min_length: 1
      
        max_length: 40
      
      
      
      
      identifier_name:
      
        min_length: 1
      
        max_length: 50
      
        excluded: ["id"]
      
        allowed_symbols: ["_"] # for gray_8D and _somePrivateVariable
      
        validates_start_with_lowercase: true
      
      
      
      
      large_tuple:
      
        warning: 5
      
        error: 7
      
      
      
      
      line_length:
      
        warning: 140
      
        error: 160
      
        ignores_urls: false
      
        ignores_function_declarations: false
      
        ignores_comments: false
      
      
      
      
      modifier_order:
      
        preferred_modifier_order: [
      
          "acl",
      
          "setterACL",
      
          "override"
      
      ]
      
      
      
      
      number_separator:
      
        minimum_length: 6
      
        minimum_fraction_length: 100 # to simply disable it
      
      
      
      
      type_body_length:
      
        warning: 600
      
        error: 800
      
      
      
      
      type_contents_order:
      
        order: [
      
          ["case"],
      
          ["type_alias", "associated_type", "subtype", "type_property", "instance_property"],
      
          ["ib_outlet"],
      
          ["ib_inspectable"],
      
          ["initializer", "deinitializer"],
      
          ["type_method"],
      
          ["subscript"],
      
          ["view_life_cycle_method"],
      
          ["ib_action"],
      
          ["other_method"]
      
        ]
      
      
      
      
      type_name:
      
        min_length: 3
      
        max_length: 50
      
        allowed_symbols: ["_"]
      
      
      
      
      # Custom Rules
      
      custom_rules:
      
        fixme:
      
          include: ".*swift"
      
          name: "FIXME"
      
          regex: "((?i)(FIXME))"
      
          match_kinds:
      
            - comment
      
          message: "Unfinished Code"
      
          severity: warning
      
      
      
      
        hanging_else_keyword:
      
          included: ".*\\.swift"
      
          name: "There should not be a newline character after the `else` keyword."
      
          regex: "\\else\\s*\\n+"
      
          severity: warning
      
      
      
      
        todo_format:
      
          include: ".*swift"
      
          name: "TODO"
      
          regex: "((?i)(TODO: ))(?![A-Z]+-)"
      
          match_kinds:
      
            - comment
      
          message: "TODO should include Jira ticket e.g. '// TODO: TEAM-xxxx:'"
      
          severity: warning
      
      
      
      
        no_newlines_after_indent_changes_before_comments:
      
          included: ".*\\.swift"
      
          name: "No Newlines After Indent Changes Before Comments"
      
          regex: "\\{[\\w ]*?\\n[ ]*?\\n[ ]*\\/{2,3}[ ](?!MARK)"
      
          severity: warning
      
      
      
      
        blank_line_after_closing_brace_or_parenthesis:
      
          included: ".*\\.swift"
      
          name: "A Closing Brace/Parenthesis On Its Own Line Must Have a Newline Between It and Any Code"
      
          regex: "\\n[ ]*?[\\}\\)]\\n[ ]*?(?!set|case|else)[\\w]"
      
          severity: warning
      
      
      
      
        triple_quotation_marks_on_declaring_line:
      
          included: ".*\\.swift"
      
          name: "First Triple Quotation Marks Must Be On Declaring Line"
      
          regex: "=\\n[ ]*\"\"\""
      
          severity: warning
      
      
      
      
        no_extra_newline_after_open_brace:
      
          included: ".*\\.swift"
      
          name: "No Extra Newline After An Open Brace"
      
          regex: "\\{\\n *\\n(?! *\\/\\/ MARK)"
      
          severity: warning
      
      
      
      
        uppercase_id_suffix:
      
          included: ".*\\.swift"
      
          name: "ID suffix should always be all uppercase"
      
          regex: "[\\w]*Id\\b"
      
          match_kinds:
      
            - argument
      
            - comment
      
            - identifier
      
            - parameter
      
          severity: warning
      
      
      
      
        uppercase_url_suffix:
      
          included: ".*\\.swift"
      
          name: "URL suffix should always be all uppercase"
      
          regex: "[\\w]*Url\\b"
      
          match_kinds:
      
            - argument
      
            - comment
      
            - identifier
      
            - parameter
      
          severity: warning
      
      
      
      
        uppercase_json_suffix:
      
          included: ".*\\.swift"
      
          name: "JSON suffix should always be all uppercase"
      
          regex: "[\\w]*Json\\b"
      
          match_kinds:
      
            - argument
      
            - comment
      
            - identifier
      
            - parameter
      
          severity: warning
      
      
      
      
        test_case_suffix:
      
          included: ".*\\.swift"
      
          name: "Test case suffix should always be 'TestCase'"
      
          regex: "[\\w]*Tests:"
      
          match_kinds:
      
            - identifier
      
          severity: warning
      
    • 配置工程,添加启动脚本 image.png
    # Type a script or drag a script file from your workspace to insert its path.
    
    if which swiftlint >/dev/null; then
    
      swiftlint
    
    else
    
      echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
    
    fi
    
    • 在终端切换到该工程目录下,执行Fastlane命令:bundle exec fastlane lint