In developing for Apple platforms — particularly iOS — there are many arguments that are disputed with the same fervor as religion or politics. Storyboards are evil, or they’re the only way to write user interfaces. AutoLayout is evil, or it’s the only reasonable way to write modern UI code. SwiftUI is ready for production, or it’s merely a new/shiny distraction from real code. All Swift files should be linted, or only overbearing technical leads bother with linting.
Today, I’d like to dip my toe into the pool by discussing linting. Linters are tools that look at your source code and ensure that very obvious errors are not made, and that a style guide is being followed. As a silly example, both of these pieces of Swift code are valid:
struct Person {
var id: Int? = nil
}
struct Person {
var id: Int?
}
A linter would have an opinion about the above. It may encourage you to use
the bottom version — var id: Int?
— because the explicit
initialization of nil
is redundant. By default, an Optional
will carry
the default value of nil
, implicitly.
SwiftLint
In my experience, the first time I really ran into a linter was once I started
doing Swift development full-time in 2018. The team I was on dabbled lightly
in using SwiftLint, the de facto standard linter for Swift
projects. The tough thing about swiftlint
is that it has a lot of rules
available — over 200 as I write this. Many of those rules are…
particular. It’s very easy to end up with a very opinionated set of
rules that are trying to change your code into something unfamiliar.
Trust me when I say some of these rules are quite a lot to swallow. One of
my absolute favorite
rules is
trailing_whitespace
, which enforces absolutely no whitespace at the
end of a line of code. 🙄
Even if you want to embrace SwiftLint in your project, you then needed to parse through 200+ rules in order to figure out what they are, whether or not they’re useful, and how many times your own existing code violates each one. No thank you.
swiftlint-autodetect
Enter the new project swiftlint-autodetect
by professional grump
(but actually good guy) Jonathan Wight. This project — as with
all clever ideas — is brilliant in its simplicity. When run against
an existing codebase, it will run SwiftLint against all rules, and then
figures out which ones are not violated at all. These rules that your
code is already passing are then output as a ready-to-use SwiftLint
configuration file.
swiftlint-autodetect generate /path/to/project/directory
The generated file will have all currently known SwiftLint rules included, but the ones where violations would occur are commented out, so they are ignored by SwiftLint. Using this file, you can integrate SwiftLint into your build process, painlessly, without having to change your code to meet some weird-ass esoteric linting requirement. 😗 👌🏻
Increasing Coverage
I’m very nearly ready to release a new project, and I’m doing some cleanup
and refactoring to get ready for its release. I decided to add SwiftLint
support using swiftlint-autodetect
, but then I wanted to investigate what
SwiftLint rules I was violating, but perhaps shouldn’t be.
Conveniently, swiftlint-autodetect
has another trick up its sleeve: it can
also output a count of the number of violations for each rule. Additionally,
it will mark with an *
which rules you can instruct SwiftLint to fix
automatically using swiftlint --fix
. That makes it easy to start at the bottom
of the resulting list, where the counts are low, and use that as a guide to
slowly layer on more and more SwiftLint rules, as appropriate.
swiftlint-autodetect count /path/to/project/directory
This is exactly what I’ve done: I started with the automatically generated
file, and then went up the list that count
generated to turn on rules that
seemed to be low-hanging fruit. Some I decided to leave disabled; some I
decided to enable and bring my code into compliance.
y tho
Thanks to the combination of these two subcommands on swiftlint-autodetect
,
I am now linting my source code before every build. I’ve fixed some
inconsistencies that I know would bother me over time. I’ve also found
a couple spots where taking a slightly different approach can help
improve performance/consistency.
Because — not despite — I’m an individual developer, I find it’s important to use the tools available to you to help you keep your code clean, correct, and working. Though I don’t deploy every tool under the sun, I do think having some combination of CI, unit testing, and linting is a very great way to use computers as a bit of parachute that, normally, your peer developers would provide.