SwiftLint im Einsatz

Eine Einführung in SwiftLint mit Xcode für die Einhaltung von Coding Guidelines.

Heutzutage besitzt ein gut organisiertes Entwicklerteam Coding Guidelines, um das Code-Bild im Team kontinuierlich hoch und gleich zu halten. Ohne Coding Guidelines kann es dazu führen, dass durch unterschiedliche Konventionen und Stile sowie Erfahrungsniveaus der Entwickler eine Anwendung sehr schwer zu debuggen ist. Für neue Entwickler, die einem Team beitreten, kann es auch deshalb sehr schwer sein den Code zu verstehen.

Mit Coding Guidelines erzeugt das Team einen gut organisierten Code der wartbar und zugleich performant ist. Damit diese Coding Guidelines fortwährend von allen Teammit-gliedern eingehalten werden, gibt es Hilfsmittel wie zum Beispiel Code Reviews. Hier kann dann ggf. auf die Nichteinhaltung hingewiesen werden. Im Eifer des Gefechts passiert es dennoch häufig, dass den Coding Guidelines zu wenig Beachtung geschenkt wird. Bei einem Code Review kann dies zu Spannungen führen.

Je früher, desto besser: Linter als sinnvolles Frühwarnsystem

Besser ist es dann doch, wenn bereits dann auf die Nichteinhaltung der Coding Guidelines hingewiesen wird, wenn der Fehler gerade passiert. In der JavaScript Welt werden zu diesem Zweck bereits seit einigen Jahren Linter eingesetzt. Die deutsche Übersetzung für das Wort lint ist Fussel. Somit „entfusselt“ der Linter den Code. Auch für Swift gibt es solche Linter, die mit Unterstützung von Xcode auf die Nichteinhaltung von Coding Guidelines zur Entwicklungszeit hinweise.

In diesem Blogbeitrag möchte ich SwiftLint vorstellen und zeigen, wie es in ein Swift-Projekt eingebunden werden kann. Anhand von Beispielen in einer SwiftLint Konfigurationsdatei möchte ich zeigen, wie vordefinierte Regeln deaktiviert bzw. aktiviert werden können. Außerdem, wie man eigene Regeln, die für das Team wichtig sind, erstellt. Auch möchte ich kurz darauf eingehen, wie sinnvoll es ist, SwiftLint in bereits bestehenden Swift-Projekten zu verwenden und was man dabei beachten sollte.

Was ist SwiftLint und wo kommt es her?

SwiftLint ist ein Werkzeug mit dessen Hilfe die Swift-Styles, Swift-Konventionen und API Design Guidelines eingehalten werden sollen. SwiftLint kommt mit vordefinierten Regeln, die sich bereits an die genannten Guidelines halten. Hinter SwiftLint steht das Unternehmen Realm Inc welches auch andere OpenSource-Projekte unterstützt. Zum Zeitpunkt der Veröffentlichung des Blogbeitrages existiert bereits eine Sammlung von 183 Regeln, welche fortlaufendergänzt werden. Es gibt drei Arten von Regeln:

  • Default Rules: Diese sind bereits bei der Verwendung von SwiftLint aktiviert und können bei Bedarf deaktiviert oder individuell konfiguriert werden. Aktuell gibt es davon 74 Regeln.
  • Opt-In Rules: Diese können optional aktiviert und ggf. konfiguriert werden. Aktuell gibt es davon 109 Regeln.
  • Custom Rules: Teameigene Regeln

Installation von SwiftLint

SwiftLint kann auf verschiedenen Wegen installiert werden:

  • über Homebrew
  • per CocoaPods
  • per Mint
  • per Installationspaket (.pkg)

Über den Swift Package Manager steht aktuell SwiftLint noch nicht zur Verfügung.

In diesem Blogbeitrag verwende ich CocoaPods, da die meisten Swift-Projekte bereits über ein Podfile verfügen und mit einem Eintrag leicht zum Projekt hinzugefügt werden könne

