Привет, Habr! Меня зовут Алексей, я iOS Developer в компании FINCH. Скоро Новый год — самое время чтобы начать жить по-другому, а поможет в этом такая классная штука как SwiftLint. В статье я расскажу, почему ее обязательно нужно внедрять во все проекты, включая legacy и pet-проекты, а также покажу как выжать из этого инструмента максимум, используя регулярные строки.
Я не буду рассказывать, что такое SwiftLint и как его можно установить — если вы не знакомы с инструментом, то лучше почитать официальную документацию.
Лучше сразу перейдем к частой проблеме, которая возникает при работе с большими проектами — несоблюдение стайлгайдов под прикрытием хотфиксов или прочим. Но по факту, даже если вы чтите стайлгайды и даже в крайне нетрезвом состоянии можете их назвать, никто не гарантирует, что не может произойти банальная опечатка, которая хоть и не повлечет за собой поломку логики, но которая явно повлияет на эстетическое удовольствие.
Итак, запоминаем:
1. SwiftLint позволяет делать:
Собственно все.
Статья могла бы здесь закончиться, но если бы это было так я бы вообще не начал бы писать эту статью. Интересно, что SwiftLint не позволяет делать — писать го… горячих фиксов код.
2. SwiftLint позволяет предотвращать:
Думаю, неплохо быть застрахованным от таких ошибок, не правда ли? Для начинающего разработчика это особенно хорошо, так как он только учится и временами не подозревает о подобных ошибках.
3. Swiftlint можно расширять по собственным правилам.
Начну я с указания final для классов, которые не будут являться родительскими по отношению к другим. Благодаря final мы экономим время сборки проекта. Вот что говорит нам документация Apple про final классы:
Решим мы такую невнимательность несложной регуляркой. Далее я буду писать сразу на ruby, чтобы вы могли вставлять код сразу в свой проект:
Небольшой и достаточно простой пример. Я не буду писать что-то подобное на каждое правило, но исходный код будет в конце статьи.
Следующий момент — это required init. Мы в компании не пользуемся сторибордами, поэтому указывать фатальный инициализатор в каждом UIView классе не совсем нормально. Для такого случая у нас есть собственный NLView (NL — NibLess) — класс, в котором required init реализован всего один раз. Новые разработчики на проекте могут этого не знать, но благо SwiftLint всегда поругается вместо ответственного лида. Или вместе с ним.
Если вы все еще пользуетесь сторибордами, вы можете использовать следующие правила, чтобы знать что все ваши проперти сторибордов приватные:
Часто бывает, что Foundation используется там, где он совершенно не нужен. Поэтому лучше подсветить его всякий раз чтобы не забыть:
Надеюсь, все знают что print — достаточно тяжелая операция, которая может сильно навредить перфомансу приложения(особенно в циклах). Единственный вердикт — принтов не должно быть совсем.
Также не следует создавать class only протоколы, так как есть вероятность, что такой синтаксис скоро станет deprecated и этого не рекомендуют делать разработчики Swift.
Ниже правило для проектов, в которых используется R.swift.
Эта лишь малая часть того, что я смог придумать, но в интернете примеров еще меньше. Всю мою «коллекцию» вы можете посмотреть на github.
Спасибо за внимание. Если вы тоже используете SwiftLint с кастомными правилами, то расскажите о них — буду рад обсудить возможные кейсы.
Я не буду рассказывать, что такое SwiftLint и как его можно установить — если вы не знакомы с инструментом, то лучше почитать официальную документацию.
Лучше сразу перейдем к частой проблеме, которая возникает при работе с большими проектами — несоблюдение стайлгайдов под прикрытием хотфиксов или прочим. Но по факту, даже если вы чтите стайлгайды и даже в крайне нетрезвом состоянии можете их назвать, никто не гарантирует, что не может произойти банальная опечатка, которая хоть и не повлечет за собой поломку логики, но которая явно повлияет на эстетическое удовольствие.
Итак, запоминаем:
1. SwiftLint позволяет делать:
- Единый стиль с соблюдением стайлгайдов
Собственно все.
Статья могла бы здесь закончиться, но если бы это было так я бы вообще не начал бы писать эту статью. Интересно, что SwiftLint не позволяет делать — писать го… горячих фиксов код.
2. SwiftLint позволяет предотвращать:
- Force unwrap
- Strong delegates
- CyclomaticComplexity
- Что-то еще…
Думаю, неплохо быть застрахованным от таких ошибок, не правда ли? Для начинающего разработчика это особенно хорошо, так как он только учится и временами не подозревает о подобных ошибках.
3. Swiftlint можно расширять по собственным правилам.
Начну я с указания final для классов, которые не будут являться родительскими по отношению к другим. Благодаря final мы экономим время сборки проекта. Вот что говорит нам документация Apple про final классы:
Declarations with internal access (the default if nothing is declared) are only visible within the module where they are declared. Because Swift normally compiles the files that make up a module separately, the compiler cannot ascertain whether or not an internal declaration is overridden in a different file. However, if Whole Module Optimization is enabled, all of the module is compiled together at the same time. This allows the compiler to make inferences about the entire module together and infer final on declarations with internal if there are no visible overrides.
Решим мы такую невнимательность несложной регуляркой. Далее я буду писать сразу на ruby, чтобы вы могли вставлять код сразу в свой проект:
final_class:
included: ".*.swift"
name: "Final class requrement"
regex: '^class'
message: "All classes must be final or nonfinal"
saverity: error
Небольшой и достаточно простой пример. Я не буду писать что-то подобное на каждое правило, но исходный код будет в конце статьи.
class SomeClass { } // ошибка
internal class SomeClass { } // верно
/* @non-final */ class SomeClass { } // тоже верно
Следующий момент — это required init. Мы в компании не пользуемся сторибордами, поэтому указывать фатальный инициализатор в каждом UIView классе не совсем нормально. Для такого случая у нас есть собственный NLView (NL — NibLess) — класс, в котором required init реализован всего один раз. Новые разработчики на проекте могут этого не знать, но благо SwiftLint всегда поругается вместо ответственного лида. Или вместе с ним.
required_init:
regex: 'required init\?\(coder: NSCoder\)'
message: "Use NL class instead"
Если вы все еще пользуетесь сторибордами, вы можете использовать следующие правила, чтобы знать что все ваши проперти сторибордов приватные:
open_iboutlets:
included: ".*.swift"
name: "IBOutlet opening"
regex: '@IBOutlet ?(weak){0,1} var'
message: "IBOutlet should be private"
severity: error
open_ibaction:
included: ".*.swift"
name: "IBAction opening"
regex: '@IBAction func'
message: "IBAction should be private"
severity: error
Часто бывает, что Foundation используется там, где он совершенно не нужен. Поэтому лучше подсветить его всякий раз чтобы не забыть:
foundation_using:
included: ".*.swift"
regex: 'import Foundation'
message: "Do you really need for Foundation ???"
Надеюсь, все знают что print — достаточно тяжелая операция, которая может сильно навредить перфомансу приложения(особенно в циклах). Единственный вердикт — принтов не должно быть совсем.
print_using:
regex: 'print'
message: "Print decrease performance of the app"
severity: error
Также не следует создавать class only протоколы, так как есть вероятность, что такой синтаксис скоро станет deprecated и этого не рекомендуют делать разработчики Swift.
class_protocol:
regex: ': class'
message: "Use Anyobject instead"
Ниже правило для проектов, в которых используется R.swift.
image_name_initialization:
included: ".*.swift"
name: "Image initialization without R.swift"
regex: 'UIImage\(named:[^)]+\)'
message: "Use R.image.name() or typealias of this instead"
severity: error
Эта лишь малая часть того, что я смог придумать, но в интернете примеров еще меньше. Всю мою «коллекцию» вы можете посмотреть на github.
Спасибо за внимание. Если вы тоже используете SwiftLint с кастомными правилами, то расскажите о них — буду рад обсудить возможные кейсы.
varton86
AnyObject. И структура всё равно не сможет конформить такой протокол. Из документации:
AnyObject can be used as the concrete type for an instance of any class, class type, or class-only protocol
alekseismirnov Автор
Да, не предусмотрел такое)