Олег Иванов, руководитель направления центра компетенций дистанционных каналов обслуживания
Всем привет! Как и обещал в статье «Мобильный банк от МКБ: история развития», хочу сегодня поговорить о статических анализаторах кода и об опыте их применения в iOS мобильных приложениях банка.
Давайте поставим цель, которую мы хотим достичь, используя этот инструментарий:
Распространённым способом достижения поставленных нами целей является ручная проверка кода – Обзор кода (Code review).
Проверяющий или группа проверяющих внимательно изучают проверяемый код, далее они выявляют ошибки или участки кода, которые могут стать ошибочными в будущем, и дают рекомендации по его улучшению через комментарии к коду, которые через некоторое (!) время будут проанализированы и исправлены разработчиком. Как мы видим, вырисовывается долгий и дорогостоящий процесс проверки кода. Плюс всегда есть человеческий фактор – некоторые ошибки могут быть просто-напросто пропущены проверяющими.
Тут нам на помощь приходят статические анализаторы кода.
Статические анализаторы кода — запускают автоматизированный процесс выявления ошибок и недочетов в исходном коде программ.
Статические анализаторы кода – это не панацея и не замена ручного Сode review, но являются отличным инструментом, позволяющим снизить время на проведение проверки и оперативно найти шаблонизированные ошибки. Актуальность использования статических анализаторов со временем будет только расти.
В качестве статического анализатора кода в своих iOS проектах мы используем SwiftLint.
SwiftLint — это утилита автоматической проверки Swift-кода, работающая на этапе сборки проекта. Утилита содержит набор правил с возможностью дополнения этого набора своими custom правилами. Инструмент также применяется для соблюдения Code Style.
Почему в приложении важно соблюдать Code Style?
Когда вы один разработчик на проекте – всё просто: вы пишете в своём стиле и можете позволить себе код в подобном виде:
Но когда вы работаете в большой команде разработчиков. важным фактором является скорость понимания и нахождения места вставки доработок в чужой код.
И здесь приходится принимать всеми членами команды соглашения и правила написания кода. Но как проверить их соблюдение? Опять нам поможет статический анализатор кода SwiftLint. И наш код примет приятный вид, который поймёт каждый член команды:
Для знакомства с инструментом рекомендую почитать официальную документацию.
Установка SwiftLint проста:
Если проект встраивания SwiftLint уже содержал код, придётся запастись терпением и планомерно исправить все рекомендации SwiftLint. Их он формирует через привычные error и warning отображения c исчерпывающими подсказками о рекомендации.
Также в скобках он выведет имя правила, которое инициировало рекомендацию (operator_usage_whitespace).
В данном случае это:
Operator Usage Whitespace
Операторы должны быть окружены одним пробелом.
Правильный код:
Код вызывающий рекомендацию:
Каждое правило имеет набор атрибутов:
Идентификатор: operator_usage_whitespace
Включено по умолчанию: отключено
Поддерживает автокоррекцию: да
Вид: стиль
Правило анализатора: Нет
Минимальная версия компилятора Swift: 3.0.0
Конфигурация по умолчанию: предупреждение
Обращайте внимание на «Минимальная версия компилятора Swift» – соотносите использование правил с этим атрибутом и с настройками вашего проекта.
Атрибут «Конфигурация по умолчанию» показывает, как правила с этим атрибутом будут восприниматься: или обычное предупреждение, или ошибка компиляции, как например, правило Force Cast (Конфигурация по умолчанию: ошибка)
Все правила можно посмотреть в очень удобной и иллюстрированной документации.
Ниже я представлю правила, которые мы выбрали в своей команде, можете использовать их как пример для создания своего проекта.
Мы разделили все правила на функциональные и стилистические – да-да-да и один пробел внутри каждой фигурной скобки, и параметры закрытия должны быть в той же строке, что и открывающая скобка, и… :). Правила не расписываю, информацию о них вы легко найдёте, воспользовавшись ссылкой выше.
Функциональные:
— 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
Cтилистические:
— 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
Также под цель работы SwiftLint мы выбрали только каталог с нашей кодовой базой, добавив соответствующую настройку в конфигурационный .swiftlint.yml файл:
included:
— <каталог кодовой базы>
Мы создали своё правило для недопустимости использования функции print. print — достаточно тяжелая операция. Через конфигурационный .swiftlint.yml файл:
Это правило побудило нас написать свою функцию логирования (с уровнем логирования). Данное логирование используется разработчиками для быстрой отладки, к примеру, логи с параметрами, телом запроса/ответа нужны для любого разбора ошибок, для разработки. Для Release уровень лога в нашем варианте .none. Остальные же использования функции print приведут к ошибке сборки проекта.
Но для использования функции принт нам пришлось закрыть её вызов в нашей функции логирования средствами SwiftLint для игнорирования собственных правил на помеченном специальной инструкцией блока кода.
SwiftLint имеет возможность игнорирования собственных правил:
Или
Используйте эту возможность, полностью отдавая себе отчёт, что это необходимо!
В заключении статьи отмечу: применение SwiftLint в нашей команде снизило стоимость ручного Code review на 20%. Теперь наши проверяющие не обращают внимание на типичные ошибки (Force Cast и др.), Code Style и могут в полной мере сосредоточиться на проверке нового кода. Что значительно повысило эффективность работы команды в целом (не нужно исправлять подобные ошибки, проверяющие – это квалифицированные сотрудники, чьё время очень важно).
Всё! Теперь SwiftLint навсегда с вами :)
Всем привет! Как и обещал в статье «Мобильный банк от МКБ: история развития», хочу сегодня поговорить о статических анализаторах кода и об опыте их применения в iOS мобильных приложениях банка.
Давайте поставим цель, которую мы хотим достичь, используя этот инструментарий:
- раннее выявление ошибок и недочётов;
- Code Style (стандарт оформления кода — набор правил и соглашений, используемых при написании кода программы).
Распространённым способом достижения поставленных нами целей является ручная проверка кода – Обзор кода (Code review).
Проверяющий или группа проверяющих внимательно изучают проверяемый код, далее они выявляют ошибки или участки кода, которые могут стать ошибочными в будущем, и дают рекомендации по его улучшению через комментарии к коду, которые через некоторое (!) время будут проанализированы и исправлены разработчиком. Как мы видим, вырисовывается долгий и дорогостоящий процесс проверки кода. Плюс всегда есть человеческий фактор – некоторые ошибки могут быть просто-напросто пропущены проверяющими.
Тут нам на помощь приходят статические анализаторы кода.
Статические анализаторы кода — запускают автоматизированный процесс выявления ошибок и недочетов в исходном коде программ.
Статические анализаторы кода – это не панацея и не замена ручного Сode review, но являются отличным инструментом, позволяющим снизить время на проведение проверки и оперативно найти шаблонизированные ошибки. Актуальность использования статических анализаторов со временем будет только расти.
В качестве статического анализатора кода в своих iOS проектах мы используем SwiftLint.
SwiftLint — это утилита автоматической проверки Swift-кода, работающая на этапе сборки проекта. Утилита содержит набор правил с возможностью дополнения этого набора своими custom правилами. Инструмент также применяется для соблюдения Code Style.
Почему в приложении важно соблюдать Code Style?
Когда вы один разработчик на проекте – всё просто: вы пишете в своём стиле и можете позволить себе код в подобном виде:
Но когда вы работаете в большой команде разработчиков. важным фактором является скорость понимания и нахождения места вставки доработок в чужой код.
И здесь приходится принимать всеми членами команды соглашения и правила написания кода. Но как проверить их соблюдение? Опять нам поможет статический анализатор кода SwiftLint. И наш код примет приятный вид, который поймёт каждый член команды:
Для знакомства с инструментом рекомендую почитать официальную документацию.
Установка SwiftLint проста:
- Добавляем pod ‘SwiftLint' в Podfile проекта
- Добавляем новый «Run Script Phase» к проекту
"${PODS_ROOT}/SwiftLint/swiftlint"
- Определяемся с набором правил SwiftLint и записываем их в конфигурационный файл .swiftlint.yml
- Добавивляем конфигурационный файл .swiftlint.yml в каталог, из которого будем запускать SwiftLint
Если проект встраивания SwiftLint уже содержал код, придётся запастись терпением и планомерно исправить все рекомендации SwiftLint. Их он формирует через привычные error и warning отображения c исчерпывающими подсказками о рекомендации.
Также в скобках он выведет имя правила, которое инициировало рекомендацию (operator_usage_whitespace).
В данном случае это:
Operator Usage Whitespace
Операторы должны быть окружены одним пробелом.
Правильный код:
Код вызывающий рекомендацию:
Каждое правило имеет набор атрибутов:
Идентификатор: operator_usage_whitespace
Включено по умолчанию: отключено
Поддерживает автокоррекцию: да
Вид: стиль
Правило анализатора: Нет
Минимальная версия компилятора Swift: 3.0.0
Конфигурация по умолчанию: предупреждение
Обращайте внимание на «Минимальная версия компилятора Swift» – соотносите использование правил с этим атрибутом и с настройками вашего проекта.
Атрибут «Конфигурация по умолчанию» показывает, как правила с этим атрибутом будут восприниматься: или обычное предупреждение, или ошибка компиляции, как например, правило Force Cast (Конфигурация по умолчанию: ошибка)
Все правила можно посмотреть в очень удобной и иллюстрированной документации.
Ниже я представлю правила, которые мы выбрали в своей команде, можете использовать их как пример для создания своего проекта.
Мы разделили все правила на функциональные и стилистические – да-да-да и один пробел внутри каждой фигурной скобки, и параметры закрытия должны быть в той же строке, что и открывающая скобка, и… :). Правила не расписываю, информацию о них вы легко найдёте, воспользовавшись ссылкой выше.
Функциональные:
— 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
Cтилистические:
— 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
Также под цель работы SwiftLint мы выбрали только каталог с нашей кодовой базой, добавив соответствующую настройку в конфигурационный .swiftlint.yml файл:
included:
— <каталог кодовой базы>
Мы создали своё правило для недопустимости использования функции print. print — достаточно тяжелая операция. Через конфигурационный .swiftlint.yml файл:
custom_rules:
disable_print:
included: ".*\\.swift"
name: "print usage"
regex: "((\\bprint)|(Swift\\.print))\\s*\\("
message: "Prefer os_log over print"
severity: error
Это правило побудило нас написать свою функцию логирования (с уровнем логирования). Данное логирование используется разработчиками для быстрой отладки, к примеру, логи с параметрами, телом запроса/ответа нужны для любого разбора ошибок, для разработки. Для Release уровень лога в нашем варианте .none. Остальные же использования функции print приведут к ошибке сборки проекта.
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
}
Но для использования функции принт нам пришлось закрыть её вызов в нашей функции логирования средствами SwiftLint для игнорирования собственных правил на помеченном специальной инструкцией блока кода.
// swiftlint:disable disable_print
// swiftlunt:enable disable_print
SwiftLint имеет возможность игнорирования собственных правил:
// swiftlint:disable <rule1 [rule2 rule3…]>
<код, который игнорируется SwiftLint для правил rule1 [rule2 rule3…]>
// swiftlunt:enable <rule1 [rule2 rule3…]>
Или
// swiftlint:disable all
<код, который игнорируется SwiftLint для всех правил>
// swiftlint:enable all
Используйте эту возможность, полностью отдавая себе отчёт, что это необходимо!
В заключении статьи отмечу: применение SwiftLint в нашей команде снизило стоимость ручного Code review на 20%. Теперь наши проверяющие не обращают внимание на типичные ошибки (Force Cast и др.), Code Style и могут в полной мере сосредоточиться на проверке нового кода. Что значительно повысило эффективность работы команды в целом (не нужно исправлять подобные ошибки, проверяющие – это квалифицированные сотрудники, чьё время очень важно).
Всё! Теперь SwiftLint навсегда с вами :)