├── .swiftlint.yml ├── LICENSE ├── README.md └── images ├── cuddled.png ├── metova.png └── uncuddled.png /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | # SwiftLint version 0.11.1 2 | 3 | disabled_rules: # rule identifiers to exclude from running 4 | - valid_docs 5 | - line_length 6 | - todo 7 | - trailing_whitespace 8 | - nesting 9 | opt_in_rules: # some rules are only opt-in 10 | - empty_count 11 | - force_unwrapping 12 | excluded: # paths to ignore during linting. Takes precedence over `included`. 13 | - Pods 14 | - Vendor 15 | - build 16 | 17 | 18 | # configurable rules can be customized from this configuration file 19 | # binary rules can set their severity level 20 | force_cast: error 21 | force_try: error 22 | force_unwrapping: error 23 | 24 | statement_position: 25 | statement_mode: uncuddled_else 26 | 27 | file_length: 28 | warning: 900 29 | variable_name: 30 | max_length: 31 | warning: 55 32 | error: 60 33 | min_length: 34 | error: 3 35 | excluded: 36 | - url 37 | type_name: 38 | max_length: 39 | warning: 55 40 | error: 60 41 | min_length: 42 | error: 3 43 | excluded: 44 | - iPhone 45 | - iPad 46 | type_body_length: 47 | warning: 300 48 | error: 900 49 | function_body_length: 50 | warning: 50 51 | error: 100 52 | function_parameter_count: 53 | warning: 6 54 | error: 8 55 | cyclomatic_complexity: 56 | warning: 12 57 | error: 20 58 | 59 | # custom rules follow. (here be regex dragons) 60 | 61 | custom_rules: 62 | empty_line_after_super: 63 | name: "Empty Line After Super" 64 | regex: "(^ *super\.[ a-zA-Z0-9=?.\(\)\{\}:,> 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Metova Swift Style Guide](images/metova.png) 2 | 3 | # [DEPRECATED] (Dec. 2022) 4 | Note that this repo is being archived due to lack of updates. There are many great alternatives today, including [Apple's](https://www.swift.org/documentation/api-design-guidelines/) and [Google's](https://google.github.io/swift/) design/styling guidelines. 5 | 6 | # Metova's Swift Style Guide 7 | 8 | This style guide is written primarily with the development of iOS and OS X applications using the Xcode IDE in mind. As such, many of the guidelines found within are based around consistency with Apple's frameworks & Xcode's default preferences. If you are developing with Swift using different frameworks, or a different IDE, you may find sections of this style guide to be less applicable. 9 | 10 | ## Table of Contents 11 | 12 | * [General](#general) 13 | * [Whitespace](#whitespace) 14 | * [Braces](#braces) 15 | * [Control Flow](#control-flow) 16 | * [Loops](#loops) 17 | * [Classes and Structures](#classes-and-structures) 18 | * [Avoid Explicit Use of Self](#avoid-explicit-use-of-self-except-where-required) 19 | * [Closures](#closures) 20 | * [Types](#types) 21 | * [Constants](#constants) 22 | * [Optionals](#optionals) 23 | * [Type Casting](#type-casting) 24 | * [Struct Initializers](#struct-initializers) 25 | * [Shorthand](#shorthand) 26 | * [Typealiasing](#typealiasing) 27 | * [Language](#language) 28 | * [Swiftlint](#swiftlint) 29 | * [Credits](#credits) 30 | 31 | ## General 32 | 33 | ### Whitespace 34 | 35 | Code should be indented four spaces per indentation level. Do not insert tab characters. 36 | 37 | Files should end with a new line. 38 | 39 | Calls to `super` should be followed by an empty line. 40 | 41 | The closing brace for `guard` statements should be followed by an empty line. 42 | 43 | --- 44 | 45 | ### Braces 46 | 47 | ##### Opening braces should be placed on the same line as the declaration they are encapsulating. 48 | 49 | *Preferred* 50 | ```swift 51 | if someCondition { 52 | // execute some code 53 | } 54 | ``` 55 | 56 | *Not Preferred* 57 | ```swift 58 | if someCondition 59 | { 60 | // execute some code 61 | } 62 | ``` 63 | 64 | Closing braces should always be on a new line by themselves, horizontally aligned with the left edge of the opening brace it is closing. 65 | 66 | *Rationale: Apple uses same line brackets in their code and Xcode autocomplete encourages it as well.* 67 | 68 | --- 69 | 70 | ### Control Flow 71 | 72 | ##### Omit unnecessary parenthesis around control flow statements. 73 | 74 | *Preferred:* 75 | 76 | ```swift 77 | while someCondition { 78 | // execute some code 79 | } 80 | ``` 81 | 82 | *Not Preferred:* 83 | 84 | ```swift 85 | while (someCondition) { 86 | // execute some code 87 | } 88 | ``` 89 | 90 | *Rationale: With braces required around bodies, the conditional part is perfectly clear without parenthesis and reads better.* 91 | 92 | --- 93 | 94 | ##### Prefer to `return` and `break` early. 95 | 96 | *Preferred:* 97 | 98 | ```swift 99 | guard shouldDoTheThing else { 100 | return 101 | } 102 | 103 | // Do the thing. 104 | ``` 105 | 106 | *Not Preferred:* 107 | 108 | ```swift 109 | if shouldDoTheThing { 110 | // Do the thing. 111 | } 112 | else { 113 | return 114 | } 115 | ``` 116 | 117 | *Rationale: This eliminates unnecessary nesting and makes the exit conditions clear up front.* 118 | 119 | --- 120 | 121 | ##### When using an early `return` or `break`, prefer `guard` to `if` statements. 122 | 123 | *Preferred:* 124 | 125 | ```swift 126 | guard shouldDoTheThing else { 127 | return 128 | } 129 | ``` 130 | 131 | *Not Preferred:* 132 | 133 | ```swift 134 | if !shouldDoTheThing { 135 | return 136 | } 137 | ``` 138 | 139 | *Rationale: The `guard` statement guarantees the early exit. If the scope isn't exited, it will generate compile-time errors. It is also easier for readers to identify as an early exit.* 140 | 141 | --- 142 | 143 | ##### Prefer using a `switch` statement over `else if` chains when dealing with enumerations. 144 | 145 | *Preferred:* 146 | 147 | ```swift 148 | switch someValue { 149 | case .foo: 150 | doFooThing() 151 | case .bar: 152 | doBarThing() 153 | } 154 | ``` 155 | 156 | *Not Preferred:* 157 | 158 | ```swift 159 | if someValue == MyEnum.foo { 160 | doFooThing() 161 | } 162 | else if someValue == MyEnum.bar { 163 | doBarThing() 164 | } 165 | ``` 166 | 167 | *Rationale: With a `switch` statement, we can more clearly distinguish whether all cases are handled. It is also more compact.* 168 | 169 | --- 170 | 171 | ##### Prefer switching on tuples than unnecessary levels of nesting. 172 | 173 | *Preferred:* 174 | 175 | ```swift 176 | switch (someValue, direction) { 177 | case (.foo, .left): 178 | doLeftThing() 179 | case (.foo, .right): 180 | doRightThing() 181 | case (.bar, .left): 182 | doBarThing() 183 | case (.bar, .right): 184 | break 185 | } 186 | ``` 187 | 188 | *Not Preferred:* 189 | 190 | ```swift 191 | switch someValue { 192 | case .foo: 193 | switch direction { 194 | case .left: 195 | doLeftThing() 196 | case .right: 197 | doRightThing() 198 | } 199 | case .bar: 200 | if direction == .left { 201 | doBarThing() 202 | } 203 | } 204 | ``` 205 | 206 | *Rationale: Switching on a tuple makes all of the possible conditions more clear from the start. It also becomes more compact and removes unnecessary levels of nesting.* 207 | 208 | --- 209 | 210 | ##### Prefer starting `else` and `catch` statements on a new line, under the closing brace for the previous statement. 211 | 212 | *Preferred:* 213 | 214 | ```swift 215 | do { 216 | try doTheThing() 217 | } 218 | catch let error { 219 | handle(error) 220 | } 221 | ``` 222 | 223 | *Not Preferred:* 224 | 225 | ```swift 226 | do { 227 | try doTheThing() 228 | } catch let error { 229 | handle(error) 230 | } 231 | ``` 232 | 233 | *Rationale: Putting these statements at the beginning of a new line helps find all of the associated statements. It also looks significantly better when collapsing code blocks in Xcode.* 234 | 235 | ![Uncuddled](images/uncuddled.png) 236 | 237 | ![Cuddled](images/cuddled.png) 238 | 239 | --- 240 | 241 | ### Loops 242 | 243 | ##### Prefer `for`-`in` loops to `forEach` in most circumstances. 244 | 245 | *Preferred:* 246 | 247 | ```swift 248 | for thing in things { 249 | thing.doTheThing() 250 | } 251 | ``` 252 | 253 | *Not Preferred:* 254 | 255 | ```swift 256 | things.forEach { thing in 257 | thing.doTheThing() 258 | } 259 | ``` 260 | 261 | *Rationale: The preferred style reads more naturally.* 262 | 263 | --- 264 | 265 | ##### When dealing with optional collections, prefer `forEach` to unwrapping. 266 | 267 | *Preferred:* 268 | 269 | ```swift 270 | things?.forEach { 271 | thing.doTheThing() 272 | } 273 | ``` 274 | 275 | *Not Preferred:* 276 | 277 | ```swift 278 | if let things = things { 279 | for thing in things { 280 | thing.doTheThing() 281 | } 282 | } 283 | ``` 284 | 285 | *Rationale: While the `for`-`in` loop reads more naturally, using `forEach` to prevent unnecessary levels of nesting helps keep the overall code more readable.* 286 | 287 | --- 288 | 289 | ##### When the loop body is nothing but passing each item in the loop into a closure, function, or method, prefer `forEach`. 290 | 291 | *Preferred:* 292 | 293 | ```swift 294 | things.forEach(handleTheThing) 295 | ``` 296 | *Not Preferred:* 297 | 298 | ```swift 299 | for thing in things { 300 | handleTheThing(thing) 301 | } 302 | ``` 303 | 304 | *Rationale: This is more compact, and passing closure arguments into methods that expect closures should feel perfectly natural in Swift.* 305 | 306 | --- 307 | 308 | ##### Prefer iterating over an array slice to any other sort of logic to deal with a specific section of an array. 309 | 310 | *Preferred:* 311 | 312 | ```swift 313 | for thing in things[first...last] { 314 | thing.doTheThing() 315 | handleTheThing(thing) 316 | } 317 | ``` 318 | 319 | *Not Preferred:* 320 | 321 | ```swift 322 | for index in first.stride(through: last, by: 1) 323 | let thing = things[index] 324 | thing.doTheThing() 325 | handleTheThing(thing) 326 | } 327 | ``` 328 | 329 | *Rationale: In almost all cases, iterating over the array slice will be both more compact source code and more efficient.* 330 | 331 | --- 332 | 333 | ##### Prefer not to pull items out of an array index within a loop. If the `index` is needed, use the `enumerated()` method. 334 | 335 | *Preferred:* 336 | 337 | ```swift 338 | for (index, thing) in things.enumerated() { 339 | print("Found \(thing) at index \(index)") 340 | } 341 | ``` 342 | 343 | *Not Preferred:* 344 | 345 | ```swift 346 | for index in 0..