├── .github └── ISSUE_TEMPLATE.md ├── PROCESS.md └── README.md /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ### Examples 8 | 9 | 24 | 25 | ### Rationale 26 | 27 | 38 | 39 | ### Enforcing the rule 40 | 41 | 46 | -------------------------------------------------------------------------------- /PROCESS.md: -------------------------------------------------------------------------------- 1 | # Process to add a new rule 2 | 3 | If you want to suggest an addition to the style guide, [create a new issue](https://github.com/wordpress-mobile/swift-style-guide/issues/new?labels=proposal) with the `proposal` label. 4 | It should include a good explanation, and examples of good/bad usage, unless the 5 | difference is obvious (e.g. tabs vs spaces). It should include other 6 | alternatives and an explanation of why this one is better, even if it’s just 7 | “it’s the one most commonly used”. Ideally, the body of the issue should be good 8 | enough to put in the style guide as-is if approved. Bonus points if the issue 9 | contains a custom rule for SwiftLint. 10 | 11 | Once an issue is created, we use Github reactions to gather interest. Issues 12 | with more :+1: get reviewed first. See [proposals sorted by vote](https://github.com/wordpress-mobile/swift-style-guide/issues?q=is%3Aissue+is%3Aopen+label%3Aproposal+sort%3Areactions-%2B1-desc) 13 | 14 | Anyone is free to leave a comment with their opinion at this point, or ask for clarifications, but there’s no review yet. 15 | 16 | When it’s time for a review, one or more issues are picked (depending on their 17 | expected controversy), and they’re up for review. Everyone is encouraged to take 18 | a look at the reviewed issues, and leave their opinion and vote. To create a new 19 | review, a Pull Request is created with the requested changes. 20 | 21 | There should be a voting scale: 22 | 23 | - :+1: :+1: I believe this is much better than the 24 | alternative 25 | - :+1: I prefer this one 26 | - :neutral_face: I don’t care 27 | - :-1: I prefer the alternative 28 | - :-1: :-1: I think the alternative is much better 29 | 30 | If you leave a vote in either end of the scale, it should come with a good 31 | explanation. 32 | 33 | When the review period is over, the votes and arguments are taken into account 34 | and a decision is made. The new rule gets added to the guide and, if possible, a 35 | new rule is added to the linter. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Swift Style Guide for WordPress Mobile apps 2 | 3 | So you want to write some Swift code for WordPress apps. That’s nice, thanks a 4 | lot. But before that take some minutes to read some tips that will probably make 5 | everyone’s life easier. 6 | 7 | First, read the [Swift API Design 8 | Guidelines](https://swift.org/documentation/api-design-guidelines/) carefully. 9 | It’s not a long document and it establishes a common ground for what good Swift 10 | code looks like. 11 | 12 | Our approach to building a style guide is to do it incrementally and reach 13 | consensus. You can read more about the [process](PROCESS.md). 14 | 15 | When there is not a rule for something or there’s ambiguity, you’re encouraged 16 | to create a new issue with a proposal. In the meantime, the [raywenderlich.com 17 | Swift Style Guide](https://github.com/raywenderlich/swift-style-guide/) is often 18 | a good place to look for reference, but it’s not our official guide. 19 | 20 | Besides keeping this document short, we’re trying to organize it in two 21 | sections: a _must read_ section for rules we can’t automatically enforce, and an 22 | _all rules_ section as a reference of every other approved rule. You are not 23 | required to read the _all rules_ section, as Xcode will tell you when you break 24 | those rules. 25 | 26 | ## Must read 27 | 28 | ### Fix all the warnings 29 | 30 | We’re using [SwiftLint](https://github.com/realm/SwiftLint) to enforce as many 31 | of our rules as we can, so trust the warnings. Don’t commit or merge code with 32 | warnings. Our `Release` builds turn warnings into errors, so if you merge a 33 | warning you will create a problem for the person doing the release later on. 34 | 35 | ### Deal with optionals early 36 | 37 | Don't allow optional parameters just to return when they're `nil`. 38 | 39 | ```swift 40 | // Correct: the return value has a chance to become non-optional. Reduced complexity. 41 | func process(image: UIImage) -> UIImage { 42 | // ...process the image... 43 | } 44 | let processedImage = optionalImage.map(process) 45 | 46 | // Wrong: the return value becomes an optional as well. Added complexity. 47 | func process(image: UIImage?) -> UIImage? { 48 | guard let image == image else { 49 | return nil 50 | } 51 | 52 | // ...process the image... 53 | } 54 | let processedImage = process(optionalImage) 55 | ``` 56 | 57 | ## All the rules 58 | 59 | ### Trailing new line 60 | 61 | Files should end in **one** empty new line. A missing new line at the end causes 62 | trouble with diffs when the last line changes. More than one empty line is 63 | unnecessary. 64 | 65 | ### Avoid trailing whitespace 66 | 67 | There’s no need for whitespace at the end of lines and it is often the cause for 68 | unnecessary noise in diffs. 69 | 70 | You can go to Xcode’s preferences for _Text Editing_ and make sure both 71 | _Automatically trim trailing whitespace_ and _Including whitespace-only lines 72 | are enabled_. Xcode misses some cases, but it should help a lot. 73 | 74 | ### Avoid semicolons 75 | 76 | Swift does not require a semicolon after each statement in your code. There’s no 77 | reason to add them. 78 | 79 | ### Colon spacing 80 | 81 | Colons always have no space on the left and one space on the right when 82 | specifying a type. 83 | 84 | ```swift 85 | // Correct 86 | let name: String 87 | struct MyType: MyProtocol {} 88 | func prompt(t: T) where T: Confirmable {} 89 | 90 | // Wrong 91 | let name : String 92 | struct MyType : MyProtocol {} 93 | func prompt(t: T) where T : Confirmable {} 94 | ``` 95 | 96 | ### Braces 97 | 98 | Opening braces should be preceded by a single space and on the same line as the declaration. The exception to this rule is when enclosed within parentheses: no preceding space is required. 99 | 100 | ``` swift 101 | // Correct 102 | if let myString = myString { 103 | print(myString) 104 | } 105 | 106 | struct Foo { 107 | let bar: String 108 | } 109 | 110 | foo.map({ print($0) }) 111 | 112 | // Wrong 113 | if let myString = myString 114 | { 115 | print(myString) 116 | } 117 | 118 | struct Foo 119 | { 120 | let bar: String 121 | } 122 | ``` 123 | 124 | Closing braces should always be placed on a new line regardless of the number of enclosed statements. 125 | 126 | ```swift 127 | // Correct 128 | guard condition else { 129 | return 130 | } 131 | 132 | // Correct 133 | if condition { 134 | 135 | } else { 136 | 137 | } 138 | 139 | // Wrong 140 | guard condition else { return } 141 | 142 | // Wrong 143 | if condition { } else { } 144 | ``` 145 | 146 | ### Commas 147 | 148 | There should be no space before a comma and a single one after any comma. 149 | 150 | **Preferred:** 151 | ```swift 152 | function print(name: String, surname: String) { 153 | ... 154 | } 155 | ``` 156 | 157 | **Not Preferred:** 158 | ```swift 159 | function print(name: String ,surname: String) { 160 | ... 161 | } 162 | ``` 163 | 164 | ## Parentheses 165 | 166 | Parentheses around conditionals, if, for, while, do statements, are not required and should be omitted. 167 | 168 | **Preferred:** 169 | ```swift 170 | if name == "Hello" { 171 | print("World") 172 | } 173 | ``` 174 | 175 | **Not Preferred:** 176 | ```swift 177 | if (name == "Hello") { 178 | print("World") 179 | } 180 | ``` 181 | 182 | ### Forced downcasts and unwrapping 183 | 184 | Avoid using `as!` to force a downcast, or `!` to force unwrapping. Use `as?` to attempt the cast, then deal with the failure case explicitly. 185 | 186 | ``` swift 187 | // Correct 188 | func process(someObject: Any) { 189 | guard let element = someObject as? Element else { 190 | // Optional error handling goes here 191 | return 192 | } 193 | process(element) 194 | } 195 | 196 | // Wrong 197 | func process(someObject: Any) { 198 | process(someObject as! Element) 199 | } 200 | ``` 201 | --------------------------------------------------------------------------------