Code style is largely unimportant, which is why it’s crucial to let tools do the pedantic formatting checks and allow yourself to focus on the more important aspects of writing code and building apps! In this Swift Language User Group talk, JP Simard explains exactly how to set up SwiftLint in your project to give you just the semantics you want, and help it stay out of your way while being an ever-watchful protector of code cleanliness.
Introduction (0:00)
This talk is “Watch your language.” It’s about how to use the tool SwiftLint to help you write clearer code in general.
What is SwiftLint? (0:36)
SwiftLint is a linter. It’s a tool that will help you identify and flag parts of your code that may not be following the stylistic rules that either the community or your team has settled on.
There’s a lot of valid Swift code out there that doesn’t necessarily look idiomatic. Or it doesn’t look like the rest of the Swift code. This is really a tool to help you write consistent code.
The way you use it is, you add a build phase to your Xcode project, or you can run it from the command line. There are a bunch of plugins that have been written by SwiftLint users over the last few months for AppCode, Xcode, Vim, Sublime Text, Atom, and Emacs. There are all sorts of options of how to actually use it. It shows you visually exactly what parts of your code aren’t conforming to the style that you decided to stick with.
SwiftLint is built right into some of the tooling that Apple provides for Swift itself–tools like Source Kit and the Swift Compiler–so it will use that to make sure that it’s not parsing its own version of Swift. Which, as we know, is constantly changing in syntax and so if that were the case you’d have all of these edge cases where, for example, it thinks a code sample that you have in a comment is actually code, not just a comment, and so it might throw warnings in there. SwiftLint has a few smarts to be syntax aware.
For example, here we see a violation of one of these rules that’s in a string, but that doesn’t really matter. That’s what SwiftLint is in a nutshell. And my philosophy around SwiftLint, and linters in general, is that code style largely doesn’t matter. This might be a bit controversial, especially coming from someone who wrote a linter, but I really believe that we’re better off focusing on the functional aspects of the code. Does it compile? Does it run? Does it behave as expected in all inputs and outputs?
Style really just is a medium, more than an end in and of itself. You can write the cleanest, prettiest code in the world and if it does nothing or if it does the thing it wants to do wrong, then what’s the point? That’s where automation comes in. That’s where a tool comes in, where if we can have machines do the heavy lifting for us, then we’re left as programmers, as developers, as code reviewers, to really focus on the more important parts, such as does this code work as expected? Does this code have bugs?
One of the decisions that we made very early on was to not have a style guide within SwiftLint itself. We choose to build a tool that was built around some of the style conventions that were already established in other people’s style guides. Github’s Swift style guide was the mainstream one that people seemed to follow around the time that SwiftLint was originally built.
SwiftLint is really a tool to enforce style guides that are determined by the community. This has a nice side effect of outsourcing all the debates, outsourcing all of the contention and all of the holy flamewars that happen when you’re saying, Where should the comma go? Do you use semicolons or not? etc. The idea is to really enforce and encourage the use of stylistic conventions that are generally adhered to and decided by the community. That’s really what drives development of new rules and styles.
Reasons people use SwiftLint (5:37)
Earlier today I asked a number of people why they use SwiftLint. There are a number of reasons. One is so that their project–among all of the files that they have, all of the Swift code that they have–stays more or less consistent. This helps when you have new developers coming on board or when you’re reading old code. If everything is consistent, then your eyes aren’t distracted by, oh, this is slightly strange syntax compared to the rest of the file. You can really focus on the important parts. Consistency within a single project is pretty important.
Another reason is consistency between projects: between your own side projects, your company’s projects, or even the Swift community as a whole. This generally helps in the sense that if you’re familiar with what Swift looks and feels like in one project, if you’re following guidelines provided by the community, you’ll be able to go and visit another project and have a decent understanding of what things do and still have that nice feel that you’re used to for the language.
Consistency across teams is useful as well. Another reason that people brought up as one of their motivations for using SwiftLint was to help the beginners on their team or the beginner contributors to their projects conform to the existing style, but also to avoid some of the bad practices.
There’s readability. Another reason was to avoid style debates, like I mentioned. If a tool is automating these pedantic checks then you can really focus on the core functionality.
And finally, a few people answered that they have very specific code styles that they care very deeply about. For example, they want all of the arrays in their Swift files have a comma in the last element, so that when you add a new element you’re not changing two lines in a diff, and things like that.
Consistency seems to be the driving factor for why people uses SwiftLint and a linter in general. It’s not so much that people care about the “one true style,” the one true way of writing Swift; it’s really to just make sure that things are consistent, and all the advantages that that brings.
Rules (8:54)
Let’s dive into some of the mechanics of SwiftLint. As of today there are 60 rules that SwiftLint can enforce for you. Out of those 60 rules, 16 of those aren’t on by default and we call those opt-in rules: as a developer, as a SwiftLint user, you have to explicitly say, yes, I want to run this on my codebase and I know all of the gotchas about why it’s not on by default.
About a third of the rules are correctable, which means that if you want SwiftLint to automatically format and fix stylistic violations in your project, you can do that. Because of that deep integration with Source Kit and Swift Compiler that I mentioned earlier, you can be fairly certain that these corrections will be done in a safe way. They won’t mangle your code.
About a third of the rules are specifically configurable. For all the rules in SwiftLint, you can specify the severity in your configuration, and in 19 of these rules you can specify custom configuration. One of them, for example, is this mandatory trailing comma rule. Like I mentioned, some people like having a comma in the last element of their array or their dictionary to avoid two lines being diffed the next time they add an element. Some people would rather avoid that extra character. You can make stylistic choices in your project like that, embed it in your configuration, and essentially it’s a message to users that this is the style that you choose to adhere to for the project.
There are rules that I consider stylistic rules. These are rules that are mostly a matter of preference. They are rules like determining where to consistently place attributes like @objc
or @testable
. You can specify what kind of behavior you want your project to conform to. You have where to place closure parameters, whether they should be on the next line or on the same line as the opening brace. Preference for using syntactic sugar types like the array shorthand or the full explicit type. This trailing comma that I mentioned. Whether or not trailing closures with no parameters should have the empty parenthesis or not. Ensuring consistency on the stylistic level, there’s a number of rules that match that criteria.
Next up, we have hygienic rules. These are rules to generally make sure that your project isn’t exploding in scope or that you’re not massively overloading a single file with more than the responsibilities or the topics that it should be aware of. You have rules to constrain how long you want your files to be, how long type bodies should be as well. Encouraging the use of extensions if you’re going to be having multiple areas of responsibility within a single type or how long you want your lines to be, your functions et cetera. These are entirely configurable as well.
It’s really up to you, for your own project, to determine what the right level is or even to turn these on at all. If you just want to refer to human instinct and decision making, you’re perfectly free to do that. These are project hygienic rules.
There are rules that I call convention rules and these are probably styles that the Swift community or Apple has chosen to conform on. For example, recently Apple mentioned that for all of their Swift documentation they will generally use empty parentheses over void->
to represent a closure that has no parameters. Whereas they prefer ->void
over arrow-empty-parentheses to state that the return value is void.
Empty Parameters: Prefer () -> over Void ->
Void Return: Prefer -> Void over -> ()
Even though all of these syntaxes are correct and will compile and will lead to valid Swift code, this is again a convention that the Swift community and Apple have decided on. Same thing goes with using these legacy geometry or constant rules, like using the CGRect.infinite
static member rather than the CGRectInfinite
variable so that you’re preferring the scoped members.
There are all sorts of these conventions. Replacing the colon with the identifier rather than with the type. These are convention rules.
Next up we have code smells. These might be perfectly fine to use in your code, but if your code is littered with things like force try, force unwrap, and force cast, it’s probably an indication that you need to rethink some of your APIs. You might need to rethink how they’re exposed and maybe try to localize things that can fail, or mark more functions as throw, so that you can specify how you want to handle errors at the call site rather than at the implementation.
Generally code smell rules are perfectly fine to use in moderation, but if you overuse them it’s probably a bad sign. Same thing with the number of parameters that you have in a function. If you have functions with 20-plus parameters, maybe you should reconsider having a configuration object or struct that will contain a type safe representation of these parameters that should be passed in in a single cohesive block.
Cyclomatic complexity is the measure of what number of code paths a certain block of code can go through that will produce different results. If that cyclomatic complexity value is very high–if it’s more than 10, or something like 20 or 30, or God forbid like a hundred–it will be very difficult to understand and reason about the code. Again, not exactly a deal breaker; it’s perfectly fine to have them in your code, especially if you have things like enums which can have lots of members. Sometimes it’s perfectly legible to have an enum with a hundred members and have different code paths for each, but again here your judgment comes into play.
Nesting: avoiding deeply nested types or statements. If you have a “pyramid of doom” or you have if, if, if, if…; again a code smell. Sometimes it’s essential, but generally something to look at.
Other category rules fit, and this might actually be my favorite category of SwiftLint rules, which isn’t stylistic, it’s actually rules that are there to protect you from yourself. It’s code that will always generate valid Swift. Your code will compile, but it might be unperformant or it might lead to undefined behavior.
For example, one that was recently added was the dynamic inline rule. This is a rule that forbids the combination of using the @dynamic
attribute with the @inline(__always)
. And even though the Swift compiler accepts this as valid code, it’ll lead to undefined behavior in your compiled code which is often difficult to reason about.
Another example is the empty count rule: if you’re calculating the count on a massive collection, that can be an expensive operation. A lot of the Swift Standard Library collections have the isEmpty
member that you can check, that actually has a much more optimized version of the code that will check to see if there are any elements at all in the collection, which is generally much faster depending on the implementation.
There are some other rules that were recently added like, sometimes you can create an IBInspectable
that isn’t supported by Xcode or by Storyboards. Same thing with marking your unit tests and IB outlets as private. It will compile, but you’ll have a hard time. This is really my favorite category of rules because they can often teach us more about how Swift works, what doesn’t work about Swift and are an extension of the compiler. These are the kinds of rules that ideally would already be provided by the compiler.
Great rules (18:55)
Let’s go over some of the rules that I like. One of the examples that I’ve been referring to is this empty parameters rule.
// Triggers
let abc: Void -> Void = {}
func foo(completion: Void -> Void)
func foo(completion: Void throws -> Void)
let foo: Void -> () throws -> Void)
// Corrections
let abc: () -> Void = {}
func foo(completion: () -> Void)
func foo(completion: () throws -> Void)
let foo: () -> () throws -> Void)
Here I’ve divided the code samples into two blocks. The top ones are code samples that trigger this rule. And in the bottom ones are what you should be writing if you want to conform to the rule. If you run “SwiftLint autocorrect”, it will automatically find these violations and translate them to the official, stylistically approved format.
But even if you’re not using SwiftLint autocorrect, sometimes you can just refer to the SwiftLint rules command. That’s a command-line command that will show you a number of triggering examples and non-triggering examples. So if you don’t really understand a rule, you can just run that command and compare the examples. It’s self-documenting in that sense. Here we can see that even in some complex settings where you have curried types, so functions to functions if you will, SwiftLint can still handle that.
On the other side, we have the void return rule. And the same thing applies there where, if you wrote the code at the top, you run SwiftLint autocorrect, it will correct it to match and conform with the style.
// Triggers
let abc: () -> () = {}
func foo(completion: () -> ())
func foo(completion: () -> ( ))
let foo: (ConfigurationTests) -> () throws -> ())
// Corrections
let abc: () -> Void = {}
func foo(completion: () -> Void)
func foo(completion: () -> Void)
let foo: (ConfigurationTests) -> () throws -> Void)
Another neat one is this legacy CGGeometry rule.
// Triggers
CGRectIsInfinite( rect )
CGRectStandardize( rect)
CGRectIntegral(rect )
CGRectInset(rect, 5.0, -7.0)
CGRectOffset(rect, -2, 8.3)
CGRectUnion(rect1, rect2)
CGRectIntersection( rect1 ,rect2)
// Corrections
rect.isInfinite
rect.standardized
rect.integral
rect.insetBy(dx: 5.0, dy: -7.0)
rect.offsetBy(dx: -2, dy: 8.3)
rect1.union(rect2)
rect1.intersect(rect2)
Earlier I mentioned that the Swift community is generally standardized on using these Swift-ier types and constants and methods, rather than using the legacy CGGeometry
C-style functions. For example, all of the code above will automatically translate to the code underneath.
What’s really neat about this one is that, you know it’s not just a string replacement here. We’re identifying that there might be a number literal. If you look at the CGRectInset
example, you’ll see that we have three parameters and we need to reorder them with the function that’s being called, meanwhile also stripping that extra whitespace so that it can conform to the style that the Swift community is settled on.
Here’s another small example, if you have either inconsistent spacing or superfluous spacing:
// Triggers
[].map( { } )
// Corrections
[].map({ })
We can correct that if you opt-in to this rule. Those are just a handful of examples.
Not so great rules (21:57)
There are also some rules that aren’t so great for a number of reasons. We generally call these the opt-in rules. And these are rules that are off by default, that you as a SwiftLint user need to explicitly opt-in to in your SwiftLint configuration.
There are a number of reasons why we may have marked a rule as opt-in. One of them is that sometimes these rules have false positives. One of the examples here is the empty count rule. Remember earlier, I was mentioning that it’s typically much more efficient for some types of collections to check if they’re empty rather than to calculate their count and then compare that to zero. Well, the truth is that some of the collections don’t necessarily have an isEmpty
member.
For example, non-standard library collections or collections that are provided by third party frameworks sometimes. And so, in that case, rather than enabling this by default and causing a bunch of warnings that you really can’t do anything about in your code, we turn it off and we say, well, if you’re aware of the trade-offs of using this rule, it can be valuable if you know how it works. That’s where we don’t assume that you’ll want all of these false positives and we ask you that if you want to use this, that you opt-into it.
This is a rule that SwiftLint itself uses itself to lint itself. It’s a self-hosting linter, if you will. But we do have to disable this rule for a handful of places.
Another reason why we might not turn a rule on by default is if it’s too slow. Sometimes you need to do a lot of processing, spend a lot of compute cycles in order to determine if a rule is being triggered or not and so, sometimes we turn off these rules by default so you’re not complaining that SwiftLint is too slow. You can explicitly opt-in to this if you want.
Finally, for rules that are contentious or haven’t reached consensus, or are only useful if you’re using a specific library, those can also sometimes be opt-in so that we don’t slow down all of SwiftLint because we want to lint for you using the writeNimble
operator, rather than the function, if you’re not using Nimble at all.
Those are reasons why we would have some opt-in rules and we have 16, so just short of a third of them being opt-in.
SwiftLint has 60 rules but really it’s really extensible. We have this concept of custom rules and these are real-world, in-use custom rules that SwiftLint users have written themselves. This was found by searching for projects on Github that have a .swiftLint.yml
file, which is what we use to configure with the custom rules key that’s specified.
comments_space: # From https://github.com/brandenr/swiftlintconfig
name: "Space After Comment"
regex: "(^ *//\w+)"
message: "There should be a space after //"
severity: error
force_https: # From https://github.com/Twigz/Game
name: "Force HTTPS over HTTP"
regex: "((?i)http(?!s))"
match_kinds: string
message: "HTTPS should be favored over HTTP"
severity: warning
double_space: # From https://github.com/IBM-Swift/Package-Builder
include: "*.swift"
name: "Double space"
regex: "([a-z,A-Z] \s+)"
message: "Double space between keywords"
match_kinds: keyword
severity: warning
People have built some really cool stuff. For example, ensuring that there’s always a single space after the comment slashes, so that your comments are consistent across your code.
Another good one that I really like is this “force_https”, which will trigger a warning if you’re using an http
and not an https
link. You’ll notice that here, you can specify the match kinds and this is where SwiftLint’s integration with Source Kit really shines. Where you have a variable or you have a comment that has http://
, well that wouldn’t trigger. We only want to trigger if you’re specifying that in a string.
And then we have the same thing here which was used by IBM: this double space custom rule which only triggers if the match kinds are keywords. It will trigger if there’s more than one space between keywords.
This is only a small selection of the custom rules that I could find, and it doesn’t even count all of the closed source users of SwiftLint that have their own custom rules.
You configure SwiftLint by specifying a .swiftlint.yml
file, and if you do this, you can do things like disabling rules that you don’t want to use throughout your project.
disabled_rules: # rule identifiers to exclude from running
- colon
- comma
- control_statement
opt_in_rules: # some rules are only opt-in
- empty_count
- missing_docs
# Find all the available rules by running:
# swiftlint rules
included: # paths to include during linting. `--path` is ignored if present.
- Source
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Carthage
- Pods
- Source/ExcludedFolder
- Source/ExcludedFile.swift
You can opt-in to rules that are disabled by default. You can specify which paths that you want to lint. This is very useful if you’re using CocoaPods or Carthage and you don’t want to lint all of that third party code.
You can include your source directory or your source and your tests directory. You can exclude Carthage and Pods, etc. You can see more about the configuration options over at SwiftLint’s homepage on Github.
Commands. There are ways to control SwiftLint from within your Swift code.
/**** Regions ****/
// swiftlint:disable colon
let noWarning :String = ""
// swiftlint:enable colon
let hasWarning :String = ""
/**** Local ****/
// swiftlint:disable:next force_cast
let noWarning = NSNumber() as! Int
let hasWarning = NSNumber() as! Int
let noWarning2 = NSNumber() as! Int // swiftlint:disable:this force_cast
let noWarning3 = NSNumber() as! Int
// swiftlint:disable:previous force_cast
This is often very useful if you want to locally disable a rule. If you want to disable it for a whole file, just add this comment swiftlint:disable :
and it will disable the colon rule for that whole file.
Sometimes that’s useful, say for doing it in the whole file for file length, or if for whatever reason you need more than one trailing newline at the end of the file. You can disable it at the top level, or if you want to do it for a region of code, this works like Clean diagnostics where you can do swiftlint:disable,
the rule identifier, have your block of code where you do a whole number of unholy things, and then you then add another comment right underneath saying swiftlint:enable
that same rule again.
This is an easy way to mark off parts of your code as having slightly different configurations than the rest of your project. Often times rules will only trigger for a single line or for a single violation in the whole project. In that case you can localize where you want that command to apply.
For example, at the bottom of this code sample here, we have disabling the next line, the current line and the previous line. Sometimes this is useful without having matching enable and disable comments, really just to squash one warning.
How do you use this? There are all sorts of ways. You can use Xcode build phases so that you get that nice visual feedback right from Xcode every time you build your project and same thing goes with whatever your editor or IDE of choice is. The community has really been great. They provided all sorts of options there.
Another is if you’re using Travis for your continuous integration, all of their Mac OS X images come with SwiftLint enabled, and so does Xcode 8.21 that was just released. It comes with the latest version of SwiftLint, so it has all the latest rules, all the latest functionality.
If you want to use it with Jenkins, there’s all sorts of reporters that you can use in your SwiftLint configuration or in the command-line invocation where you can specify the Xcode format or output in JSON, HTML, checkstyle for XML, which there are all sorts of Jenkins plugins for. And you can write your own. If you want a custom format, you can use something like the HTML reporter and put an HTML report on an X3 and a static site every time you do a build and just link to it in a Github comment, so that you can have this nicely formatted report.
It’s also an option provided by Hound CI, which is the service run by the awesome folks over at ThoughtBot. This is a Github integration that will automatically comment in your PR to show all of the SwiftLint violations that you have. Another way to enforce your styles is, when you’re running locally, you can run the auto-correct and auto-format commands.
Here I’m running it in one of our projects over at Realm called Realm Tasks, which is a demo app that shows off the realm of a platform in real time synchronization for a to-do list app. And so in this case I’m running SwiftLint auto-correct from the root of the repo. It takes about a second to run and then afterwards, I’m showing the diff. And here, specifically, I ran this right after we’d added the void return rule so that all of these places where we weren’t conforming to the style, we can automatically adjust. Then I’m using kaleidoscope to show the diff, so you can see that it really just affected the parts that were appropriate.
Ways to use SwiftLint (32:10)
For the last part of this talk I want to go over different ways to use SwiftLint. There’s no right or wrong way, but there are some that are more right than others. After a year and a half of chatting with users and seeing how they use it I’ve named six prototypical SwiftLint users.
The first one I call the incremental code stylist. This usage pattern is, for a very large project, you start off with the bulk of SwiftLint’s rules disabled. That’s perfectly fine. Some style linting is better than none at all. And so you can start by doing SwiftLint integration, disabling all the rules if you want, but just getting it integrated into your infrastructure. And then you turn on the one that you think is most important. And then you fix whatever issues come up when that rule runs and then you commit that, you enable that rule. And then slowly but surely, as you have time to wade through your massive, hundred-thousand-line Swift project, you can slowly enable the rules that you care about.
You might not care that there’s a trailing semicolon here and there in your files, but you might care if you’re using the @dynamic
and @inline always
. And this is entirely configurable; it’s entirely up to you, no one’s going to cry if you’re not using some of SwiftLint’s fancy new rules. It’s designed to be a cafeteria. You go in there, you pick what you like and you use that. This is a great approach that I’ve seen, especially for larger teams or larger projects where you don’t want to refactor your entire code base just to please a linter.
Another prototypical use case that I’ve seen is the skeptic: someone who thinks they just shouldn’t be linting at all. If you don’t believe in linters, it’s perfectly fine to use a very tight subset of the rules that SwiftLint provides. Just pick the ones that you think are most important and add the most value to your project and just leave it at that. Again, no one’s going to be disappointed if you don’t constantly update your version of SwiftLint and constantly add support for the new rules.
The completionist. This one’s kind of fun. I’ve seen users say, oh, SwiftLint is great, everything that it says is the rule of law. I should enable all the rules. If the rules exist it must be for a reason. I lint every single file in my project, every single line of each file, I lint all day and everyday and everyone should lint all the time.
No. SwiftLint is a tool. It’s a tool that’s run by machines and machines are stupid and humans are smart. I’m over-generalizing here, but this is probably the one use case that I really advise against. Each rule in SwiftLint has its place, it has areas where it should be applied, shouldn’t be applied, should be bypassed and silenced, etc. Just because a rule exists, doesn’t mean you should use it.
The commander in chief. This is a fun one, and this is how I think I use SwiftLint mostly. And that’s to basically tailor different parts of your projects depending on the use case. If you have one file where you’ve tried to consolidate all of your force casts for example, or just disable force-casting in that file and add a comment saying why you think it’s safe to do so in that specific place. Basically use these comment commands liberally as you see fit. Obviously you don’t want to clutter your entire code base with all of these SwiftLint commands, you don’t want the linter to overwhelm the actual value that the linter provides. Use this liberally but judiciously.
Doing this a little is fine. If you’re constantly varying which rules in what capacity are applied to different parts of your project, you won’t get that consistency that we saw earlier on the talk, which is what most people are actually after when using a linter.
The vagabond. This is kind of a version of the skeptic. You don’t necessarily want to litter your whole code with all sorts of SwiftLint commands, which is fine. You really just want to occasionally do a sanity check and see if there are any glaring issues with your code. You can occasionally revisit running it one-off on your code base. This is great if you don’t want the burden and the churn of constantly having to think about style in your code. If you’re prototyping something and you need to move really quickly, you’re better off leaving linting as a cleanup step before you push up your pull requests or something like that.
You can either do this with SwiftLint as a whole or just for specific rules or for specific files in your project. Totally up to you. One of the usage patterns that I’d encourage for this use case is, say there’s a handful of rules that you want to run once a month, to just do a sanity check. For example, the empty count rule, where you don’t want to break CI if there’s a violation, but it might be worth investigating a tiny bit. In that case you can add a second SwiftLint configuration file that runs SwiftLint in hard mode, adds a bunch of different rules that you wouldn’t normally run and you can run these ad-hoc as you see fit. You can specify which configuration file you want to use in SwiftLint.
Finally, the last prototypical use case that I have here is the sniper. Say you’ve been bitten once by having @dynamic
and @inline always
and you really just want to prevent that one bug from ever creeping up in your project again, just enable that. Use SwiftLint as much or as little as you would like.
Tips from the trenches/Conclusion (39:07)
Some closing thoughts and tips. Don’t bend to the will of the machines. You’re a human, you have a brain; the machines do not. SwiftLint is not perfect, bug-free code itself. Use your judgment.
// Persist your data easily
let realm = try! Realm()
try! realm.write {
realm.add(mydog)
}
You really have to rely on your thought process to know when SwiftLint is wrong or when to ignore SwiftLint. And SwiftLint will be wrong. If SwiftLint was always right, that’d be the compiler’s job. These rules would be in the compiler. If something should never compile but it does, then I’d argue that’s a compiler bug. The role of a linter is to be a bit opinionated, to differentiate between two versions of valid Swift code and say, you might want to prefer this one over this other one.
Once again, don’t bend to the will of the machines. Feel free to disable rules. Feel free to use nested configurations to disable rules just in certain parts of your project; you can use .swiftlint.yml
files in sub-directories of your project if you want, and those configurations will be applied downstream. And feel free to disable rules in source code. That’s perfectly intended usage.
Finally, I want to thank all of the contributors to SwiftLint. We’ve had almost a hundred contributors for the last 18 months that the project’s been alive. I think it really is proof that it’s community driven and that we don’t have to wait for Apple to build tools. The community can get together and build it themselves. So thanks to everyone who has contributed, and if you see a rule you’d like to see or if you can think of one that you’d like to see in SwiftLint, you should really contribute. Thanks.
Receive news and updates from Realm straight to your inbox