Consistent Swift style in Xcode with SwiftLint
Code style can be a controversial topic and lead to some passionate debates between developers. The use of a tool to enforce a set of rules on style can be very helpful to avoid contentious arguments and ensure the code is consistent throughout a project. SwiftLint can easily be incorporated into Xcode projects to flag code style violations as either warnings or errors at compile time.
Integrate SwiftLint with Xcode
SwiftLint is available from GitHub and can be installed in a number of ways such as downloading a SwiftLint.pkg or using a HomeBrew command.
1brew install swiftlint
Once SwiftLint has been installed, it can be integrated with an Xcode project by
adding a run phase in the Build Phase of the primary app target. Select the +
button and "New Run Script Phase" and add the following script. The export statement
is needed on silicon Macs because Homebrew installs the binaries into the
/opt/homebrew/bin folder by default.
1export PATH="$PATH:/opt/homebrew/bin"
2if which swiftlint > /dev/null; then
3 swiftlint
4else
5 echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
6fi
Add run script phase to Integrate SwiftLint with Xcode
SwiftLint rule violation
The good news is that there are no SwiftLint default rule violations on a new project in Xcode. Once you discover SwiftLint, it is a good idea to immediately add it to every project from the start. Add a blank space after a Text view on a new iOS app. Now a warning is generated when the code is compiled.
1struct ContentView: View {
2 var body: some View {
3 VStack {
4 Text("Hello, world!")
5
6 Text("second line")
7
8 Spacer()
9 }
10 }
11}
This code violates the trailing_whitespace
rule, which is enabled by default.
1+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
2| identifier | opt-in | correctable | enabled in your config | kind | analyzer | configuration |
3+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
4| trailing_whitespace | no | yes | yes | style | no | warning, i... |
SwiftLint warning on extra blank space at the end of a line
SwiftLint Rules
There are over 200 rules included in SwiftLint, and the Swift community continues to
contribute more rules regularly. One way to see the SwiftLint rules is to run
swiftlint rules
command in Terminal. This will show the rules as well as a number
of attributes on the rules such as whether or not the rule is opt-in or correctable.
These are the default SwiftLint rules as of version 0.46.5.
1swiftlint rules
2+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
3| identifier | opt-in | correctable | enabled in your config | kind | analyzer | configuration |
4+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
5| anonymous_argument_in_multiline_closure | yes | no | no | idiomatic | no | warning |
6| anyobject_protocol | yes | yes | no | lint | no | warning |
7| array_init | yes | no | no | lint | no | warning |
8| attributes | yes | no | no | style | no | warning, a... |
9| balanced_xctest_lifecycle | yes | no | no | lint | no | warning |
10| block_based_kvo | no | no | yes | idiomatic | no | warning |
11| capture_variable | yes | no | no | lint | yes | warning |
12| class_delegate_protocol | no | no | yes | lint | no | warning |
13| closing_brace | no | yes | yes | style | no | warning |
14| closure_body_length | yes | no | no | metrics | no | warning: 2... |
15| closure_end_indentation | yes | yes | no | style | no | warning |
16| closure_parameter_position | no | no | yes | style | no | warning |
17| closure_spacing | yes | yes | no | style | no | warning |
18| collection_alignment | yes | no | no | style | no | warning, a... |
19| colon | no | yes | yes | style | no | warning, f... |
20| comma | no | yes | yes | style | no | warning |
21| comment_spacing | no | yes | yes | lint | no | warning |
22| compiler_protocol_init | no | no | yes | lint | no | warning |
23| computed_accessors_order | no | no | yes | style | no | warning, o... |
24| conditional_returns_on_newline | yes | no | no | style | no | warning, i... |
25| contains_over_filter_count | yes | no | no | performance | no | warning |
26| contains_over_filter_is_empty | yes | no | no | performance | no | warning |
27| contains_over_first_not_nil | yes | no | no | performance | no | warning |
28| contains_over_range_nil_comparison | yes | no | no | performance | no | warning |
29| control_statement | no | yes | yes | style | no | warning |
30| convenience_type | yes | no | no | idiomatic | no | warning |
31| custom_rules | no | no | no | style | no | user-defin... |
32| cyclomatic_complexity | no | no | yes | metrics | no | warning: 1... |
33| deployment_target | no | no | yes | lint | no | warning, i... |
34| discarded_notification_center_observer | yes | no | no | lint | no | warning |
35| discouraged_assert | yes | no | no | idiomatic | no | warning |
36| discouraged_direct_init | no | no | yes | lint | no | warning, t... |
37| discouraged_none_name | yes | no | no | idiomatic | no | warning |
38| discouraged_object_literal | yes | no | no | idiomatic | no | warning, i... |
39| discouraged_optional_boolean | yes | no | no | idiomatic | no | warning |
40| discouraged_optional_collection | yes | no | no | idiomatic | no | warning |
41| duplicate_enum_cases | no | no | yes | lint | no | error |
42| duplicate_imports | no | no | yes | idiomatic | no | warning |
43| duplicated_key_in_dictionary_literal | no | no | yes | lint | no | warning |
44| dynamic_inline | no | no | yes | lint | no | error |
45| empty_collection_literal | yes | no | no | performance | no | warning |
46| empty_count | yes | no | no | performance | no | error, onl... |
47| empty_enum_arguments | no | yes | yes | style | no | warning |
48| empty_parameters | no | yes | yes | style | no | warning |
49| empty_parentheses_with_trailing_closure | no | yes | yes | style | no | warning |
50| empty_string | yes | no | no | performance | no | warning |
51| empty_xctest_method | yes | no | no | lint | no | warning |
52| enum_case_associated_values_count | yes | no | no | metrics | no | warning: 5... |
53| expiring_todo | yes | no | no | lint | no | (approachi... |
54| explicit_acl | yes | no | no | idiomatic | no | warning |
55| explicit_enum_raw_value | yes | no | no | idiomatic | no | warning |
56| explicit_init | yes | yes | no | idiomatic | no | warning |
57| explicit_self | yes | yes | no | style | yes | warning |
58| explicit_top_level_acl | yes | no | no | idiomatic | no | warning |
59| explicit_type_interface | yes | no | no | idiomatic | no | warning, e... |
60| extension_access_modifier | yes | no | no | idiomatic | no | warning |
61| fallthrough | yes | no | no | idiomatic | no | warning |
62| fatal_error_message | yes | no | no | idiomatic | no | warning |
63| file_header | yes | no | no | style | no | warning, r... |
64| file_length | no | no | yes | metrics | no | warning: 4... |
65| file_name | yes | no | no | idiomatic | no | (severity)... |
66| file_name_no_space | yes | no | no | idiomatic | no | (severity)... |
67| file_types_order | yes | no | no | style | no | warning, o... |
68| first_where | yes | no | no | performance | no | warning |
69| flatmap_over_map_reduce | yes | no | no | performance | no | warning |
70| for_where | no | no | yes | idiomatic | no | warning |
71| force_cast | no | no | yes | idiomatic | no | error |
72| force_try | no | no | yes | idiomatic | no | error |
73| force_unwrapping | yes | no | no | idiomatic | no | warning |
74| function_body_length | no | no | yes | metrics | no | warning: 4... |
75| function_default_parameter_at_end | yes | no | no | idiomatic | no | warning |
76| function_parameter_count | no | no | yes | metrics | no | warning: 5... |
77| generic_type_name | no | no | yes | idiomatic | no | (min_lengt... |
78| ibinspectable_in_extension | yes | no | no | lint | no | warning |
79| identical_operands | yes | no | no | lint | no | warning |
80| identifier_name | no | no | yes | style | no | (min_lengt... |
81| implicit_getter | no | no | yes | style | no | warning |
82| implicit_return | yes | yes | no | style | no | warning, i... |
83| implicitly_unwrapped_optional | yes | no | no | idiomatic | no | warning, m... |
84| inclusive_language | no | no | yes | style | no | warning, a... |
85| indentation_width | yes | no | no | style | no | severity: ... |
86| inert_defer | no | no | yes | lint | no | warning |
87| is_disjoint | no | no | yes | idiomatic | no | warning |
88| joined_default_parameter | yes | yes | no | idiomatic | no | warning |
89| large_tuple | no | no | yes | metrics | no | warning: 2... |
90| last_where | yes | no | no | performance | no | warning |
91| leading_whitespace | no | yes | yes | style | no | warning |
92| legacy_cggeometry_functions | no | yes | yes | idiomatic | no | warning |
93| legacy_constant | no | yes | yes | idiomatic | no | warning |
94| legacy_constructor | no | yes | yes | idiomatic | no | warning |
95| legacy_hashing | no | no | yes | idiomatic | no | warning |
96| legacy_multiple | yes | no | no | idiomatic | no | warning |
97| legacy_nsgeometry_functions | no | yes | yes | idiomatic | no | warning |
98| legacy_objc_type | yes | no | no | idiomatic | no | warning |
99| legacy_random | yes | no | no | idiomatic | no | warning |
100| let_var_whitespace | yes | no | no | style | no | warning |
101| line_length | no | no | yes | metrics | no | warning: 1... |
102| literal_expression_end_indentation | yes | yes | no | style | no | warning |
103| lower_acl_than_parent | yes | no | no | lint | no | warning |
104| mark | no | yes | yes | lint | no | warning |
105| missing_docs | yes | no | no | lint | no | warning: o... |
106| modifier_order | yes | yes | no | style | no | warning, p... |
107| multiline_arguments | yes | no | no | style | no | warning, f... |
108| multiline_arguments_brackets | yes | no | no | style | no | warning |
109| multiline_function_chains | yes | no | no | style | no | warning |
110| multiline_literal_brackets | yes | no | no | style | no | warning |
111| multiline_parameters | yes | no | no | style | no | warning, a... |
112| multiline_parameters_brackets | yes | no | no | style | no | warning |
113| multiple_closures_with_trailing_closure | no | no | yes | style | no | warning |
114| nesting | no | no | yes | metrics | no | (type_leve... |
115| nimble_operator | yes | yes | no | idiomatic | no | warning |
116| no_extension_access_modifier | yes | no | no | idiomatic | no | error |
117| no_fallthrough_only | no | no | yes | idiomatic | no | warning |
118| no_grouping_extension | yes | no | no | idiomatic | no | warning |
119| no_space_in_method_call | no | yes | yes | style | no | warning |
120| notification_center_detachment | no | no | yes | lint | no | warning |
121| nslocalizedstring_key | yes | no | no | lint | no | warning |
122| nslocalizedstring_require_bundle | yes | no | no | lint | no | warning |
123| nsobject_prefer_isequal | no | no | yes | lint | no | warning |
124| number_separator | yes | yes | no | style | no | warning, m... |
125| object_literal | yes | no | no | idiomatic | no | warning, i... |
126| opening_brace | no | yes | yes | style | no | warning, a... |
127| operator_usage_whitespace | yes | yes | no | style | no | warning, l... |
128| operator_whitespace | no | no | yes | style | no | warning |
129| optional_enum_case_matching | yes | yes | no | style | no | warning |
130| orphaned_doc_comment | no | no | yes | lint | no | warning |
131| overridden_super_call | yes | no | no | lint | no | warning, e... |
132| override_in_extension | yes | no | no | lint | no | warning |
133| pattern_matching_keywords | yes | no | no | idiomatic | no | warning |
134| prefer_nimble | yes | no | no | idiomatic | no | warning |
135| prefer_self_in_static_references | yes | yes | no | style | no | N/A |
136| prefer_self_type_over_type_of_self | yes | yes | no | style | no | warning |
137| prefer_zero_over_explicit_init | yes | yes | no | idiomatic | no | warning |
138| prefixed_toplevel_constant | yes | no | no | style | no | warning, o... |
139| private_action | yes | no | no | lint | no | warning |
140| private_outlet | yes | no | no | lint | no | warning, a... |
141| private_over_fileprivate | no | yes | yes | idiomatic | no | warning, v... |
142| private_subject | yes | no | no | lint | no | warning |
143| private_unit_test | no | no | yes | lint | no | warning: X... |
144| prohibited_interface_builder | yes | no | no | lint | no | warning |
145| prohibited_super_call | yes | no | no | lint | no | warning, e... |
146| protocol_property_accessors_order | no | yes | yes | style | no | warning |
147| quick_discouraged_call | yes | no | no | lint | no | warning |
148| quick_discouraged_focused_test | yes | no | no | lint | no | warning |
149| quick_discouraged_pending_test | yes | no | no | lint | no | warning |
150| raw_value_for_camel_cased_codable_enum | yes | no | no | lint | no | warning |
151| reduce_boolean | no | no | yes | performance | no | warning |
152| reduce_into | yes | no | no | performance | no | warning |
153| redundant_discardable_let | no | yes | yes | style | no | warning |
154| redundant_nil_coalescing | yes | yes | no | idiomatic | no | warning |
155| redundant_objc_attribute | no | yes | yes | idiomatic | no | warning |
156| redundant_optional_initialization | no | yes | yes | idiomatic | no | warning |
157| redundant_set_access_control | no | no | yes | idiomatic | no | warning |
158| redundant_string_enum_value | no | no | yes | idiomatic | no | warning |
159| redundant_type_annotation | yes | yes | no | idiomatic | no | warning |
160| redundant_void_return | no | yes | yes | idiomatic | no | warning |
161| required_deinit | yes | no | no | lint | no | warning |
162| required_enum_case | yes | no | no | lint | no | No protoco... |
163| return_arrow_whitespace | no | yes | yes | style | no | warning |
164| self_in_property_initialization | no | no | yes | lint | no | warning |
165| shorthand_operator | no | no | yes | style | no | error |
166| single_test_class | yes | no | no | style | no | warning |
167| sorted_first_last | yes | no | no | performance | no | warning |
168| sorted_imports | yes | yes | no | style | no | warning |
169| statement_position | no | yes | yes | style | no | (statement... |
170| static_operator | yes | no | no | idiomatic | no | warning |
171| strict_fileprivate | yes | no | no | idiomatic | no | warning |
172| strong_iboutlet | yes | yes | no | lint | no | warning |
173| superfluous_disable_command | no | no | yes | lint | no | warning |
174| switch_case_alignment | no | no | yes | style | no | warning, i... |
175| switch_case_on_newline | yes | no | no | style | no | warning |
176| syntactic_sugar | no | yes | yes | idiomatic | no | warning |
177| test_case_accessibility | yes | yes | no | lint | no | warning, a... |
178| todo | no | no | yes | lint | no | warning |
179| toggle_bool | yes | yes | no | idiomatic | no | warning |
180| trailing_closure | yes | no | no | style | no | warning, o... |
181| trailing_comma | no | yes | yes | style | no | warning, m... |
182| trailing_newline | no | yes | yes | style | no | warning |
183| trailing_semicolon | no | yes | yes | idiomatic | no | warning |
184| trailing_whitespace | no | yes | yes | style | no | warning, i... |
185| type_body_length | no | no | yes | metrics | no | warning: 2... |
186| type_contents_order | yes | no | no | style | no | warning, o... |
187| type_name | no | no | yes | idiomatic | no | (min_lengt... |
188| unavailable_function | yes | no | no | idiomatic | no | warning |
189| unneeded_break_in_switch | no | no | yes | idiomatic | no | warning |
190| unneeded_parentheses_in_closure_argument | yes | yes | no | style | no | warning |
191| unowned_variable_capture | yes | no | no | lint | no | warning |
192| untyped_error_in_catch | yes | yes | no | idiomatic | no | warning |
193| unused_capture_list | no | no | yes | lint | no | warning |
194| unused_closure_parameter | no | yes | yes | lint | no | warning |
195| unused_control_flow_label | no | yes | yes | lint | no | warning |
196| unused_declaration | yes | no | no | lint | yes | severity: ... |
197| unused_enumerated | no | no | yes | idiomatic | no | warning |
198| unused_import | yes | yes | no | lint | yes | severity: ... |
199| unused_optional_binding | no | no | yes | style | no | warning, i... |
200| unused_setter_value | no | no | yes | lint | no | warning |
201| valid_ibinspectable | no | no | yes | lint | no | warning |
202| vertical_parameter_alignment | no | no | yes | style | no | warning |
203| vertical_parameter_alignment_on_call | yes | no | no | style | no | warning |
204| vertical_whitespace | no | yes | yes | style | no | warning, m... |
205| vertical_whitespace_between_cases | yes | yes | no | style | no | warning |
206| vertical_whitespace_closing_braces | yes | yes | no | style | no | N/A |
207| vertical_whitespace_opening_braces | yes | yes | no | style | no | N/A |
208| void_return | no | yes | yes | style | no | warning |
209| weak_delegate | yes | yes | no | lint | no | warning |
210| xct_specific_matcher | yes | no | no | idiomatic | no | warning |
211| xctfail_message | no | no | yes | idiomatic | no | warning |
212| yoda_condition | yes | no | no | lint | no | warning |
213+------------------------------------------+--------+-------------+------------------------+-------------+----------+---------------+
Run SwiftLint in Terminal
SwiftLint can be configured to run as pre-commit hook on a repository to ensure code
checked-in is consistent. It can also be run as a command in a Terminal simply by
running swiftlint
in the project directory. run swiftlint --help
for more
options.
1swiftlint --help
2OVERVIEW: A tool to enforce Swift style and conventions.
3
4USAGE: swiftlint <subcommand>
5
6OPTIONS:
7 --version Show the version.
8 -h, --help Show help information.
9
10SUBCOMMANDS:
11 analyze Run analysis rules
12 docs Open SwiftLint documentation website in the default web browser
13 generate-docs Generates markdown documentation for all rules
14 lint (default) Print lint warnings and errors
15 rules Display the list of rules and their identifiers
16 version Display the current version of SwiftLint
17
18 See 'swiftlint help <subcommand>' for detailed help.
Running swiftlint in a Terminal shows the same violation of the trailing_whitespace
rule in the HelloSwiftLintApp.
1swiftlint
2Linting Swift files in current working directory
3Linting 'HelloSwiftLintApp.swift' (1/2)
4Linting 'ContentView.swift' (2/2)
5...HelloSwiftLint/ContentView.swift:13:1: warning: Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
6Done linting! Found 1 violation, 0 serious in 2 files.
Automatically fix SwiftLint violations
It can be seen from the list of rules above that some of these can be automatically corrected. This is straight-forward for spacing rules - like the extra white space introduced above. Automatically fixing SwiftLint rule violations can be done wherever SwiftLint analysis can be done.
Running the swiftlint --fix
in a Terminal will fix all the SwiftLint violations
that can be fixed automatically.
1swiftlint --fix
2Correcting Swift files in current working directory
3Correcting 'HelloSwiftLintApp.swift' (1/2)
4Correcting 'ContentView.swift' (2/2)
5.../HelloSwiftLint/ContentView.swift:13:1 Corrected Trailing Whitespace
6Done inspecting 2 files for auto-correction!
Alternatively, the automatic fix can be incorporated into the Build Phase of the project in Xcode. Edit the "Run Script Phase" of the project to include fixing the SwiftLint violations. Now, adding a trailing space is automatically removed when the code is compiled in Xcode.
1export PATH="$PATH:/opt/homebrew/bin"
2if which swiftlint > /dev/null; then
3 swiftlint --fix && swiftlint
4else
5 echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
6fi
Integrate SwiftLint to automatically fix rule violations at compile time where possible
Manually fix SwiftLint rule violations
Not all rule violations can be fixed automatically. There are a number of options available to deal with warnings and errors generated by SwiftLint analysis. If there are only one or two issues, then the best approach is to fix them and move on.
Options to deal with SwiftLint rule violations:
1. Modify the code to be compliant with the SwiftLint rules
2. Add exception in code to ignore specific rule violations
3. Customise the SwiftLint rules for the project
4. Ignore the warnings - not a good option
Fix the issues is the best approach and this can be easily achieved when SwiftLint is incorporated into the project from the start.
Here is a sample of code I've written that loops through an array of vertices and
creates a path. Using enumerated method which yields the index number and the
item in the array. Using a single character n
for a variable causes a compile time
error and the use of only characters causes a warning.
1 for (n, pt) in vertices.enumerated() {
2 n == 0 ? path.move(to: pt) : path.addLine(to: pt)
3 }
These "identifier-name" violations cannot be fixed automatically. It can be tempting to create exceptions for these, but it will help, in the long run, to be more descriptive of the identifiers and make the code easier to maintain.
1 for (index, point) in vertices.enumerated() {
2 index == 0 ? path.move(to: point) : path.addLine(to: point)
3 }
I notice that the sample code from Apple on enumerated has similar issues!
SwiftLint identifier-name violations that cannot be fixed automatically
Exceptions to the rule
There are circumstances where the code needs to be compatible with some external API
or data source. The Open Weather API provides data in JSON format as described in
Read JSON with codeable in Swift. The weather data contains data in the main
section with identifier names containing underscores, such as feels_like
. The Swift
struct used to decode this JSON has to match the names in JSON, so the Swift code
yields compile time errors because of SwiftLint rule "identifier_name" violation.
Sample JSON weather data
1{
2 "coord": {
3 "lon": -122.08,
4 "lat": 37.39
5 },
6 "weather": [
7 {
8 "id": 800,
9 "main": "Clear",
10 "description": "clear sky",
11 "icon": "01d"
12 }
13 ],
14 "base": "stations",
15 "main": {
16 "temp": 9.4,
17 "feels_like": 8.71,
18 "temp_min": 7.22,
19 "temp_max": 11.11,
20 "pressure": 1023,
21 "humidity": 100,
22 "sea_level": 100
23 },
24 "visibility": 16093,
25 "wind": {
26 "speed": 1.5,
27 "deg": 350
28 },
29 "clouds": {
30 "all": 1
31 },
32 "dt": 1560350645,
33 "sys": {
34 "type": 1,
35 "id": 5122,
36 "message": 0.0139,
37 "country": "US",
38 "sunrise": 1560343627,
39 "sunset": 1560396563
40 },
41 "timezone": -25200,
42 "id": 420006353,
43 "name": "Mountain View",
44 "cod": 200
45}
1struct WeatherRawData: Codable {
2 var name: String
3 var timezone: Double
4 var weather: [WeatherData]
5 var main: MainData
6 var wind: WindData
7 var clouds: CloudsData
8
9 struct WeatherData: Codable {
10 var id: Double
11 var main: String
12 var description: String
13 var icon: String
14 }
15
16 struct MainData: Codable {
17 var temp: Double
18 var feels_like: Double
19 var temp_min: Double
20 var temp_max: Double
21 var pressure: Double
22 var humidity: Double
23 }
24
25 struct WindData: Codable {
26 var speed: Double
27 var deg: Double
28 }
29
30 struct CloudsData: Codable {
31 var all: Double
32 }
33}
SwiftLint identifier-name violations that cannot be cannot be changed
It is not necessary to throw everything out. In this case, it is possible to simply disable the SwiftLint rule before the offending code and re-enable the rule afterwards. Obviously, it would not be great if these enable/disable snippets were sprinkled thoughtout the code. This technique should be used sparingly, being the exception that proves the rule. If it is found that the same rule needs to be disable in a number of places, consider disabling the rule for the entire project.
1struct WeatherRawData: Codable {
2 var name: String
3 var timezone: Double
4 var weather: [WeatherData]
5 var main: MainData
6 var wind: WindData
7 var clouds: CloudsData
8
9 struct WeatherData: Codable {
10 var id: Double
11 var main: String
12 var description: String
13 var icon: String
14 }
15
16 // swiftlint:disable identifier_name
17 struct MainData: Codable {
18 var temp: Double
19 var feels_like: Double
20 var temp_min: Double
21 var temp_max: Double
22 var pressure: Double
23 var humidity: Double
24 }
25 // swiftlint:enable identifier_name
26
27 struct WindData: Codable {
28 var speed: Double
29 var deg: Double
30 }
31
32 struct CloudsData: Codable {
33 var all: Double
34 }
35}
Disable SwiftLint specific rules for a section of code
Don't rush to disable rules
There will occasionally be exceptions to the SwiftLint rules, but don't be too quick
to disable a rule. In the above example, there is a better way to map variable names
in Swift code to the JSON content using CodingKey. Rather than commenting out the
SwiftLint rules, use a swift property name feelsLike
and specify the alternative
value of feels_like
to match the JSON data.
1 // swiftlint:disable identifier_name
2 struct MainData: Codable {
3 var temp: Double
4 var feels_like: Double
5 var temp_min: Double
6 var temp_max: Double
7 var pressure: Double
8 var humidity: Double
9 }
10 // swiftlint:enable identifier_name
1struct MainData: Codable {
2 let temp: Double
3 let feelsLike: Double
4 let tempMin: Double
5 let tempMax: Double
6 let pressure: Double
7 let humidity: Double
8
9 enum CodingKeys: String, CodingKey {
10 case temp
11 case feelsLike = "feels_like"
12 case tempMin = "temp_min"
13 case tempMax = "temp_max"
14 case pressure
15 case humidity
16 }
17}
Use of CodingKeys to map JSON variables rather than disabling SwiftLint rules
Customise SwiftLint rules
The approach of "Fix all the rule violations" can be more difficult to do when SwiftLint is added to an existing project that show hundreds of issues. In this case it can be more appropriate to add a SwiftLint configuration to the project. This is a YAML file where it is possible to disable rules, list opt-in rules or limit rules to only rules in this file. In this way SwiftLint is endlessly customisable. More details are available on SwiftLint Configuration section.
An example of a warning is the presence of TODO
comments in the codebase. SwiftLint
will flag these as warnings as they are flags to places in the code that still have
work remaining.
TODO comments in the code default to compiler warnings by SwiftLint
There may be times when you want to merge in the current code and leave the TODO
comments in place, but it is also desired to have the code compile without warnings.
Each individual TODO
comment could have disable/enable placed before it or a
".swiftlint.yml" file could be added to disable this rule for the entire project.
The following .swiftlint.yml file added to the project now allows the project to
compile without a warning for the TODO
comment. The other SwiftLint rules are
unaffected.
1disabled_rules: # rule identifiers to exclude from running
2 - todo
1struct ContentView: View {
2
3 var body: some View {
4 VStack {
5 Text("Hello, world!")
6
7 // TODO: Remove this function when done
8
9 let a = 24
10 Text("second line \(a)")
11
12 Spacer()
13 }
14 }
15}
TODO comments not causing warning but other SwiftLint rules applied
The easiest way to get started with SwiftLint on an existing project is:
- Install SwiftLint
- Integrate SwiftLint into the Xcode project with a build phase script
- Compile to assess all the warnings and errors
- Add a ".swiftlint.yml" file with the rules with the highest count violations disabled
- Enable one rule at a time and fix the issues in the code
Conclusion
Use of SwiftLint is a must for any Swift development. It helps to avoid arguments over code style on a team and it helps create consistent code style in the codebase. In my case, it helps me from bad habits like creating single letter identifiers!
Adding SwiftLint to an existing codebase can be more work that adding to a new project as it can show hundreds of warnings and errors. SwiftLint can be adopted into and existing project by configuring the rules and turning on more rules over time.
The ability for SwiftLint to automatically fix rule violations is fantastic and the initial sight of hundreds of violations can be significantly addressed by fixing automatically - just ensure the code is checked into version control before making widespread automatic changes, so it is easy to reverse if something goes wrong.