Using SwiftLint Static Code Analyzer in iOS Mobile Banking Applications

Oleg Ivanov, Head of the Competence Center for Remote Service Channels



Hello everyone! As promised in the article “Mobile Banking from ICD: History of Development” , today I want to talk about static code analyzers and the experience of their application in iOS mobile applications of the bank.

Let's set the goal that we want to achieve using this toolkit:

  • early detection of errors and shortcomings;
  • Code Style ( — , ).

A common way to achieve our goals is to manually check the code - Code Review .

The tester or a group of testers carefully study the code being tested, then they identify errors or sections of code that may become erroneous in the future, and give recommendations for improving it through comments on the code, which after some time (!) Will be analyzed and corrected by the developer. As we can see, a long and expensive code verification process is emerging. Plus, there is always a human factor - some errors can simply be skipped by reviewers.

Here static code analyzers come to our aid.

Static Code Analyzers- start an automated process for detecting errors and shortcomings in the source code of programs.

Static code analyzers are not a panacea or a replacement for manual Code review, but they are an excellent tool to reduce the time it takes to carry out checks and quickly find patterned errors. The relevance of using static analyzers will only grow over time.

As a static code analyzer in our iOS projects we use SwiftLint.

SwiftLint is a Swift code auto-checking utility that runs during the build phase of a project. The utility contains a set of rules with the ability to supplement this set with your custom rules. The tool is also used to comply with Code Style.

Why is it important to follow Code Style in the application?
When you are one developer on a project, everything is simple: you write in your own style and you can afford the code in this way:



But when you work in a large development team. an important factor is the speed of understanding and finding the place to insert improvements into someone else's code.
And here all the team members have to accept the conventions and rules for writing code. But how to check their compliance? Once again, the SwiftLint static code analyzer will help us. And our code will take a pleasant look that every member of the team will understand:



To get acquainted with the tool, I recommend reading the official documentation .

Installing SwiftLint is simple:

  1. Add pod 'SwiftLint' to the project podfile
  2. Add a new “Run Script Phase” to the project.

    "${PODS_ROOT}/SwiftLint/swiftlint"
  3. SwiftLint .swiftlint.yml
  4. .swiftlint.yml , SwiftLint

If the SwiftLint embedding project already contained code, you will have to be patient and systematically fix all SwiftLint recommendations. He forms them through the usual error and warning displays with comprehensive tips about the recommendations.



Also in parentheses, it displays the name of the rule that initiated the recommendation (operator_usage_whitespace) .

In this case, these are:

Operator Usage Whitespace
Operators must be surrounded by a single space.

Correct code:




Recommended code:



Each rule has a set of attributes:

Identifier : operator_usage_whitespace
Enabled by default : disabled
Supported by auto-correction :yes
Type : style
Analyzer rule : No
Minimum version of the Swift compiler : 3.0.0
Default configuration : warning

Pay attention to the “Minimum version of the Swift compiler” - correlate the use of rules with this attribute and with the settings of your project.

The “Default configuration” attribute shows how rules with this attribute will be perceived: either a regular warning, or a compilation error, such as a Force Cast rule (Default configuration: error)

All the rules can be found in a very convenient and illustrated documentation.

Below I will present the rules that we have chosen in our team, you can use them as an example to create your project.

We divided all the rules into functional and stylistic - yes, yes, yes, and one space inside each curly brace, and the closing parameters should be on the same line as the opening bracket, and ... :). I don’t paint the rules, you can easily find information about them using the link above.

Functional :

- private_outlet
- force_unwrapping
- force_cast
- force_try
- strong_iboutlet
- private_action
- block_based_kvo
- contains_over_first_not_nil
- discarded_notification_center_observer
- discouraged_direct_init
- discouraged_object_literal
- discouraged_optional_boolean
- discouraged_optional_collection
- duplicate_imports
- dynamic_inline
- empty_count
- empty_parameters
- empty_parentheses_with_trailing_closure
- empty_string
- explicit_enum_raw_value
- function_default_parameter_at_end
- generic_type_name
- identical_operands
- implicit_getter
- is_disjoint
- notification_center_detachment
- nsobject_prefer_isequal
- redundant_set_access_control
- unused_capture_list

The style of :

- unneeded_parentheses_in_closure_argument
- let_var_whitespace
- yoda_condition
- colon
- comma
- closure_parameter_position
- closure_spacing
- collection_alignment
- leading_whitespace
- mark
- opening_brace
- operator_usage_whitespace
- operator_whitespace
- protocol_property_accessors_order
- return_arrow_whitespace
- switch_case_alignment
- statement_position
- trailing_comma
- trailing_newline
- unneeded_break_in_switch
- custom_rules
- closure_end_indentation
- file_name_no_space
- unowned_variable_capture
- no_space_in_method_call
- contains_over_filter_count
- contains_over_filter_is_empty
- contains_over_range_nil_comparison
- duplicate_enum_cases
- empty_collection_literal

also a goal of the SwiftLint we chose only the catalog with our code base by adding the appropriate settings in the configuration file .swiftlint.yml:

included:

- <code base directory>


We've created your rule for inadmissibility print functions. print is a pretty hard operation. Via the configuration .swiftlint.yml file:

custom_rules:
  disable_print:
    included: ".*\\.swift"
    name: "print usage"
    regex: "((\\bprint)|(Swift\\.print))\\s*\\("
    message: "Prefer os_log over print"
    severity: error

This rule prompted us to write our logging function (with a logging level). This logging is used by developers for quick debugging, for example, logs with parameters, request / response body are needed for any analysis of errors, for development. For Release, the log level in our .none version. The rest of the use of the print function will result in a build error for the project.

func logApp(level: Constants.LogLevel, items: Any...) {
    if Constants.logLevel == .none {
        return
    }
    
    if level.rawValue <= Constants.logLevel.rawValue {
        // swiftlint:disable disable_print
        if let strings = items as? [String] {
            for string in strings {
                print(string)
            }
        } else {
            print(items)
        }
        // swiftlunt:enable disable_print
    }

But to use the print function, we had to close its call in our logging function using SwiftLint to ignore our own rules on the code block marked with a special instruction.

// swiftlint:disable disable_print
// swiftlunt:enable disable_print

SwiftLint has the ability to ignore its own rules:

// swiftlint:disable <rule1 [rule2 rule3…]>
<,   SwiftLint   rule1 [rule2 rule3…]>
// swiftlunt:enable <rule1 [rule2 rule3…]>

Or

// swiftlint:disable all
<,   SwiftLint   >
// swiftlint:enable all

Use this opportunity, fully aware that it is necessary!

In conclusion, I note : the use of SwiftLint in our team reduced the cost of manual Code review by 20%. Now our reviewers do not pay attention to typical errors (Force Cast, etc.), Code Style and can fully concentrate on checking the new code. Which significantly increased the overall team efficiency (no need to fix such errors checking, these are qualified employees, whose time is very important).

All! Now SwiftLint is forever with you :)


All Articles