pod 'SwiftLint'

Nachdem die Zeile in dem Podfile hinzugefügt wurde muss mit dem Pod-Befehl „pod install“ SwiftLint installiert werden.

Damit SwiftLint uns zum Entwicklungszeitpunkt unterstützt und bei der Code-Erstellung auf Regelverstöße in der IDE hinweist, muss im Target unter „Build Phase“ ein „Run Script“ hinzugefügt werden:

"${PODS_ROOT}/SwiftLint/swiftlint"

Der oben gezeigte „Run Script“ ist nur in Verbindung mit CocoaPods zu verwenden! Für die anderen Installationsvarianten muss folgender „Run Script“ verwendet werden:

if which swiftlint >/dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed, download from github.com/realm/SwiftLint"
fi

Damit ist die Installation abgeschlossen und SwiftLint unterstützt uns nun dabei, mit jedem Build (Shortcut: CMD + B) die in SwiftLint bereits standardisiert aktivierten Regeln zu beachten.

Die Konfiguration von SwiftLint

Schauen wir uns jetzt die Konfiguration von SwiftLint an. Um den Linter zu konfigurieren, benötigen wir im Projektverzeichnis eine Konfigurationsdatei mit dem Namen „.swiftlint.yml“. In dieser Datei können wir folgende Konfigurationen durchführen:

  • disabled_rules: Hier können wir standardmäßig aktivierte Regeln wieder deaktivieren.
  • opt_in_rules: Nicht standardmäßig aktivierte optionale Regeln, sogenannte Opt-In-Rules, können unter diesem Punkt aktiviert werden.
  • whitelist_rules: Wenn diese Konfiguration verwendet wird, dürfen disabled_rules und opt_in_rules nicht zeitgleich verwendet werden. Nur in dieser Liste aufgeführte Regeln werden verwendet.
  • included: Der Linter prüft alle Swift-Dateien die sich im Projektverzeichnis und dessen Unterverzeichnissen befinden. Mit der included können weitere Verzeichnisse zur Prüfung eingetragen werden.
  • excluded: Im Gegensatz zu included können auch Verzeichnisse von der Prüfung ausgeschlossen werden. Ich empfehle hier zum Beispiel das Pods-Verzeichnis einzutragen.
  • analyzer_rules: Diese Konfiguration wird nur bei „Analyze“ verwendet. Alle hier aufgelisteten Regeln werden dann für die Analyse des Codes verwendet. Dies ist momentan aber nur experimentell!
  • Ebenfalls können die in dieser Konfigurationsdatei aktivierten Regeln individuell angepasst werden. Wie dies funktioniert zeige ich weiter unten.
  • Zu guter Letzt sind da noch die angesprochenen Custom Rules,,, die in der Konfigurationsdatei von uns definiert werden. Auch hier werde ich weiter unten ein Beispiel zeigen.

Hier ein Beispiel wie eine SwiftLint Konfigurationsdatei aussehen kann:

disabled_rules:
  - colon
  - comma
  - control_statement

opt_in_rules:
  - empty_count

included:
  - Source

