SwiftLint - отличный инструмент с открытым исходным кодом, который упрощает соблюдение стиля и соглашений Swift. Это также помогает выявлять возможные ошибки на раннем этапе, выделяя проблемное использование. Вы можете запустить SwiftLint в своем проекте Xcode, чтобы увидеть все исключения руководства по стилю в строках, где они возникают, и быстро исправить их. Мне очень помогло, когда Я перенес свой код с Objective-c на Swift.
Чтобы сделать SwiftLint еще более полезным для разработчиков Firebase, мы добавили в SwiftLint несколько новых экспериментальных правил Firebase. Эти правила будут отображать предупреждения об общих ошибках, которые могут привести к ошибкам при использовании Firebase SDK.
Не имея никаких знаний о разработке линтеров или правил, я погрузился в правила SwiftLint. SwiftLint разработан для обеспечения соблюдения соглашений о стилях. Таким образом, большинство его правил проверяют наличие определенных ключевых слов или символов. И где они есть. Но в правилах Firebase у меня была другая цель. Правильные вызовы методов должны находиться в правильных файлах в рамках правильных функций. Например правила FirebaseCore. Мы хотим убедиться, что вызывается FirebaseApp.configure (). И он вызывается внутри функции application: didFinishLaunchingWithOptions: UIApplicationDelegate.
Моя первоначальная попытка заключалась в выполнении сложной проверки регулярного выражения. Чтобы найти конкретную функцию класса, вызовите метод Firebase и посмотрите, находятся ли они на одной границе. Это оказалось довольно неэффективным и остановило Xcode.
Я просмотрел вспомогательную функцию SwiftLint и обнаружил, что уже существует функция, выполняющая это за меня.
func match(pattern: String, range: NSRange? = nil, excludingSyntaxKinds: [SyntaxKind], excludingPattern: String, exclusionMapping: MatchMapping = { $0.range })->[NSRange]
Это обнаруживает, возникает ли шаблон, а другой - нет. Так я наконец начал видеть результаты и заставлял правила работать.
Я воспринял это как первую попытку запроса на вытягивание. Но я обнаружил, что это приведет к слишком большому количеству ложных срабатываний. Поскольку я выполнял базовую проверку регулярного выражения вместо реальной проверки синтаксиса. Это заставило меня глубоко погрузиться в SwiftLint, чтобы понять, как он на самом деле выполняет проверку синтаксиса.
Под SwiftLint есть очаровательный маленький фреймворк под названием SourceKitten. Он взаимодействует с SourceKit для анализа Swift AST. Когда у вас есть AST, вы можете выполнить правильную проверку синтаксиса.
{ "key.substructure" : [ { "key.kind" : "source.lang.swift.decl.struct", "key.offset" : 0, "key.nameoffset" : 7, "key.namelength" : 1, "key.bodyoffset" : 10, "key.bodylength" : 13, "key.length" : 24, "key.substructure" : [ { "key.kind" : "source.lang.swift.decl.function.method.instance", "key.offset" : 11, "key.nameoffset" : 16, "key.namelength" : 3, "key.bodyoffset" : 21, "key.bodylength" : 0, "key.length" : 11, "key.substructure" : [ ], "key.name" : "b()" } ], "key.name" : "A" } ], "key.offset" : 0, "key.diagnostic_stage" : "source.diagnostic.stage.swift.parse", "key.length" : 24 }
Таким образом, я вернулся к быстрому запуску Firebase, распечатал их Swift AST из интерфейса командной строки этого инструмента. Я использовал SwiftExpressionKind и SwiftDeclarationKind от SourceKitten. Затем повторил через AST. Таким образом, у меня наконец были твердые правила. Они проверяли точные функции и типы синтаксиса в конкретных файлах. Я понял, что доступных вспомогательных рекурсивных функций для моего случая недостаточно. Поэтому я расширил их для себя, чтобы достичь этой цели.
public func validateRecursive(dictionary: [String: SourceKitRepresentable]) -> Bool { if validateBaseCase(dictionary: dictionary) { return true } for subDict in dictionary.substructure { if validateRecursive(dictionary: subDict) { return true } } return false }
После завершения юнит-тестов я вернулся с еще одним пиаром. На этот раз недостающей частью были примеры. Каждое правило SwiftLint поставляется с примерами срабатывания и без срабатывания. Это помогает сделать ваш код более читабельным. Но примеры также используются в модульных тестах. Приведя больше примеров, я проверил и ложные срабатывания, и ложноотрицательные.
public static let description = RuleDescription( identifier: "firebase_config_activate", name: "Firebase Config Activate", description: "Firebase Config should be activated.", nonTriggeringExamples: [ "remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) {" + " (status, error) -> Void in \n self.remoteConfig.activateFetched() \n }", "foo.fetch() { }", "foo.fetch(fromURL: URL) { }" ], triggeringExamples: [ "remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) {" + " (status, error) -> Void in \n }", "remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) {" + " (status, error) -> Void in \n foo.fetch() \n }" ] )
В настоящее время мы размещаем правила на нашей вилке в ветке firebase_rules. Наш предварительный двоичный файл содержит правила Firebase. Вы можете просто загрузить файл .pkg и дважды щелкнуть его для установки. Вы также можете собрать двоичный файл из исходного кода.
Поскольку правила являются обязательными, вам необходимо добавить файл .swiftlint.yml в ту же папку, что и исходные файлы Swift, содержащий следующий текст:
opt_in_rules: - firebase_config_activate - firebase_config_defaults - firebase_config_fetch - firebase_core - firebase_dynamiclinks_customschemeURL - firebase_dynamiclinks_schemeURL - firebase_dynamiclinks_universallink - firebase_invites
Затем просто запустите SwiftLint в своем проекте, как обычно.
Мы будем рады, если вы попробуете и отправите нам отзыв в Twitter с #FirebaseLinter. Вы также можете задавать вопросы о StackOverflow, используя теги firebase и swiftlint вместе.