excluded:
  - Carthage
  - Pods
  - Source/ExcludedFolder
  - Source/ExcludedFile.swift
  - Source/*/ExcludedFile.swift

analyzer_rules:
  - explicit_self

...
ANMERKUNG: In der SwiftLint Konfigurationsdatei kann auf Xcode Umgebungsvariablen folgendermaßen zugegriffen werden:

${SOME_VARIABLE}.

SwiftLint Regeln individuell anpassen

Die SwiftLint Regeln können an die Teambedürfnisse angepasst werden. Was genau bei einer Regel angepasst werden kann, findet man in der Dokumentation der einzelnen Regeln.

Bild: Dokumentation am Beispiel von Colon


In der Dokumentation gibt es zu jeder Regel Beispiele, wann eine Warnung bzw. ein Fehler ausgegeben wird. Die Dokumentation ist sehr ausführlich und es empfiehlt sich, jede einzelne Regel anzuschauen.

Nachfolgend zeige ich einige Beispiele wie die Regeln angepasst werden können:
...

force_cast: warning  # Die Regel nur als Warnung markieren

force_try:
  severity: warning  # Die Regel nur als Warnung markieren

line_length: 110

type_body_length:
  warning: 300  # Mehr als 300 Zeilen für eine Typ-Definition -> Warnung angezeigen
  error: 400  # Mehr als 400 Zeilen für eine Typ-Definition -> Fehler anzeigen

file_length:
  warning: 500
  error: 1200

type_name:
  min_length:
    warning: 4
  max_length:
    warning: 40
    error: 50
  excluded: iPhone
  allowed_symbols: ["_"]  # Liste der Zeichen die in einem Typ-Bezeichner verwendet werden dürfen

identifier_name:
  min_length:
    error: 4
  excluded:
    - id  # Die Regel gilt nicht für Bezeichner
    - URL  # Die Regel gilt nicht für URL's

...

Verwendung von Custom Rules

Die Custom Rules werden ebenfalls in der SwiftLint Konfigurationsdatei definiert. Die Regeln werden Regex-basiert beschrieben:
...

custom_rules: #1
  swift_beat_objC: #2
    included: ".*\\.swift" #3
    excluded: ".*Test\\.swift" #4
    name: "Swift Beat ObjC" #5
    regex: "([o,O]bj[c,C])" #6
    match_kinds: #7
      - comment
      - identifier
    message: "Swift are better than ObjC." #8
    severity: error #9

...

Was genau die einzelnen Zeilen bedeuten, möchte ich hier einmal beschreiben:

  1. Unterhalb von custom_rules werden eigene Regeln definiert.
  2. Die Regel bekommt einen eindeutigen Identifier (Bezeichner).
  3. Regex-basierte Definition für einen Pfad, der zusätzlich geprüft werden soll.
  4. Auch hier eine Regex-basierte Definition für einen Pfad, der nicht in die Linter-Prüfung einbezogen werden soll.
  5. Die Regel bekommt einen Namen.
  6. Das Regex-basierte Match-Pattern das für die Regel verwendet werden soll.
  7. Mit match_kinds kann man spezifizieren, in welchen Syntax-Elementen die definierte Regex in regex angewendet werden soll. Eine Liste von möglichen Syntax-Elementen sind:
  • argument
  • attribute.builtin
  • attribute.id
  • buildconfig.id
  • buildconfig.keyword
  • comment
  • comment.mark
  • comment.url
  • doccomment
  • doccomment.field
  • identifier
  • keyword
  • number
  • objectliteral
  • parameter
  • placeholder
  • string
  • typeidentifier

8. Mit message kann die Nachricht angegeben werden, die in Xcode angezeigt werden soll, wenn die Regel missachtet wurde.
9. severity gibt an, ob die Regelverletzung als Warnung oder als Fehler markiert werden soll.

Das Verhalten der im oben gezeigte Custmer Rule sieht in Xcode an einem Code-Beispiel so aus:

Bild: Die Ausgabe der swift_beat_ObjC Regel

SwiftLint und die Kommandozeile

Wie oben bereits im „Run Script“ zu sehen, wird der Linter über die Kommandozeile ausgeführt. Eine Liste von möglichen Befehlen kann wie üblich über den folgenden Befehl ausgegeben werden:

$ swiftlint help

Available commands:

  analyze         [Experimental] Run analysis rules
  autocorrect     Automatically correct warnings and errors
  generate-docs   Generates markdown documentation for all rules
  help            Display general or command-specific help
  lint            Print lint warnings and errors (default command)
  rules           Display the list of rules and their identifiers
  version         Display the current version of SwiftLint

SwiftLint unterstützt auch die Autokorrektur

SwiftLint unterstützt auch die Autokorrektur, bei der der Code automatisch korrigiert wird. Aber Vorsicht! Ich empfehle beimSchreiben von Code auf die Autokorrektur zu verzichten, da die Korrekturen direkt in der Datei auf der Festplatte und nicht in Xcode durchgeführt werden. Dadurch wird der Code unwiderruflich überschrieben und kann nicht mehr rückgängig gemacht werden. Es ist besser, die Korrekturen in Xcode manuell durchzuführen, damit die Änderungen ggf. in der IDE rückgängig gemacht werden können.

Sinnvoll ist die Autokorrektur bei bestehenden Swift-Projekten, bei denen das Tool zum Einsatz kommen soll. Oft besteht der bereits existierende Code die Coding Guidelines nicht, wodurch die entsprechenden Code-Stellen vom Linter automatisch korrigiert werden. Das manuelle Korrigieren ist bei größeren Projekten sehr aufwendig und kostenintensiv. Aber auch hier sollte man vorher sicherstellen, dass eine Sicherheitskopie der Dateien, zum Beispiel in einem Git-Repository, vorhanden ist, bevor die Autokorrektur verwendet wird.

SwiftLint zum Continuous Integration Prozess hinzufügen

Um sicherzustellen, dass der von allen Teammitgliedern geschriebene Code den Coding Guidelines entspricht, sollte SwiftLint zusätzlich in den Continuous Integration Prozess eingebunden werden. Dadurch wird kontinuierlich der eingereichte Code auf das Einhalten der festgelegten Coding Guidelines geprüft und ggf. als Warnung oder Fehler ausgegeben. Auch lässt sich SwiftLint durch die Kommando-Befehle in fastlane integrieren:

swiftlint(
  # SwiftLint mode: :lint (default) or :autocorrect
  mode: :lint,

  # Specify path to lint (optional)     
  path: "/path/to/lint",
 
  # The path of the output file (optional)
  output_file: "swiftlint.result.json",

  # The path of the configuration file (optional)
  config_file: ".swiftlint-ci.yml",

  # List of files to process (optional)
  files: [
    "AppDelegate.swift",
    "path/to/project/Model.swift"
  ],

  # Allow fastlane to raise an error if swiftlint fails
  raise_if_swiftlint_error: true,  

  # Allow fastlane to continue even if SwiftLint returns a non-zero exit status
  ignore_exit_status: true    
)
Quelle: https://docs.fastlane.tools/actions/swiftlint/

Fazit

Durch die Verwendung von SwiftLint kann kontinuierlich und automatisch überprüft werden, ob der Code den im Team bestehenden Coding Guidelines entspricht. Ggf. werden dann Warnungen bzw. Fehler direkt in der IDE angezeigt oder im Continuous Integration Prozess ausgegeben. Ein Team kann sich an bereits existierenden Coding Guidelines bedienen oder eigene Regeln in der SwiftLint Konfigurationsdatei definieren. Einige namhafte Unternehmen haben ihre Rapid-Code-Style-Guidesfür die freie Verwendung in anderen Projekten bereitgestellt, an denen man sich orientieren kann:

  • Apple API-Designrichtlinien
  • Raywenderlich Swift Style Guidelines
  • Google's Swift Style Guidelines
  • LinkedIn's offizieller Swift Style Guidelines
  • Airbnb's Swift Style Guidelines

Inzwischen habe ich SwiftLint in mehrere Projekte eingebunden. Das ist durch die einfache Installation und der Möglichkeit der individuellen Anpassung der Regeln in SwiftLint sehr einfach und macht dieses Werkzeug sehr flexibel. Zudem können teameigene Regeln zu den Bestehenden hinzugefügt werden.

Das Feedback im Team  zu SwiftLint ist durchweg positiv. Das Lesen des Codes in einem Code Review ist nun einfacher und macht nun auch Spaß. Jedes Teammitglied, egal mit welchem Erfahrungsniveau, erzeugt mit Hilfe des Linters einen gut organisierten Code, der wartbar und zugleich performant ist.


Der Autor

Torben Erz

Torben Erz

iOS Entwickler