├── .gitignore ├── LICENSE ├── README.md ├── SWIFTLINT.markdown ├── com.raywenderlich.swiftlint.yml └── screens ├── add-run-script.png ├── empty-run-script.png ├── indentation.png ├── project_settings.png ├── swiftlint-warning.png ├── trailing-whitespace.png └── xcode-jump-bar.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 SketchK 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kodeco 官方代码风格指南中文版[长期维护项目] 2 | 3 | ## 原文地址 4 | 5 | [英文原版在线版](https://github.com/raywenderlich/swift-style-guide) 6 | 7 | ## 更新记录 8 | 9 | * 2023.10.22 对翻译版本进行更新 10 | * 与原文 master 分支的 [ca4d811](https://github.com/kodecocodes/swift-style-guide/commit/ca4d811d50356f4708858c17c5bfff525f652e4c) 节点一致,2023.04.13 11 | * 2022.01.02 对翻译版本进行更新 12 | * 与原文 master 分支的 [3b85871](https://github.com/raywenderlich/swift-style-guide/commit/3b858711d49a39c15a8fd37482f1b3cfea8e29b4) 节点一致, 2022.01.02 13 | * 2021.09.09 对翻译版本进行更新 14 | * 与原文 master 分支的 [a9f6e8d](https://github.com/raywenderlich/swift-style-guide/commit/a9f6e8dd3200447bbc6f9d378fcb0c8a2b423d7f) 节点一致, 2021.09.07 15 | * 2020.03.09 对翻译版本进行更新 16 | * 与原文 master 分支的 [c01c0f9](https://github.com/raywenderlich/swift-style-guide/commit/c01c0f995e410f772860959adf1f777f207e9243) 节点一致, 2020.03.03 17 | * 2019.11.13 与原文对比无更新 18 | * 与原文 master 分支的 [127e671](https://github.com/raywenderlich/swift-style-guide/commit/127e6710db9965b76e4124916ebd96c0285f8ec3) 节点一致, 2019.09.05 19 | * 2019.09.16 与原文对比无更新 20 | * 2019.08.14 与原文对比无更新 21 | * 2019.07.14 校对完成 22 | * 2019.07.02 完成初稿 23 | * 与原文 master 分支的 [1c9e796](https://github.com/raywenderlich/swift-style-guide/commit/1c9e7967fc5b5b27bab5e25c1f3734d914c9e27e) 节点一致, 2019.04.27 24 | 25 | ## 贡献力量 26 | 27 | 如果想做出贡献的话,你可以: 28 | 29 | * 参与翻译 30 | * 帮忙校对,挑错别字、病句等等 31 | * 提出修改建议 32 | * 提出术语翻译建议 33 | 34 | ## 翻译建议 35 | 36 | 如果你有兴趣参与项目,请仔细阅读说明: 37 | 38 | 排版格式和流程说明: 39 | 40 | * 翻译排版格式要求参考 SwiftGG [排版指南](https://github.com/SwiftGGTeam/translation/blob/master/SwiftGG%20排版指南.md) 41 | * Pull Request 发起方式参考 SwiftGG [Pull Request 说明](https://github.com/SwiftGGTeam/translation/blob/master/%E7%BF%BB%E8%AF%91%E6%B5%81%E7%A8%8B%E6%A6%82%E8%BF%B0%E5%8F%8APR%E8%AF%B4%E6%98%8E.md#%E5%A6%82%E4%BD%95%E5%8F%91%E8%B5%B7-pull-request) 42 | 43 | 其他说明: 44 | 45 | * 相关术语请严格按照术语表来翻译,如果有问题可以发 Issue 大家一起讨论 46 | * 使用 Markdown 进行翻译,文件名必须使用英文 47 | * 翻译后的文档请放到 source 文件夹下的对应章节中,然后 Pull Request 即可,我们会用 GitBook 编译成网页 48 | * 有其他任何问题都欢迎发 Issue 49 | 50 | ### Updated for Swift 5 51 | 52 | This style guide is different from others you may see, because the focus is centered on readability for print and the web. We created this style guide to keep the code in our books, tutorials, and starter kits nice and consistent — even though we have many different authors working on the books. 53 | 54 | 这篇代码风格指南可能不同于你看到的其他代码风格指南。因为它倾向于保证出版物和网页内容的可读性。我们创建这篇代码风格指南的初衷是为了让我们的书、教程和初学者指南中的代码,即使在有很多作者同时协作的情况下,也能保持质量与风格统一。 55 | 56 | Our overarching goals are clarity, consistency and brevity, in that order. 57 | 58 | 总的来说,这份风格指南的目标依次是清晰、一致和简洁。 59 | 60 | ## 目录/Table of Contents 61 | 62 | * [正确性/Correctness](#正确性Correctness) 63 | * [使用 SwiftLint/Using SwiftLint](#使用-SwiftLintUsing-SwiftLint) 64 | * [命名/Naming](#命名Naming) 65 | * [在文章中正确引用代码/Prose](#在文章中引用代码Prose) 66 | * [代理/Delegates](#代理Delegates) 67 | * [使用上下文进行类型推断/Use Type Inferred Context](#使用上下文进行类型推断Use-Type-Inferred-Context) 68 | * [通用类型参数/Generics](#泛型Generics) 69 | * [类前缀/Class Prefixes](#类前缀Class-Prefixes) 70 | * [语言/Language](#语言Language) 71 | * [代码组织/Code Organization](#代码组织Code-Organization) 72 | * [协议一致性/Protocol Conformance](#协议一致性Protocol-Conformance) 73 | * [无用代码/Unused Code](#无效代码Unused-Code) 74 | * [简化引用/Minimal Imports](#简化引用Minimal-Imports) 75 | * [空格/Spacing](#空格Spacing) 76 | * [注释/Comments](#注释Comments) 77 | * [类与结构体/Classes and Structures](#类和结构体Classes-and-Structures) 78 | * [Self 的使用/Use of Self](#Self-的使用Use-of-Self) 79 | * [协议一致性/Protocol Conformance](#协议一致性Protocol-Conformance) 80 | * [计算型属性/Computed Properties](#计算属性Computed-Properties) 81 | * [Final 关键字/Final](#Final-关键字Final) 82 | * [函数声明/Function Declarations](#函数声明Function-Declarations) 83 | * [函数调用/Function Calls](#函数调用Function-Calls) 84 | * [闭包表达式/Closure Expressions](#闭包表达式Closure-Expressions) 85 | * [类型/Types](#类型Types) 86 | * [常量/Constants](#常量Constants) 87 | * [静态方法和可变类型属性/Static Methods and Variable Type Properties](#静态方法和可变类型属性Static-Methods-and-Variable-Type-Properties) 88 | * [可选类型/Optionals](#可选类型Optionals) 89 | * [延时初始化/Lazy Initialization](#延迟初始化Lazy-Initialization) 90 | * [类型推断/Type Inference](#类型推断Type-Inference) 91 | * [语法糖/Syntactic Sugar](#语法糖Syntactic-Sugar) 92 | * [函数与方法/Functions vs Methods](#函数-vs-方法Functions-vs-Methods) 93 | * [内存管理/Memory Management](#内存管理Memory-Management) 94 | * [延长对象生命周期/Extending Object Lifetime](#延长对象的生命周期Extending-object-lifetime) 95 | * [访问控制/Access Control](#访问控制Access-Control) 96 | * [控制流/Control Flow](#控制流Control-Flow) 97 | * [三元条件表达式/Ternary Operator](#三元条件表达式Ternary-Operator) 98 | * [黄金路径/Golden Path](#黄金路径Golden-Path) 99 | * [失败防护/Failing Guards](#失败防护Failing-Guards) 100 | * [分号/Semicolons](#分号Semicolons) 101 | * [括号/Parentheses](#括号Parentheses) 102 | * [多行字符串字面量/Multi-line String Literals](#多行字符串字面量Multi-line-String-Literals) 103 | * [不要使用 Emoji/No Emoji](#不要使用-EmojiNo-Emoji) 104 | * [不要使用 #imageLiteral 和 #colorLiteral](#不要使用-imageLiteral-和-colorLiteralNo-imageLiteral-or-colorLiteral) 105 | * [组织名称和包标识/Organization and Bundle Identifier](#组织名称和包标识Organization-and-Bundle-Identifier) 106 | * [版权声明/Copyright Statement](#版权声明Copyright-Statement) 107 | * [笑脸/Smiley Face](#笑脸Smiley-Face) 108 | * [参考资料/References](#参考References) 109 | 110 | ## 正确性/Correctness 111 | 112 | Strive to make your code compile without warnings. This rule informs many style decisions such as using `#selector` types instead of string literals. 113 | 114 | 尽量保证代码在编译的过程中不会出现任何警告。这条规则左右了其他规则的制定,例如使用 `#selector` 类型而不是字符串字面量。 115 | 116 | ## 使用 SwiftLint/Using-SwiftLint 117 | 118 | When writing for Kodeco, you are strongly encouraged — perhaps even required, depending on your team — to use our SwiftLint configuration. See the [SwiftLint Policy](SWIFTLINT.markdown) for more information. 119 | 120 | 在为Kodeco撰写代码时,强烈鼓励(甚至可能是必须的,根据你所在的团队)使用我们的SwiftLint配置。关于 SwiftLint 的配置详情见 [SwiftLint Policy](https://github.com/raywenderlich/swift-style-guide/blob/master/SWIFTLINT.markdown)。 121 | 122 | ## 命名/Naming 123 | 124 | Descriptive and consistent naming makes software easier to read and understand. Use the Swift naming conventions described in the [API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). Some key takeaways include: 125 | 126 | 具有描述性和一致性的命名能够让软件更易于阅读和理解。遵循 [API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) 中描述的命名规范。下面展示了一些关键点: 127 | 128 | * striving for clarity at the call site 129 | 130 | * 在调用时力求意图清晰明确 131 | 132 | * prioritizing clarity over brevity 133 | 134 | * 传达清晰的意图比文字的简洁更重要 135 | 136 | * using `camelCase` (not `snake_case`) 137 | 138 | * 使用驼峰命名法(而不是蛇形命名法) 139 | 140 | > 译者注:snake case 是指使用下划线的命名方法,[详情参考链接](https://fr.wikipedia.org/wiki/Snake_case) 141 | 142 | * using `UpperCamelCase` for types (and protocols), `lowerCamelCase` for everything else 143 | 144 | * 类型(和协议)使用首字母大写,其它都是首字母小写 145 | 146 | * including all needed words while omitting needless words 147 | 148 | * 包含所有需要的单词,同时省略不必要的单词 149 | 150 | * using names based on roles, not types 151 | 152 | * 基于作用命名,而不是类型 153 | 154 | * sometimes compensating for weak type information 155 | 156 | * 必要时,为无明确意义的类型补充额外信息 157 | 158 | * striving for fluent usage 159 | 160 | * 尽量保证使用上的流畅 161 | 162 | * beginning factory methods with `make` 163 | 164 | * 工厂方法要以 `make` 开头 165 | 166 | * naming methods for their side effects 167 | 168 | * 根据方法的副作用进行命名 169 | 170 | * verb methods follow the -ed, -ing rule for the non-mutating version 171 | 172 | * 动词方法名的不可变版本,使用动词的 -ed 或者 —ing 形式命名 173 | 174 | * noun methods follow the formX rule for the mutating version 175 | 176 | * 名词方法名的可变版本,使用 formX 来命名 177 | 178 | > 译者注: formX 中的 X 指代对应的名词,例如 `y.formUnion()` 方法,X 指代 Union。 179 | 180 | * boolean types should read like assertions 181 | 182 | * 布尔类型读起来应该像断言 183 | 184 | * protocols that describe _what something is_ should read as nouns 185 | 186 | * _描述事物的_ 协议,读起来应该像名词 187 | 188 | * protocols that describe _a capability_ should end in _-able_ or _-ible_ 189 | 190 | * _描述能力_ 的协议,应该使用后缀 _-able_,_ible_ 191 | 192 | * using terms that don't surprise experts or confuse beginners 193 | 194 | * 使用术语的时候,不要让专家觉得惊讶,也不要让初学者感到困惑 195 | 196 | * generally avoiding abbreviations 197 | 198 | * 通常要避免缩写 199 | 200 | * using precedent for names 201 | 202 | * 命名可以遵循先例 203 | 204 | * preferring methods and properties to free functions 205 | 206 | * 优先选择方法或属性,而非全局函数 207 | 208 | > 译者注:free function 在这里翻译为全局函数,[详情请点击这里](https://en.wikipedia.org/wiki/Free_function) 209 | 210 | * casing acronyms and initialisms uniformly up or down 211 | 212 | * 首字母缩写的单词要根据惯例,保持全大小或者全小写的方式 213 | 214 | * giving the same base name to methods that share the same meaning 215 | 216 | * 当某些方法的含义基本一致时,可以共享方法名的基础部分 217 | 218 | * avoiding overloads on return type 219 | 220 | * 避免重载返回类型 221 | 222 | * choosing good parameter names that serve as documentation 223 | 224 | * 选择具有说明作用的形参名,能够让文档注释质量更高 225 | 226 | * preferring to name the first parameter instead of including its name in the method name, except as mentioned under Delegates 227 | 228 | * 尽量为第一个参数设置参数标签,不要将此参数标签放在方法名中,代理模式下的方法可以忽略此规定 229 | 230 | * labeling closure and tuple parameters 231 | 232 | * 为闭包和元组参数设置标签 233 | 234 | * taking advantage of default parameters 235 | 236 | * 用好默认参数 237 | 238 | ### 在文章中引用代码/Prose 239 | 240 | When referring to methods in prose, being unambiguous is critical. To refer to a method name, use the simplest form possible. 241 | 242 | 在文章中引用方法时,含义明确是至关重要的。尽可能用最简单的形式引用方法。 243 | 244 | 1. Write the method name with no parameters. **Example:** Next, you need to call `addTarget`. 245 | 246 | * 写一个不带参数的方法。**例如**:下一步,你需要调用方法 `addTarget`。 247 | 248 | 2. Write the method name with argument labels. **Example:** Next, you need to call `addTarget(_:action:)`. 249 | 250 | * 写一个带参数标签的方法。**例如**:下一步,你需要调用方法 `addTarget(_:action:)`。 251 | 252 | 3. Write the full method name with argument labels and types. **Example:** Next, you need to call `addTarget(_: Any?, action: Selector?)`. 253 | 254 | * 写一个带参数标签和参数类型的完整方法。**例如**:下一步, 你需要调用方法 `addTarget(_: Any?, action: Selector?)`。 255 | 256 | For the above example using `UIGestureRecognizer`, 1 is unambiguous and preferred. 257 | 258 | 上面这些 `UIGestureRecognizer` 的例中, 1 的表述简单明了,不会造成歧义,推荐使用。 259 | 260 | **Pro Tip:** You can use Xcode's jump bar to lookup methods with argument labels. If you’re particularly good at mashing lots of keys simultaneously, put the cursor in the method name and press **Shift-Control-Option-Command-C** (all 4 modifier keys) and Xcode will kindly put the signature on your clipboard. 261 | 262 | **小提示:** 你可以使用 Xcode 的 jump bar 来查看方法的参数标签。如果你能够相对轻松的操作多个按键,那么可以尝试将光标放在方法名上,并同时按下 **Shift-Control-Option-Command-C** 键,此时 Xcode 会将此方法的签名复制到剪贴板上。 263 | 264 | > 译者注:与复制相关的快捷操作有三种,需要注意的是在 playground 中这些方法不生效。 265 | > 266 | > * **command+C** :Copy(光标所在位置的单词):`viewDidLoad`。 267 | > * **control+shift+command+C**:Copy Symbol Name(光标所在位置的消息符号名称):`viewDidLoad()`。 268 | > * **option+control+shift+command+C**:Copy Qualified Symbol Name(光标所在位置的消息符号全名,带所属类名):`ViewController.viewDidLoad()`。 269 | 270 | ![Methods in Xcode jump bar](screens/xcode-jump-bar.png) 271 | 272 | ### 类前缀/Class Prefixes 273 | 274 | Swift types are automatically namespaced by the module that contains them and you should not add a class prefix such as RW. If two names from different modules collide you can disambiguate by prefixing the type name with the module name. However, only specify the module name when there is possibility for confusion, which should be rare. 275 | 276 | Swift 里各种类型被其所处的模块自动分配了命名空间。不应该再添加类似于 RW 的类前缀。如果不同模块间的两个类型命名冲突,可以在类型名前添加模块名来消除歧义。无论如何,仅在少数可能引起混淆的情况下添加模块名。 277 | 278 | ```swift 279 | import SomeModule 280 | 281 | let myClass = MyModule.UsefulClass() 282 | ``` 283 | 284 | ### 代理/Delegates 285 | 286 | When creating custom delegate methods, an unnamed first parameter should be the delegate source. (UIKit contains numerous examples of this.) 287 | 288 | 当创建自定义的代理方法时,第一个未命名的参数应该是代理源。(UIKit 包含很多类似的例子。) 289 | 290 | **推荐(Preferred)**: 291 | 292 | ```swift 293 | func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) 294 | func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool 295 | ``` 296 | 297 | **不推荐(Not Preferred)**: 298 | 299 | ```swift 300 | func didSelectName(namePicker: NamePickerViewController, name: String) 301 | func namePickerShouldReload() -> Bool 302 | ``` 303 | 304 | ### 使用上下文进行类型推断/Use Type Inferred Context 305 | 306 | Use compiler inferred context to write shorter, clear code. (Also see [Type Inference](#type-inference).) 307 | 308 | 利用编译器根据上下文进行类型推断的能力,书写更简洁明了的代码。(你也可以阅读 [类型推断](#type-inference)。) 309 | 310 | **推荐(Preferred)**: 311 | 312 | ```swift 313 | let selector = #selector(viewDidLoad) 314 | view.backgroundColor = .red 315 | let toView = context.view(forKey: .to) 316 | let view = UIView(frame: .zero) 317 | ``` 318 | 319 | **不推荐(Not Preferred)**: 320 | 321 | ```swift 322 | let selector = #selector(ViewController.viewDidLoad) 323 | view.backgroundColor = UIColor.red 324 | let toView = context.view(forKey: UITransitionContextViewKey.to) 325 | let view = UIView(frame: CGRect.zero) 326 | ``` 327 | 328 | ### 泛型/Generics 329 | 330 | Generic type parameters should be descriptive, upper camel case names. When a type name doesn't have a meaningful relationship or role, use a traditional single uppercase letter such as `T`, `U`, or `V`. 331 | 332 | 泛型的类型参数应该遵循大写驼峰法命名规则,并且应该具有较强的描述性。当类型参数与函数或泛型之间无明显关联时,通常使用单个大写字母来命名,例如 `T` 、`U` 或 `V`。 333 | 334 | **推荐(Preferred)**: 335 | 336 | ```swift 337 | struct Stack { ... } 338 | func write(to target: inout Target) 339 | func swap(_ a: inout T, _ b: inout T) 340 | ``` 341 | 342 | **不推荐(Not Preferred)**: 343 | 344 | ```swift 345 | struct Stack { ... } 346 | func write(to target: inout target) 347 | func swap(_ a: inout Thing, _ b: inout Thing) 348 | ``` 349 | 350 | ### 语言/Language 351 | 352 | Use US English spelling to match Apple's API. 353 | 354 | 使用美式英语拼写,和 Apple 的 API 一致。 355 | 356 | **推荐(Preferred)**: 357 | 358 | ```swift 359 | let color = "red" 360 | ``` 361 | 362 | **不推荐(Not Preferred)**: 363 | 364 | ```swift 365 | let colour = "red" 366 | ``` 367 | 368 | ## 代码组织/Code Organization 369 | 370 | Use extensions to organize your code into logical blocks of functionality. Each extension should be set off with a `// MARK: -` comment to keep things well-organized. 371 | 372 | 用扩展将代码组织成各个不同的功能逻辑块。每个扩展都应该添加 `// MARK: -` 注释符号,以保证代码的结构清晰。 373 | 374 | ### 协议一致性/Protocol Conformance 375 | 376 | In particular, when adding protocol conformance to a model, prefer adding a separate extension for the protocol methods. This keeps the related methods grouped together with the protocol and can simplify instructions to add a protocol to a class with its associated methods. 377 | 378 | 通常来说,当遵循某个协议后,推荐将与该协议相关的方法统一到某个单独的扩展中。这会让与协议相关的方法聚集在一起,也能简化整体的代码结构。 379 | 380 | **推荐(Preferred)**: 381 | 382 | ```swift 383 | class MyViewController: UIViewController { 384 | // class stuff here 385 | } 386 | 387 | // MARK: - UITableViewDataSource 388 | extension MyViewController: UITableViewDataSource { 389 | // table view data source methods 390 | } 391 | 392 | // MARK: - UIScrollViewDelegate 393 | extension MyViewController: UIScrollViewDelegate { 394 | // scroll view delegate methods 395 | } 396 | ``` 397 | 398 | **不推荐(Not Preferred)**: 399 | 400 | ```swift 401 | class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate { 402 | // all methods 403 | } 404 | ``` 405 | 406 | Since the compiler does not allow you to re-declare protocol conformance in a derived class, it is not always required to replicate the extension groups of the base class. This is especially true if the derived class is a terminal class and a small number of methods are being overridden. When to preserve the extension groups is left to the discretion of the author. 407 | 408 | 编译器不允许你在派生类中重新声明已经遵守的协议,因此可以在派生类中省略基类的扩展声明。这在派生类是一个终端类,且只有很少的方法需要重写的情况下是合理的。何时保留扩展声明将由开发者自行决定。 409 | 410 | For UIKit view controllers, consider grouping lifecycle, custom accessors, and IBAction in separate class extensions. 411 | 412 | 对于 UIKit 中的视图控制器,可考虑将生命周期、自定义存取器和 IBAction 分组在单独的类扩展中。 413 | 414 | ### 无效代码/Unused Code 415 | 416 | Unused (dead) code, including Xcode template code and placeholder comments should be removed. An exception is when your tutorial or book instructs the user to use the commented code. 417 | 418 | 应该移除无用代码,比如 Xcode 模板工程代码和占位注释。但教程或书籍中用于指导用户的注释代码除外。 419 | 420 | Aspirational methods not directly associated with the tutorial whose implementation simply calls the superclass should also be removed. This includes any empty/unused UIApplicationDelegate methods. 421 | 422 | 仅实现简单的调用父类,且与教程无直接关联的方法也应该被移除。包括任何空的或无用的 UIApplicationDelegate 方法。 423 | 424 | **推荐(Preferred)**: 425 | 426 | ```swift 427 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 428 | return Database.contacts.count 429 | } 430 | ``` 431 | 432 | **不推荐(Not Preferred)**: 433 | 434 | ```swift 435 | override func didReceiveMemoryWarning() { 436 | super.didReceiveMemoryWarning() 437 | // Dispose of any resources that can be recreated. 438 | } 439 | 440 | override func numberOfSections(in tableView: UITableView) -> Int { 441 | // #warning Incomplete implementation, return the number of sections 442 | return 1 443 | } 444 | 445 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 446 | // #warning Incomplete implementation, return the number of rows 447 | return Database.contacts.count 448 | } 449 | 450 | ``` 451 | 452 | ### 简化引用/Minimal Imports 453 | 454 | Import only the modules a source file requires. For example, don't import `UIKit` when importing `Foundation` will suffice. Likewise, don't import `Foundation` if you must import `UIKit`. 455 | 456 | 只引用必要的文件。举个例子,引用 `Foundation` 就足够的情况下不要再引用 `UIKit`。同样,需要引用 `UIKit` 的时候,就不要引用 `Foundation`。 457 | 458 | **推荐(Preferred)**: 459 | 460 | ```swift 461 | import UIKit 462 | var view: UIView 463 | var deviceModels: [String] 464 | ``` 465 | 466 | **推荐(Preferred)**: 467 | 468 | ```swift 469 | import Foundation 470 | var deviceModels: [String] 471 | ``` 472 | 473 | **不推荐(Not Preferred)**: 474 | 475 | ```swift 476 | import UIKit 477 | import Foundation 478 | var view: UIView 479 | var deviceModels: [String] 480 | ``` 481 | 482 | **不推荐(Not Preferred)**: 483 | 484 | ```swift 485 | import UIKit 486 | var deviceModels: [String] 487 | ``` 488 | 489 | ## 空格/Spacing 490 | 491 | * Indent using 2 spaces rather than tabs to conserve space and help prevent line wrapping. Be sure to set this preference in Xcode and in the Project settings as shown below: 492 | 493 | * 用两个字符缩进比用制表符(tab)缩进更节省空间,同时能防止过早换行。务必在 Xcode 和项目配置中进行设置,如下所示: 494 | 495 | ![Xcode indent settings](screens/indentation.png) 496 | 497 | * Method braces and other braces (`if`/`else`/`switch`/`while` etc.) always open on the same line as the statement but close on a new line. 498 | 499 | * 方法的大括号和其他大括号(`if` / `else` / `switch` / `while` 等)总是在和语句相同的行写左括号,在新行写右括号。 500 | 501 | * Tip: You can re-indent by selecting some code (or **Command-A** to select all) and then **Control-I** (or **Editor ▸ Structure ▸ Re-Indent** in the menu). Some of the Xcode template code will have 4-space tabs hard coded, so this is a good way to fix that. 502 | 503 | * 提示:你可以选中一些代码(或按 **Command-A** 选中全部)然后按 **Control-I**(或在目录中选择**Editor ▸ Structure ▸ Re-Indent**)来重新缩进代码。一些 Xcode 模板代码会使用 4 个空格的制表符硬编码,这就是一个修正它的好方法。 504 | 505 | **推荐(Preferred)**: 506 | 507 | ```swift 508 | if user.isHappy { 509 | // Do something 510 | } else { 511 | // Do something else 512 | } 513 | ``` 514 | 515 | **不推荐(Not Preferred)**: 516 | 517 | ```swift 518 | if user.isHappy 519 | { 520 | // Do something 521 | } 522 | else { 523 | // Do something else 524 | } 525 | ``` 526 | 527 | * There should be one blank line between methods and up to one blank line between type declarations to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods. 528 | 529 | * 方法之间应该有一个空行,类型声明之间最多应该保留一个空行,这样的目的是为了让代码在视觉上更清晰和更有条理。方法中的空白应该按功能分隔代码,但在一个方法中有很多段意味着你应该对其进行重构和封装。 530 | 531 | * There should be no blank lines after an opening brace or before a closing brace. 532 | 533 | * 在左括号之后或者右括号之前没有独立的空行。 534 | 535 | * Closing parentheses should not appear on a line by themselves. 536 | 537 | * 右括号不应单独出现在一行中。 538 | 539 | **推荐(Preferred)**: 540 | 541 | ```swift 542 | let user = try await getUser( 543 | for: userID, 544 | on: connection) 545 | ``` 546 | 547 | **不推荐(Not Preferred)**: 548 | 549 | ```swift 550 | let user = try await getUser( 551 | for: userID, 552 | on: connection 553 | ) 554 | ``` 555 | 556 | * Colons always have no space on the left and one space on the right. Exceptions are the ternary operator `? :`, empty dictionary `[:]` and `#selector` syntax `addTarget(_:action:)`. 557 | 558 | * 冒号左边没有空格,右边有空格。三元运算符 `? :`、空字典 `[:]` 和 `#selector` 语法里的方法名,例如 `addTarget(_:action:)` 不需要遵守此项规定。 559 | 560 | **推荐(Preferred)**: 561 | 562 | ```swift 563 | class TestDatabase: Database { 564 | var data: [String: CGFloat] = ["A": 1.2, "B": 3.2] 565 | } 566 | ``` 567 | 568 | **不推荐(Not Preferred)**: 569 | 570 | ```swift 571 | class TestDatabase : Database { 572 | var data :[String:CGFloat] = ["A" : 1.2, "B":3.2] 573 | } 574 | ``` 575 | 576 | * Long lines should be wrapped at around 70 characters. A hard limit is intentionally not specified. 577 | 578 | * 当一行的字符数达到 70 个左右时就应该换行,这里并非硬性限制,可自行调整。 579 | 580 | * Avoid trailing whitespaces at the ends of lines. 581 | 582 | * 避免在行尾增加空格。 583 | 584 | * Add a single newline character at the end of each file. 585 | 586 | * 在每个文件的结尾增加一个单独的换行符。 587 | 588 | ## 注释/Comments 589 | 590 | When they are needed, use comments to explain **why** a particular piece of code does something. Comments must be kept up-to-date or deleted. 591 | 592 | 需要的时候,用注释来解释**为什么**这个代码片段要做这些事情。注释应当保持最新的状态,否则就该被删除。 593 | 594 | Avoid block comments inline with code, as the code should be as self-documenting as possible. _Exception: This does not apply to those comments used to generate documentation._ 595 | 596 | 避免出现大块的代码注释,代码应该尽可能自文档化。_例外:这条规则不适用于生成文档的注释。_ 597 | 598 | > 译者注:[详情见链接](https://github.com/raywenderlich/swift-style-guide/issues/310) 599 | 600 | Avoid the use of C-style comments (`/* ... */`). Prefer the use of double- or triple-slash. 601 | 602 | 避免使用 C 风格的注释方式(`/* ... */`),尽量使用 2 个或 3 个斜杠进行注释。 603 | 604 | ## 类和结构体/Classes and Structures 605 | 606 | ### 如何选择/Which one to use? 607 | 608 | Remember, structs have [value semantics](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_144). Use structs for things that do not have an identity. An array that contains [a, b, c] is really the same as another array that contains [a, b, c] and they are completely interchangeable. It doesn't matter whether you use the first array or the second, because they represent the exact same thing. That's why arrays are structs. 609 | 610 | 请记住,结构体有 [值语义](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_144)。对没有身份标识的事物使用结构体类型。一个包含 [a, b, c] 的数组和另一个包含 [a, b, c] 的数组是完全一样的。它们完全可以互换。使用第一个数组还是第二个数组都无所谓,因为它们代表着完全相同的事物。这就是为什么数组是结构体。 611 | 612 | Classes have [reference semantics](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_145). Use classes for things that do have an identity or a specific life cycle. You would model a person as a class because two person objects are two different things. Just because two people have the same name and birthdate, doesn't mean they are the same person. But the person's birthdate would be a struct because a date of 3 March 1950 is the same as any other date object for 3 March 1950. The date itself doesn't have an identity. 613 | 614 | 类有 [引用语义](https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_145)。对有身份标识或有具体生命周期的事物使用类类型。你需要将人建模为一个类,因为不同的两个人是两个不同的事物。只是因为两个人拥有相同的名字和生日不意味着他们是同一个人。但是人的生日应该是一个结构体,因为 1950 年 3 月 3 日和任何其它的 1950 年 3 月 3 日日期对象是相同的。日期本身没有标识。 615 | 616 | Sometimes, things should be structs but need to conform to `AnyObject` or are historically modeled as classes already (`NSDate`, `NSSet`). Try to follow these guidelines as closely as possible. 617 | 618 | 有时,事物本应该是结构体,但需要遵循 `AnyObject`,或由于历史原因已经被建模为类(`NSDate` 、 `NSSet`)。不管怎样,都请尽可能遵循前面提到的原则。 619 | 620 | ### 示例/Example definition 621 | 622 | Here's an example of a well-styled class definition: 623 | 624 | 下面是一个代码风格良好的类定义示例: 625 | 626 | ```swift 627 | class Circle: Shape { 628 | var x: Int, y: Int 629 | var radius: Double 630 | var diameter: Double { 631 | get { 632 | return radius * 2 633 | } 634 | set { 635 | radius = newValue / 2 636 | } 637 | } 638 | 639 | init(x: Int, y: Int, radius: Double) { 640 | self.x = x 641 | self.y = y 642 | self.radius = radius 643 | } 644 | 645 | convenience init(x: Int, y: Int, diameter: Double) { 646 | self.init(x: x, y: y, radius: diameter / 2) 647 | } 648 | 649 | override func area() -> Double { 650 | return Double.pi * radius * radius 651 | } 652 | } 653 | 654 | extension Circle: CustomStringConvertible { 655 | var description: String { 656 | return "center = \(centerString) area = \(area())" 657 | } 658 | private var centerString: String { 659 | return "(\(x),\(y))" 660 | } 661 | } 662 | ``` 663 | 664 | The example above demonstrates the following style guidelines: 665 | 666 | 上面的例子遵循了以下代码规范: 667 | 668 | * Specify types for properties, variables, constants, argument declarations and other statements with a space after the colon but not before, e.g. `x: Int`, and `Circle: Shape`. 669 | 670 | * 遵循冒号前无空格,冒号后有空格的规则,并应用在属性、变量、常量、参数声明和其它类型的语句中,例如:`x: Int` 和 `Circle: Shape`。 671 | 672 | * Define multiple variables and structures on a single line if they share a common purpose / context. 673 | 674 | * 如果多个变量和结构体遵循同一目的 / 上下文,则可以在同一行中定义。 675 | 676 | * Indent getter and setter definitions and property observers. 677 | 678 | * 缩进 getter、setter 的定义和属性观察器。 679 | 680 | * Don't add modifiers such as `internal` when they're already the default. Similarly, don't repeat the access modifier when overriding a method. 681 | 682 | * 不要将默认的修饰符重复添加到代码中,例如 `internal` 关键字。类似的,当重写一个方法时,不要再重复添加与访问权限相关的修饰符。 683 | 684 | * Organize extra functionality (e.g. printing) in extensions. 685 | 686 | * 在扩展中组织额外功能(例如打印相关的代码)。 687 | 688 | * Hide non-shared, implementation details such as `centerString` inside the extension using `private` access control. 689 | 690 | * 利用隐藏非共享的实现细节,例如在扩展中实现 `centerString` 方法并设置 `private` 级别的访问控制权限。 691 | 692 | ### Self 的使用/Use of Self 693 | 694 | For conciseness, avoid using `self` since Swift does not require it to access an object's properties or invoke its methods. 695 | 696 | 为了简洁,请避免使用 `self` 关键词,Swift 不需要用它来访问一个对象属性或调用它的方法。 697 | 698 | Use self only when required by the compiler (in `@escaping` closures, or in initializers to disambiguate properties from arguments). In other words, if it compiles without `self` then omit it. 699 | 700 | 仅在编译器需要时(在 `@escaping` 闭包或初始化函数中消除参数与属性的歧义)才使用 self。换句话说,如果不需要 `self` 就能编译通过,可以忽略它。 701 | 702 | ### 计算属性/Computed Properties 703 | 704 | For conciseness, if a computed property is read-only, omit the get clause. The get clause is required only when a set clause is provided. 705 | 706 | 为了简洁,如果一个计算属性是只读的,可以忽略 get 子句。仅在 set 子句存在的情况下才需要 get 子句。 707 | 708 | **推荐(Preferred)**: 709 | 710 | ```swift 711 | var diameter: Double { 712 | return radius * 2 713 | } 714 | ``` 715 | 716 | **不推荐(Not Preferred)**: 717 | 718 | ```swift 719 | var diameter: Double { 720 | get { 721 | return radius * 2 722 | } 723 | } 724 | ``` 725 | 726 | ### Final 关键字/Final 727 | 728 | Marking classes or members as `final` in tutorials can distract from the main topic and is not required. Nevertheless, use of `final` can sometimes clarify your intent and is worth the cost. In the below example, `Box` has a particular purpose and customization in a derived class is not intended. Marking it `final` makes that clear. 729 | 730 | 在教程中将类或成员标记为 `final` 会偏离主题,且不是必需的。不过有时 `final` 的使用可以明确你的意图,也值得你这样做。在下面的例子中,`Box` 有特定的用途,且不打算在派生类中进行自定义。标记为 `final` 可以使它更清晰。 731 | 732 | ```swift 733 | // Turn any generic type into a reference type using this Box class. 734 | final class Box { 735 | let value: T 736 | init(_ value: T) { 737 | self.value = value 738 | } 739 | } 740 | ``` 741 | 742 | ## 函数声明/Function Declarations 743 | 744 | Keep short function declarations on one line including the opening brace: 745 | 746 | 保持函数的声明语句在一行以内,包括左括号: 747 | 748 | ```swift 749 | func reticulateSplines(spline: [Double]) -> Bool { 750 | // reticulate code goes here 751 | } 752 | ``` 753 | 754 | For functions with long signatures, put each parameter on a new line and add an extra indent on subsequent lines: 755 | 756 | 对于函数名较长的情况,需要保证每个参数新起一行,且在该行的开始处添加一个缩进符: 757 | 758 | ```swift 759 | func reticulateSplines( 760 | spline: [Double], 761 | adjustmentFactor: Double, 762 | translateConstant: Int, 763 | comment: String 764 | ) -> Bool { 765 | // reticulate code goes here 766 | } 767 | ``` 768 | 769 | Don't use `(Void)` to represent the lack of an input; simply use `()`. Use `Void` instead of `()` for closure and function outputs. 770 | 771 | 不要使用 `(Void)` 来表示入参为空的情况,用 `()` 即可。在闭包和函数的输出参数为空的情况时,推荐使用 `Void`, 而不是 `()`。 772 | 773 | **推荐(Preferred)**: 774 | 775 | ```swift 776 | func updateConstraints() -> Void { 777 | // magic happens here 778 | } 779 | 780 | typealias CompletionHandler = (result) -> Void 781 | ``` 782 | 783 | **不推荐(Not Preferred)**: 784 | 785 | ```swift 786 | func updateConstraints() -> () { 787 | // magic happens here 788 | } 789 | 790 | typealias CompletionHandler = (result) -> () 791 | ``` 792 | 793 | ## 函数调用/Function Calls 794 | 795 | Mirror the style of function declarations at call sites. Calls that fit on a single line should be written as such: 796 | 797 | 调用代码应该和函数声明风格一致。如果调用函数只需要一行就可以完成,应当像下面这样组织代码: 798 | 799 | ```swift 800 | let success = reticulateSplines(splines) 801 | ``` 802 | 803 | If the call site must be wrapped, put each parameter on a new line, indented one additional level: 804 | 805 | 当函数调用必须换行时,需要让每个参数新起一行并添加缩进: 806 | 807 | ```swift 808 | let success = reticulateSplines( 809 | spline: splines, 810 | adjustmentFactor: 1.3, 811 | translateConstant: 2, 812 | comment: "normalize the display") 813 | ``` 814 | 815 | ## 闭包表达式/Closure Expressions 816 | 817 | Use trailing closure syntax only if there's a single closure expression parameter at the end of the argument list. Give the closure parameters descriptive names. 818 | 819 | 当参数列表的最后一个元素是闭包表达式,且整个参数列表里只有一个闭包参数的情况下,使用尾随闭包语法。给闭包参数定义一个描述性的命名。 820 | 821 | **推荐(Preferred)**: 822 | 823 | ```swift 824 | UIView.animate(withDuration: 1.0) { 825 | self.myView.alpha = 0 826 | } 827 | 828 | UIView.animate(withDuration: 1.0, animations: { 829 | self.myView.alpha = 0 830 | }, completion: { finished in 831 | self.myView.removeFromSuperview() 832 | }) 833 | ``` 834 | 835 | **不推荐(Not Preferred)**: 836 | 837 | ```swift 838 | UIView.animate(withDuration: 1.0, animations: { 839 | self.myView.alpha = 0 840 | }) 841 | 842 | UIView.animate(withDuration: 1.0, animations: { 843 | self.myView.alpha = 0 844 | }) { f in 845 | self.myView.removeFromSuperview() 846 | } 847 | ``` 848 | 849 | For single-expression closures where the context is clear, use implicit returns: 850 | 851 | 对于上下文清晰的单独表达式闭包,使用隐式返回: 852 | 853 | ```swift 854 | attendeeList.sort { a, b in 855 | a > b 856 | } 857 | ``` 858 | 859 | Chained methods using trailing closures should be clear and easy to read in context. Decisions on spacing, line breaks, and when to use named versus anonymous arguments is left to the discretion of the author. Examples: 860 | 861 | 在链式方法结合尾随闭包的使用场景中,应当保证代码清晰且可读性强。作者将自行抉择空格、换行、何时使用具名参数、何时使用匿名参数等问题。举例: 862 | 863 | ```swift 864 | let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90) 865 | 866 | let value = numbers 867 | .map {$0 * 2} 868 | .filter {$0 > 50} 869 | .map {$0 + 10} 870 | ``` 871 | 872 | ## 类型/Types 873 | 874 | Always use Swift's native types and expressions when available. Swift offers bridging to Objective-C so you can still use the full set of methods as needed. 875 | 876 | 请尽可能多的使用 Swift 原生类型。 Swift 提供了 Objective-C 桥接,所以当你需要时仍然可以使用对应的方法。 877 | 878 | **推荐(Preferred)**: 879 | 880 | ```swift 881 | let width = 120.0 // Double 882 | let widthString = "\(width)" // String 883 | ``` 884 | 885 | **可接受(Less Preferred)**: 886 | 887 | ```swift 888 | let width = 120.0 // Double 889 | let widthString = (width as NSNumber).stringValue // String 890 | ``` 891 | 892 | **不推荐(Not Preferred)**: 893 | 894 | ```swift 895 | let width: NSNumber = 120.0 // NSNumber 896 | let widthString: NSString = width.stringValue // NSString 897 | ``` 898 | 899 | In drawing code, use `CGFloat` if it makes the code more succinct by avoiding too many conversions. 900 | 901 | 在绘图相关的代码中,使用 `CGFloat` 可以避免代码频繁的转换,从而让你的代码更加简洁。 902 | 903 | ### 常量/Constants 904 | 905 | Constants are defined using the `let` keyword and variables with the `var` keyword. Always use `let` instead of `var` if the value of the variable will not change. 906 | 907 | 使用 `let` 关键字来定义常量,使用 `var` 关键字来定义变量。如果变量的值不会改变,则要使用 `let` 来代替 `var`。 908 | 909 | **Tip:** A good technique is to define everything using `let` and only change it to `var` if the compiler complains! 910 | 911 | **提示**: 一个比较好的技巧是所有东西都使用 `let` 定义, 当编译器警告时再改为 `var`。 912 | 913 | You can define constants on a type rather than on an instance of that type using type properties. To declare a type property as a constant simply use `static let`. Type properties declared in this way are generally preferred over global constants because they are easier to distinguish from instance properties. Example: 914 | 915 | 你可以在类型上定义常量,而不是在实例上通过属性来定义常量。使用 `static let` 把一个类型属性声明为常量。相比于全局变量声明常量的方式,更推荐用这种方式,因为这种方式更能和实例属性区分开。举例: 916 | 917 | **推荐(Preferred)**: 918 | 919 | ```swift 920 | enum Math { 921 | static let e = 2.718281828459045235360287 922 | static let root2 = 1.41421356237309504880168872 923 | } 924 | 925 | let hypotenuse = side * Math.root2 926 | 927 | ``` 928 | 929 | **Note:** The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace. 930 | 931 | **注意:** 使用无枚举值的枚举类型时,它的一大优势就是不会被意外的实例化,只是一个单纯的命名空间。 932 | 933 | **不推荐(Not Preferred)**: 934 | 935 | ```swift 936 | let e = 2.718281828459045235360287 // pollutes global namespace 937 | let root2 = 1.41421356237309504880168872 938 | 939 | let hypotenuse = side * root2 // what is root2? 940 | ``` 941 | 942 | ### 静态方法和可变类型属性/Static Methods and Variable Type Properties 943 | 944 | Static methods and type properties work similarly to global functions and global variables and should be used sparingly. They are useful when functionality is scoped to a particular type or when Objective-C interoperability is required. 945 | 946 | 静态方法和类型属性跟全局函数和全局变量的工作原理类似,应当谨慎使用。当功能的作用域是一个特定类型,或需要与 Objective-C 交互时,它们非常有用。 947 | 948 | ### 可选类型/Optionals 949 | 950 | Declare variables and function return types as optional with `?` where a `nil` value is acceptable. 951 | 952 | 在可接受 `nil` 值的情况下,使用 `?` 声明变量和函数返回类型为可选类型。 953 | 954 | Use implicitly unwrapped types declared with `!` only for instance variables that you know will be initialized later before use, such as subviews that will be set up in `viewDidLoad()`. Prefer optional binding to implicitly unwrapped optionals in most other cases. 955 | 956 | 用 `!` 声明的隐式解包类型,适用于在使用前一定能被正确初始化的实例变量,比如在 `viewDidLoad` 中设置子视图。大多数场景下,倾向使用可选绑定而不是隐式解包。 957 | 958 | When accessing an optional value, use optional chaining if the value is only accessed once or if there are many optionals in the chain: 959 | 960 | 当访问一个可选值时,如果可选值仅被访问一次,或在调用链中有许多可选值时,使用可选链: 961 | 962 | ```swift 963 | textContainer?.textLabel?.setNeedsDisplay() 964 | ``` 965 | 966 | Use optional binding when it's more convenient to unwrap once and perform multiple operations: 967 | 968 | 当可选值只需一次就可解绑,且之后执行的操作与该可选值相关,使用可选绑定: 969 | 970 | ```swift 971 | if let textContainer = textContainer { 972 | // do many things with textContainer 973 | } 974 | ``` 975 | 976 | **Notes:** Swift 5.7 introduced new shorthand syntax for unwrapping optionals into shadowed variables: 977 | 978 | **注意:** Swift 5.7 引入了一种新的简写语法,用于将可选项解包到阴影变量(shadowed variables)中: 979 | 980 | ```swift 981 | if let textContainer { 982 | // do many things with textContainer 983 | } 984 | ``` 985 | 986 | When naming optional variables and properties, avoid naming them like `optionalString` or `maybeView` since their optional-ness is already in the type declaration. 987 | 988 | 在命名可选变量和属性时,避免类似 `optionalString` 或 `maybeView` 这样的命名,因为它们的可选性已经体现在类型声明中。 989 | 990 | For optional binding, shadow the original name whenever possible rather than using names like `unwrappedView` or `actualLabel`. 991 | 992 | 对于可选绑定,适当时使用原始名称,而不是使用像 `unwrappedView` 或 `actualLabel` 这样的名称。 993 | 994 | **推荐(Preferred)**: 995 | 996 | ```swift 997 | var subview: UIView? 998 | var volume: Double? 999 | 1000 | // later on... 1001 | if let subview = subview, let volume = volume { 1002 | // do something with unwrapped subview and volume 1003 | } 1004 | 1005 | // another example 1006 | resource.request().onComplete { [weak self] response in 1007 | guard let self = self else { return } 1008 | let model = self.updateModel(response) 1009 | self.updateUI(model) 1010 | } 1011 | ``` 1012 | 1013 | **不推荐(Not Preferred)**: 1014 | 1015 | ```swift 1016 | var optionalSubview: UIView? 1017 | var volume: Double? 1018 | 1019 | if let unwrappedSubview = optionalSubview { 1020 | if let realVolume = volume { 1021 | // do something with unwrappedSubview and realVolume 1022 | } 1023 | } 1024 | 1025 | // another example 1026 | UIView.animate(withDuration: 2.0) { [weak self] in 1027 | guard let strongSelf = self else { return } 1028 | strongSelf.alpha = 1.0 1029 | } 1030 | ``` 1031 | 1032 | ### 延迟初始化/Lazy Initialization 1033 | 1034 | Consider using lazy initialization for finer grained control over object lifetime. This is especially true for `UIViewController` that loads views lazily. You can either use a closure that is immediately called `{ }()` or call a private factory method. Example: 1035 | 1036 | 在更细粒度地控制对象声明周期时,可以考虑使用延迟初始化。对于 `UIViewController`,延迟初始化视图是非常正确的。你可以使用立即调用的闭包(比如 `{ }()`)或调用私有工厂方法。例如: 1037 | 1038 | ```swift 1039 | lazy var locationManager = makeLocationManager() 1040 | 1041 | private func makeLocationManager() -> CLLocationManager { 1042 | let manager = CLLocationManager() 1043 | manager.desiredAccuracy = kCLLocationAccuracyBest 1044 | manager.delegate = self 1045 | manager.requestAlwaysAuthorization() 1046 | return manager 1047 | } 1048 | ``` 1049 | 1050 | **注意/Notes:** 1051 | 1052 | * `[unowned self]` is not required here. A retain cycle is not created. 1053 | 1054 | * 因为没有发生循环引用,所以这里不需要 `[unowned self]`。 1055 | 1056 | * Location manager has a side-effect for popping up UI to ask the user for permission so fine grain control makes sense here. 1057 | 1058 | * 位置管理器向用户申请权限时,有弹出 UI 界面的副作用,所以细颗粒地控制在这里是有意义的。 1059 | 1060 | ### 类型推断/Type Inference 1061 | 1062 | Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small, non-empty arrays and dictionaries. When required, specify the specific type such as `CGFloat` or `Int16`. 1063 | 1064 | 优先选择简洁紧凑的代码,让编译器对单个实例的常量或变量进行推断类型。类型推断也适合于小型的,非空的数组和字典。当数据是一些特殊类型,如 `CGFloat` 或 `Int16`时,请指明此特定类型。 1065 | 1066 | **推荐(Preferred)**: 1067 | 1068 | ```swift 1069 | let message = "Click the button" 1070 | let currentBounds = computeViewBounds() 1071 | var names = ["Mic", "Sam", "Christine"] 1072 | let maximumWidth: CGFloat = 106.5 1073 | ``` 1074 | 1075 | **不推荐(Not Preferred)**: 1076 | 1077 | ```swift 1078 | let message: String = "Click the button" 1079 | let currentBounds: CGRect = computeViewBounds() 1080 | var names = [String]() 1081 | ``` 1082 | 1083 | #### 空数组和空字典的类型注释/Type Annotation for Empty Arrays and Dictionaries 1084 | 1085 | For empty arrays and dictionaries, use type annotation. (For an array or dictionary assigned to a large, multi-line literal, use type annotation.) 1086 | 1087 | 为空数组和空字典使用类型注释。(对于内容量大、多行的字面量数组和字典可以使用类型注释。) 1088 | 1089 | **推荐(Preferred)**: 1090 | 1091 | ```swift 1092 | var names: [String] = [] 1093 | var lookup: [String: Int] = [:] 1094 | ``` 1095 | 1096 | **不推荐(Not Preferred)**: 1097 | 1098 | ```swift 1099 | var names = [String]() 1100 | var lookup = [String: Int]() 1101 | ``` 1102 | 1103 | **NOTE**: Following this guideline means picking descriptive names is even more important than before. 1104 | 1105 | **注意**:遵循此原则意味着在命名时要更注重命名的描述性。 1106 | 1107 | ### 语法糖/Syntactic Sugar 1108 | 1109 | Prefer the shortcut versions of type declarations over the full generics syntax. 1110 | 1111 | 推荐使用简短版本的类型声明,而不是完整的泛型语法。 1112 | 1113 | **推荐(Preferred)**: 1114 | 1115 | ```swift 1116 | var deviceModels: [String] 1117 | var employees: [Int: String] 1118 | var faxNumber: Int? 1119 | ``` 1120 | 1121 | **不推荐(Not Preferred)**: 1122 | 1123 | ```swift 1124 | var deviceModels: Array 1125 | var employees: Dictionary 1126 | var faxNumber: Optional 1127 | ``` 1128 | 1129 | ## 函数 vs 方法/Functions vs Methods 1130 | 1131 | Free functions, which aren't attached to a class or type, should be used sparingly. When possible, prefer to use a method instead of a free function. This aids in readability and discoverability. 1132 | 1133 | 不附属于类或类型的全局函数应该被谨慎使用。可能的话,首选方法而不是全局函数。这有助于提升可读性,也更容易被检索到。 1134 | 1135 | Free functions are most appropriate when they aren't associated with any particular type or instance. 1136 | 1137 | 全局函数最适用于它们与任何特定类或实例无关的情况。 1138 | 1139 | **推荐(Preferred)**: 1140 | 1141 | ```swift 1142 | let sorted = items.mergeSorted() // easily discoverable 1143 | rocket.launch() // acts on the model 1144 | ``` 1145 | 1146 | **不推荐(Not Preferred)**: 1147 | 1148 | ```swift 1149 | let sorted = mergeSort(items) // hard to discover 1150 | launch(&rocket) 1151 | ``` 1152 | 1153 | **适合全局函数的场景/Free Function Exceptions** 1154 | 1155 | ```swift 1156 | let tuples = zip(a, b) // feels natural as a free function (symmetry) 1157 | let value = max(x, y, z) // another free function that feels natural 1158 | ``` 1159 | 1160 | ## 内存管理/Memory Management 1161 | 1162 | Code (even non-production, tutorial demo code) should not create reference cycles. Analyze your object graph and prevent strong cycles with `weak` and `unowned` references. Alternatively, use value types (`struct`, `enum`) to prevent cycles altogether. 1163 | 1164 | 代码(即使是非生产环境、教程演示的代码)都不应该出现循环引用。分析你的对象关系图并用 `weak` 和 `unowned` 来防止循环引用。或者使用值类型(`struct`、`enum`)来杜绝循环引用。 1165 | 1166 | ### 延长对象的生命周期/Extending object lifetime 1167 | 1168 | Extend object lifetime using the `[weak self]` and `guard let self = self else { return }` idiom. `[weak self]` is preferred to `[unowned self]` where it is not immediately obvious that `self` outlives the closure. Explicitly extending lifetime is preferred to optional chaining. 1169 | 1170 | 使用惯用语法 `[weak self]` 和 `guard let self = self else { return }` 来延长对象的生命周期。 在 `self` 超出闭包生命周期不明确的情况下,优先使用 `[weak self]` 而不是 `[unowned self]`。利用固定代码明确延长对象的生命周期优于可选链的书写方式。 1171 | 1172 | **推荐(Preferred)**: 1173 | 1174 | ```swift 1175 | resource.request().onComplete { [weak self] response in 1176 | guard let self = self else { 1177 | return 1178 | } 1179 | let model = self.updateModel(response) 1180 | self.updateUI(model) 1181 | } 1182 | ``` 1183 | 1184 | **不推荐(Not Preferred)**: 1185 | 1186 | ```swift 1187 | // might crash if self is released before response returns 1188 | resource.request().onComplete { [unowned self] response in 1189 | let model = self.updateModel(response) 1190 | self.updateUI(model) 1191 | } 1192 | ``` 1193 | 1194 | **不推荐(Not Preferred)**: 1195 | 1196 | ```swift 1197 | // deallocate could happen between updating the model and updating UI 1198 | resource.request().onComplete { [weak self] response in 1199 | let model = self?.updateModel(response) 1200 | self?.updateUI(model) 1201 | } 1202 | ``` 1203 | 1204 | ## 访问控制/Access Control 1205 | 1206 | Full access control annotation in tutorials can distract from the main topic and is not required. Using `private` and `fileprivate` appropriately, however, adds clarity and promotes encapsulation. Prefer `private` to `fileprivate`; use `fileprivate` only when the compiler insists. 1207 | 1208 | 在教程中,完整的访问控制注释会偏离主题且是不必要的。然而,适时地使用 `private` 和 `fileprivate` 会使代码更加清晰,也会有助于封装。 在合理情况下,`private` 要优于 `fileprivate`。 只有编译器强制的情况下使用 `fileprivate`。 1209 | 1210 | Only explicitly use `open`, `public`, and `internal` when you require a full access control specification. 1211 | 1212 | 只有需要完整的访问控制格式时,才显式地使用 `open`、`public` 和 `internal`。 1213 | 1214 | Use access control as the leading property specifier. The only things that should come before access control are the `static` specifier or attributes such as `@IBAction`, `@IBOutlet` and `@discardableResult`. 1215 | 1216 | 把访问控制相关的关键字用作前置属性说明符。仅有 `static` 说明符或诸如 `@IBAction`、`@IBOutlet` 和 `@discardableResult` 标志应该放在访问控制符前面。 1217 | 1218 | **推荐(Preferred)**: 1219 | 1220 | ```swift 1221 | private let message = "Great Scott!" 1222 | 1223 | class TimeMachine { 1224 | private dynamic lazy var fluxCapacitor = FluxCapacitor() 1225 | } 1226 | ``` 1227 | 1228 | **不推荐(Not Preferred)**: 1229 | 1230 | ```swift 1231 | fileprivate let message = "Great Scott!" 1232 | 1233 | class TimeMachine { 1234 | lazy dynamic private var fluxCapacitor = FluxCapacitor() 1235 | } 1236 | ``` 1237 | 1238 | ## 控制流/Control Flow 1239 | 1240 | Prefer the `for-in` style of `for` loop over the `while-condition-increment` style. 1241 | 1242 | 优先选择 `for` 循环的 `for-in` 格式而不是 `while-condition-increment` 风格的循环。 1243 | 1244 | **推荐(Preferred)**: 1245 | 1246 | ```swift 1247 | for _ in 0..<3 { 1248 | print("Hello three times") 1249 | } 1250 | 1251 | for (index, person) in attendeeList.enumerated() { 1252 | print("\(person) is at position #\(index)") 1253 | } 1254 | 1255 | for index in stride(from: 0, to: items.count, by: 2) { 1256 | print(index) 1257 | } 1258 | 1259 | for index in (0...3).reversed() { 1260 | print(index) 1261 | } 1262 | ``` 1263 | 1264 | **不推荐(Not Preferred)**: 1265 | 1266 | ```swift 1267 | var i = 0 1268 | while i < 3 { 1269 | print("Hello three times") 1270 | i += 1 1271 | } 1272 | 1273 | var i = 0 1274 | while i < attendeeList.count { 1275 | let person = attendeeList[i] 1276 | print("\(person) is at position #\(i)") 1277 | i += 1 1278 | } 1279 | ``` 1280 | 1281 | ### 三元条件表达式/Ternary Operator 1282 | 1283 | The Ternary operator, `?:` , should only be used when it increases clarity or code neatness. A single condition is usually all that should be evaluated. Evaluating multiple conditions is usually more understandable as an `if` statement or refactored into instance variables. In general, the best use of the ternary operator is during assignment of a variable and deciding which value to use. 1284 | 1285 | 仅当三元条件表达式能够让代码更清晰或者更整洁时才使用它。即使是单个条件分支也需要进行判断。而在存在多个条件分支时,利用 `if` 语句的方式或者将相关代码重构为实例变量的方式,能够降低整体的理解难度。总体而言,使用三元条件表达式的最佳场景,就是赋值变量并为变量选择值的时候。 1286 | 1287 | **推荐(Preferred)**: 1288 | 1289 | ```swift 1290 | let value = 5 1291 | result = value != 0 ? x : y 1292 | 1293 | let isHorizontal = true 1294 | result = isHorizontal ? x : y 1295 | ``` 1296 | 1297 | **不推荐(Not Preferred)**: 1298 | 1299 | ```swift 1300 | result = a > b ? x = c > d ? c : d : y 1301 | ``` 1302 | 1303 | ## 黄金路径/Golden Path 1304 | 1305 | When coding with conditionals, the left-hand margin of the code should be the "golden" or "happy" path. That is, don't nest `if` statements. Multiple return statements are OK. The `guard` statement is built for this. 1306 | 1307 | 当使用条件语句编码时,代码的左边距应该是“黄金”或“快乐”的路径。换句话说,不要嵌套 `if` 语句。多个返回语句是可以的。`guard` 语句就是因为避免这个问题而生的。 1308 | 1309 | **推荐(Preferred)**: 1310 | 1311 | ```swift 1312 | func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies { 1313 | guard let context = context else { 1314 | throw FFTError.noContext 1315 | } 1316 | guard let inputData = inputData else { 1317 | throw FFTError.noInputData 1318 | } 1319 | 1320 | // use context and input to compute the frequencies 1321 | return frequencies 1322 | } 1323 | ``` 1324 | 1325 | **不推荐(Not Preferred)**: 1326 | 1327 | ```swift 1328 | func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies { 1329 | if let context = context { 1330 | if let inputData = inputData { 1331 | // use context and input to compute the frequencies 1332 | 1333 | return frequencies 1334 | } else { 1335 | throw FFTError.noInputData 1336 | } 1337 | } else { 1338 | throw FFTError.noContext 1339 | } 1340 | } 1341 | ``` 1342 | 1343 | When multiple optionals are unwrapped either with `guard` or `if let`, minimize nesting by using the compound version when possible. In the compound version, place the `guard` on its own line, then indent each condition on its own line. The `else` clause is indented to match the conditions and the code is indented one additional level, as shown below. Example: 1344 | 1345 | 用 `guard` 或 `if let` 解包多个可选值时,在条件允许的状态下,尽量使用混合的方式来简化嵌套。在混合的方式中,将 `guard` 关键字放在句子的最开始处,并缩进与此相关的条件分支。`else` 与 `guard` 字句的缩进匹配。就像下面的示例一样: 1346 | 1347 | **推荐(Preferred)**: 1348 | 1349 | ```swift 1350 | guard 1351 | let number1 = number1, 1352 | let number2 = number2, 1353 | let number3 = number3 1354 | else { 1355 | fatalError("impossible") 1356 | } 1357 | // do something with numbers 1358 | ``` 1359 | 1360 | **不推荐(Not Preferred)**: 1361 | 1362 | ```swift 1363 | if let number1 = number1 { 1364 | if let number2 = number2 { 1365 | if let number3 = number3 { 1366 | // do something with numbers 1367 | } else { 1368 | fatalError("impossible") 1369 | } 1370 | } else { 1371 | fatalError("impossible") 1372 | } 1373 | } else { 1374 | fatalError("impossible") 1375 | } 1376 | ``` 1377 | 1378 | ### 失败防护/Failing Guards 1379 | 1380 | Guard statements are required to exit in some way. Generally, this should be simple one line statement such as `return`, `throw`, `break`, `continue`, and `fatalError()`. Large code blocks should be avoided. If cleanup code is required for multiple exit points, consider using a `defer` block to avoid cleanup code duplication. 1381 | 1382 | 在某些退出的场景下,防护语句是必不可少的。一般来说,它应该是一行简洁的语句,比如:`return`、`throw`、`break`、`continue` 和 `fatalError()`。应该避免大量的代码块。如果与清理相关的代码被用在多个退出点,则可以考虑用 `defer` 块来避免代码的重复。 1383 | 1384 | ## 分号/Semicolons 1385 | 1386 | Swift does not require a semicolon after each statement in your code. They are only required if you wish to combine multiple statements on a single line. 1387 | 1388 | 在 Swift 中,每条代码语句后面不需要加分号。只有在一行中拼接多条语句时,才需要加分号。 1389 | 1390 | Do not write multiple statements on a single line separated with semicolons. 1391 | 1392 | 尽量避免在一行内写多个语句。 1393 | 1394 | **推荐(Preferred)**: 1395 | 1396 | ```swift 1397 | let swift = "not a scripting language" 1398 | ``` 1399 | 1400 | **不推荐(Not Preferred)**: 1401 | 1402 | ```swift 1403 | let swift = "not a scripting language"; 1404 | ``` 1405 | 1406 | **NOTE**: Swift is very different from JavaScript, where omitting semicolons is [generally considered unsafe](https://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript) 1407 | 1408 | 注:Swift 非常不同于 JavaScript。在 JavaScript 中忽略分号 [一般被认为是不安全的](https://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript)。 1409 | 1410 | ## 括号/Parentheses 1411 | 1412 | Parentheses around conditionals are not required and should be omitted. 1413 | 1414 | 条件周围的括号是不必要的,应该被忽略。 1415 | 1416 | **推荐(Preferred)**: 1417 | 1418 | ```swift 1419 | if name == "Hello" { 1420 | print("World") 1421 | } 1422 | ``` 1423 | 1424 | **不推荐(Not Preferred)**: 1425 | 1426 | ```swift 1427 | if (name == "Hello") { 1428 | print("World") 1429 | } 1430 | ``` 1431 | 1432 | In larger expressions, optional parentheses can sometimes make code read more clearly. 1433 | 1434 | 在更大的表达式中,额外的括号有时可以让代码读起来更清晰。 1435 | 1436 | **推荐(Preferred)**: 1437 | 1438 | ```swift 1439 | let playerMark = (player == current ? "X" : "O") 1440 | ``` 1441 | 1442 | ## 多行字符串字面量/Multi-line String Literals 1443 | 1444 | When building a long string literal, you're encouraged to use the multi-line string literal syntax. Open the literal on the same line as the assignment but do not include text on that line. Indent the text block one additional level. 1445 | 1446 | 当创建一个较长的字符串字面量时,你可以使用多行字符串字面量的便利语法。在赋值语句处开始创建这个字符串字面量,但是不包含任何文字。真正的内容将在第二行出现并缩进一个层级。 1447 | 1448 | **推荐(Preferred)**: 1449 | 1450 | ```swift 1451 | let message = """ 1452 | You cannot charge the flux \ 1453 | capacitor with a 9V battery. 1454 | You must use a super-charger \ 1455 | which costs 10 credits. You currently \ 1456 | have \(credits) credits available. 1457 | """ 1458 | ``` 1459 | 1460 | **不推荐(Not Preferred)**: 1461 | 1462 | ```swift 1463 | let message = """You cannot charge the flux \ 1464 | capacitor with a 9V battery. 1465 | You must use a super-charger \ 1466 | which costs 10 credits. You currently \ 1467 | have \(credits) credits available. 1468 | """ 1469 | ``` 1470 | 1471 | **不推荐(Not Preferred)**: 1472 | 1473 | ```swift 1474 | let message = "You cannot charge the flux " + 1475 | "capacitor with a 9V battery.\n" + 1476 | "You must use a super-charger " + 1477 | "which costs 10 credits. You currently " + 1478 | "have \(credits) credits available." 1479 | ``` 1480 | 1481 | ## 不要使用 Emoji/No Emoji 1482 | 1483 | Do not use emoji in your projects. For those readers who actually type in their code, it's an unnecessary source of friction. While it may be cute, it doesn't add to the learning and it interrupts the coding flow for these readers. 1484 | 1485 | 不要在工程里使用 Emoji。对于读者而言,输入这一类型的代码意义并不大。虽然看起来十分可爱,但它对于学习的帮助不大且会打乱输入代码的节奏。 1486 | 1487 | ## 不要使用 #imageLiteral 和 #colorLiteral/No #imageLiteral or #colorLiteral 1488 | 1489 | Likewise, do not use Xcode's ability to drag a color or an image into a source statement. These turn into #colorLiteral and #imageLiteral, respectively, and present unpleasant challenges for a reader trying to enter them based on tutorial text. Instead, use `UIColor(red:green:blue)` and `UIImage(imageLiteralResourceName:)`. 1490 | 1491 | 同样地,不要使用 Xcode 提供的 #colorLiteral 和# imageLiteral 来设置颜色或图像,这会给读者带来学习不好的体验。因此,推荐使用`UIColor(red:green:blue)`和`UIImage(imageLiteralResourceName:)`。 1492 | 1493 | ## 组织名称和包标识/Organization and Bundle Identifier 1494 | 1495 | Where an Xcode project is involved, the organization should be set to `Kodeco` and the Bundle Identifier set to `com.yourcompany.TutorialName` where `TutorialName` is the name of the tutorial project. 1496 | 1497 | 涉及到 Xcode 项目的地方,组织应该被设置为 `Kodeco` 并且包标识符应该被设置为 `com.yourcompany.TutorialName`,其中 `TutorialName` 是教程的名字。 1498 | 1499 | ![Xcode Project settings](screens/project_settings.png) 1500 | 1501 | ## 版权声明/Copyright Statement 1502 | 1503 | The following copyright statement should be included at the top of every source file: 1504 | 1505 | 以下版权声明应该被包含在每个源文件的顶部: 1506 | 1507 | ```swift 1508 | /// Copyright (c) 2023 Kodeco Inc. 1509 | /// 1510 | /// Permission is hereby granted, free of charge, to any person obtaining a copy 1511 | /// of this software and associated documentation files (the "Software"), to deal 1512 | /// in the Software without restriction, including without limitation the rights 1513 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1514 | /// copies of the Software, and to permit persons to whom the Software is 1515 | /// furnished to do so, subject to the following conditions: 1516 | /// 1517 | /// The above copyright notice and this permission notice shall be included in 1518 | /// all copies or substantial portions of the Software. 1519 | /// 1520 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, 1521 | /// distribute, sublicense, create a derivative work, and/or sell copies of the 1522 | /// Software in any work that is designed, intended, or marketed for pedagogical or 1523 | /// instructional purposes related to programming, coding, application development, 1524 | /// or information technology. Permission for such use, copying, modification, 1525 | /// merger, publication, distribution, sublicensing, creation of derivative works, 1526 | /// or sale is expressly withheld. 1527 | /// 1528 | /// This project and source code may use libraries or frameworks that are 1529 | /// released under various Open-Source licenses. Use of those libraries and 1530 | /// frameworks are governed by their own individual licenses. 1531 | /// 1532 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1533 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1534 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1535 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1536 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1537 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 1538 | /// THE SOFTWARE. 1539 | ``` 1540 | 1541 | ## 笑脸/Smiley Face 1542 | 1543 | Smiley faces are a very prominent style feature of the [Kodeco](https://www.kodeco.com/) site! It is very important to have the correct smile signifying the immense amount of happiness and excitement for the coding topic. The closing square bracket `]` is used because it represents the largest smile able to be captured using ASCII art. A closing parenthesis `)` creates a half-hearted smile, and thus is not preferred. 1544 | 1545 | 笑脸是 [Kodeco](https://www.kodeco.com/) 网站非常显著的风格特点!拥有正确的笑容能传达出对编程主题的极度幸福和兴奋之情非常重要。闭合方括号 `]` 被用来表示能通过 ASCII 艺术表达的最大笑容。而闭合括号 `)` 则只能形成一个半喜悦的笑容,因此并不理想。 1546 | 1547 | **推荐(Preferred)**: 1548 | 1549 | ```swift 1550 | :] 1551 | ``` 1552 | 1553 | **不推荐(Not Preferred)**: 1554 | 1555 | ```swift 1556 | :) 1557 | ``` 1558 | 1559 | ## 参考/References 1560 | 1561 | * [The Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) 1562 | * [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html) 1563 | * [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html) 1564 | * [Swift Standard Library Reference](https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/index.html) 1565 | -------------------------------------------------------------------------------- /SWIFTLINT.markdown: -------------------------------------------------------------------------------- 1 | # The Official Kodeco SwiftLint Policy 2 | 3 | The SwiftLint configuration in this repo is designed to ensure the work we create at Kodeco conforms with [The Official Kodeco Swift Style Guide](https://github.com/kodecocodes/swift-style-guide/blob/main/README.markdown). 4 | 5 | 本仓库中的 SwiftLint 配置旨在确保我们在 Kodeco 创建的工作符合 [The Official Kodeco Swift Style Guide](https://github.com/kodecocodes/swift-style-guide/blob/main/README.markdown)。 6 | 7 | The focus of this style is to improve readability in our print and web publications. Therefore, this style may be different from others you've used, but the demands of print and online reading are different from other contexts. 8 | 9 | 本样式指南的重点是提高我们印刷和网页出版物的可读性。因此,本样式指南可能与您以前使用的其他样式指南有所不同,但印刷和在线阅读的需求与其他场景有所不同。 10 | 11 | The policies described here have a goal of achieving consistency between all of our projects, which will also streamline the flow of content through our editing process. Some of the choices also aim to ensure we've removed as much of the burden from our readers as possible. 12 | 13 | 这里描述的策略旨在实现我们所有项目之间的一致性,这也会进一步提高我们的工作效率。某些选择也旨在确保我们尽可能减轻读者的负担。 14 | 15 | These guides use SwiftLint as a standard. You can learn more about SwiftLint by visiting its [official documentation page](https://github.com/realm/SwiftLint). 16 | 17 | 这些指南以 SwiftLint 为标准。您可以通过访问其[官方文档页面]((https://github.com/realm/SwiftLint))了解更多关于 SwiftLint 的信息。 18 | 19 | 20 | ## Table of Contents 21 | 22 | * [安装 SwiftLint/Installing SwiftLint](#installing-swiftlint) 23 | * [使用配置文件/Using the Configuration File](#using-the-configuration-file) 24 | * [Xcode 配置/Xcode Settings](#xcode-settings) 25 | * [运行 SwiftLine/Running SwiftLint](#running-swiftlint) 26 | * [处理异常规则/Handling Rule Exceptions](#handling-rule-exceptions) 27 | * [预期中的异常/Approved Exceptions](#approved-exceptions) 28 | * [隐式解包/Implicitly Unwrapped Optionals](#implicitly-unwrapped-optionals) 29 | * [强制转换/Force Cast](#force-cast) 30 | * [强制解包/Force Unwrapping](#force-unwrapping) 31 | * [SwiftUI 和多尾随闭包/SwiftUI and Multiple Trailing Closures](#swiftui-and-multiple-trailing-closures) 32 | * [开源文件/Open-Source Files](#open-source-files) 33 | * [其他注意事项/Other Notes](#other-notes) 34 | 35 | ## 安装 SwiftLint/Installing SwiftLint 36 | 37 | We recommend that Kodeco team members install SwiftLint using Homebrew: 38 | 39 | 我们建议 Kodeco 团队成员使用 Homebrew 安装 SwiftLint: 40 | 41 | ```bash 42 | brew install swiftlint 43 | ``` 44 | 45 | If you are unable to use Homebrew, you may use one of the other methods described in the [SwiftLint documentation](https://github.com/realm/SwiftLint). 46 | 47 | 如果您无法使用 Homebrew,可以使用 SwiftLint 文档中描述的其他方法之一。 48 | 49 | **Do not** install SwiftLint as a CocoaPod in your project. 50 | 51 | **不要**在您的项目中将 SwiftLint 作为 CocoaPod 的包进行安装。 52 | 53 | ## 使用配置文件/Using the Configuration File 54 | 55 | **Do not** place the configuration file inside your project. We don't want to impose this style on readers without their express knowledge or understanding of what's going on. 56 | 57 | **不要**将配置文件放置在您的项目内部。我们不想在读者没有明确知晓或理解发生的情况下强加此样式给他们。 58 | 59 | Download **com.raywenderlich.swiftlint.yml** from the [Swift Style Guide repo]([https://github.com/raywenderlich/swift-style-guide](https://github.com/kodecocodes/swift-style-guide/)) and place it your home directory: **~/com.raywenderlich.swiftlint.yml**. 60 | 61 | 从 [Swift Style Guide repo]([https://github.com/raywenderlich/swift-style-guide](https://github.com/kodecocodes/swift-style-guide/)) 下载 **com.raywenderlich.swiftlint.yml**,并将其放置在您的主目录中:**~/com.raywenderlich.swiftlint.yml**。 62 | 63 | 64 | ## Xcode 设置/Xcode Settings 65 | 66 | You'll need to configure Xcode to remove trailing whitespace from all lines. This is not the default configuration. 67 | 68 | 您需要配置 Xcode 以删除所有行的尾随空白。这不是默认配置。 69 | 70 | In Xcode's Preferences, select **Text Editing** ▸ **Editing** and check **Including whitespace-only lines**. 71 | 72 | 在 Xcode 的首选项中,选择 **Text Editing ▸ Editing**,并勾选 **Including whitespace-only lines**。 73 | 74 | ![](screens/trailing-whitespace.png) 75 | 76 | ## 运行 SwiftLint/Running SwiftLint 77 | 78 | To simplify the process for everyone in the content pipeline, you'll need to add the necessary instructions to your sample project to run SwiftLint automatically on each build. To do so: 79 | 80 | 为了简化工作流中每个人的流程,您需要为样例项目添加必要的说明,以便在每次构建时自动运行 SwiftLint。要做到这一点: 81 | 82 | 1. 在项目导航器中选择项目文档。 / Select the project document in the Project navigator. 83 | 1. 选择 **Build Phases** 选项卡。 / Select the **Build Phases** tab. 84 | 1. 点击 **+** 并选择 **New Run Script Phase**。/ Click **+** and choose **New Run Script Phase**. 85 | 86 | ![](screens/add-run-script.png) 87 | 88 | 4. 将新创建的阶段拖动到 **Compile Sources** 阶段之前。/ Drag the new phase before the **Compile Sources** phase. 89 | 4. 点击 **Run Script** 阶段的披露三角形,并确保 **Shell** 设置为 **/bin/sh**。/ Click the disclosure triangle on the **Run Script** phase and ensure **Shell** is set to **/bin/sh**. 90 | 91 | ![](screens/empty-run-script.png) 92 | 93 | 6. 添加以下脚本 / Add the following script: 94 | ``` 95 | PATH=/opt/homebrew/bin:$PATH 96 | if [ -f ~/com.raywenderlich.swiftlint.yml ]; then 97 | if which swiftlint >/dev/null; then 98 | swiftlint --no-cache --config ~/com.raywenderlich.swiftlint.yml 99 | fi 100 | fi 101 | ``` 102 | 103 | ## 处理异常规则/Handling Rule Exceptions 104 | 105 | Your sample project must compile without warnings — SwiftLint or otherwise. In general, you should change your code to eliminate all warnings where necessary. When it comes to SwiftLint, however, there will be times when this isn't possible. In these situations, you'll need to use in-line comments to temporarily disable rules. You can find appropriate syntax to do this in [the SwiftLint documentation](https://realm.github.io/SwiftLint/#disable-rules-in-code). 106 | 107 | 您的示例项目必须在没有警告的情况下进行编译 —— 不管是 SwiftLint 还是其他警告。通常,您应该根据需要更改代码以消除所有警告。但是,当涉及到 SwiftLint 时,有时这是不可能的。在这种情况下,您需要使用内联注释来临时禁用规则。您可以在 [the SwiftLint documentation](https://realm.github.io/SwiftLint/#disable-rules-in-code) 文档中找到合适的禁用规则的语法。 108 | 109 | You may only disable a rule if it is on the list of approved exceptions listed below. 110 | 111 | 您只能禁用下面列出的经批准的异常规则列表中的规则。 112 | 113 | Prefer the form that disables a rule only for the next line: 114 | 115 | 优先使用仅禁用下一行的规则的形式: 116 | 117 | ``` 118 | // swiftlint:disable:next implicitly_unwrapped_optional 119 | var injectedValue: Data! 120 | ``` 121 | 122 | Or, similarly, for the previous line: 123 | 124 | 或者,对于前一行实施类似的规则: 125 | 126 | ``` 127 | var injectedValue: Data! 128 | // swiftlint:disable:previous implicitly_unwrapped_optional 129 | ``` 130 | 131 | If there are several approved exceptions, group them together and disable the rule for that region. Be sure to enable the rule after that section. Do not leave a rule disabled throughout the source file. 132 | 133 | 如果有多个预期内的异常,请将其组合在一起,并对该区域禁用规则。一定要在该部分之后再启用规则。不要在整个源文件中保持规则被禁用状态。 134 | 135 | ``` 136 | // swiftlint:disable implicitly_unwrapped_optional 137 | var injectedValue1: Data! 138 | var injectedValue2: Data! 139 | // swiftlint:enable implicitly_unwrapped_optional 140 | ``` 141 | 142 | If you must disable rules in your project, leave those in-line comments in the project for the benefit of your teammates in the editing pipeline. 143 | 144 | 如果您必须在项目中禁用规则,请为工作流中的队友留下这些内联注释。 145 | 146 | Finally, if you're not sure which rule is triggering a warning, you can find the rule name in parentheses at the end of message: 147 | 148 | 最后,如果您不确定哪条规则触发了警告,可以在消息末尾的括号中找到规则名称: 149 | 150 | ![](screens/swiftlint-warning.png) 151 | 152 | ## 预期中的异常/Approved Exceptions 153 | 154 | There are certain common idioms that violate SwiftLint's strict checking. If you are unable to work around them — and you've already tried to find a better way to structure your code — you may disable rules as described in this section. 155 | 156 | 有些常见的用法违反了 SwiftLint 的严格检查。如果您无法解决它们 - 并且您已经尝试找到重构代码的更好方法 - 您可以按本节所述禁用规则。 157 | 158 | If you find that you're struggling with rules other than those described below, please reach out to your Team Lead with your specific example. 159 | 160 | 如果您发现除了下面描述的规则之外,您还在努力遵守其他规则,请随时与您的团队主管联系,提供您的具体例子。 161 | 162 | ### 隐式解包/Implicitly Unwrapped Optionals 163 | 164 | It is sometimes common, in lieu of using dependency injection, to declare a child view controller's properties as Implicitly Unwrapped Optionals (IUO). If you're unable to structure your project to avoid this, you may disable the `implicitly_unwrapped_optional` rule for those dependency declarations. With the advent of `@IBSegueAction`, this should be rare. 165 | 166 | 有时,为了避免使用依赖注入,会将子视图控制器的属性声明为隐式解包可选项(IUO)。如果您无法对项目进行结构化以避免此情况,您可以对这些依赖关系声明禁用 `implicitly_unwrapped_optional` 规则。随着 `@IBSegueAction` 的出现,这种情况应该很少见。 167 | 168 | ### 强制转换/Force Cast 169 | 170 | You may use force casting — and disable the `force_cast` rule — in the `UITableViewDataSource` and `UICollectionViewDataSource` methods that dequeue cells. 171 | 172 | 在 `UITableViewDataSource` 和 `UICollectionViewDataSource` 的相关方法中取出 Cell 时,您可以使用强制转换 - 并禁用 `force_cast` 规则。 173 | 174 | ### 强制解包/Force Unwrapping 175 | 176 | You may use forced unwrapping — rule name `force_unwrap` — when returning a color from an asset catalog: 177 | 178 | 从资源目录获取颜色时,您可以使用强制解包 - 规则名称为 `force_unwrap`: 179 | 180 | ``` 181 | static var rwGreen: UIColor { 182 | // swiftlint:disable:next force_unwrapping 183 | UIColor(named: "rw-green")! 184 | } 185 | ``` 186 | 187 | You may also use it in the same context as the force cast exception above, dequeuing cells in `UITableViewDataSource` and `UICollectionViewDataSource` methods. 188 | 189 | 在上面强制转换的上下文中,也即在 UITableViewDataSource 和 UICollectionViewDataSource 方法中取出 Cell 时,您也可以使用这个规则。 190 | 191 | Although we prefer you to model appropriately defensive code for our readers, you may use force unwrapping to access resources that you _know_ are included in the app bundle. 192 | 193 | 虽然我们更喜欢您为读者编写适当的防御性代码,但是您可以使用强制解包访问您确定包含在应用程序 bundle 中的资源。 194 | 195 | Finally, you may use force unwrapping when constructing a `URL` from a hard-coded, and guaranteed valid, URL string. 196 | 197 | 最后,在从硬编码且保证有效的 URL 字符串构造 `URL` 时,您可以使用强制解包。 198 | 199 | ### SwiftUI 和多个尾随闭包/SwiftUI and Multiple Trailing Closures 200 | 201 | Idiomatic SwiftUI uses trailing closures to provide the view content for certain user interface elements. `Button` is a prime example; it has an initializer form that uses a closure to provide its `label`. It's common to write something like the following: 202 | 203 | 惯用的 SwiftUI 使用尾随闭包为某些用户界面元素提供视图内容。`Button` 就是一个很好的例子;它有一个使用闭包提供其 `label` 的初始化器形式。编写如下代码很常见: 204 | 205 | ``` 206 | Button(action: { self.isPresented.toggle() }) { 207 | Image(systemName: "plus") 208 | } 209 | ``` 210 | 211 | This violates the rule that you should not use trailing closure syntax when a method accepts multiple closure parameters, so SwiftLint will flag it with a warning. 212 | 213 | 这违反了当方法接受多个闭包参数时不应使用尾随闭包语法的规则,因此 SwiftLint 会发出警告。 214 | 215 | You can work around this by extracting the `Button`'s action into a separate method. While this is frequently a better solution when the action requires several statements, it's an onerous requirement when the action is a single statement, as in the example above. 216 | 217 | 您可以通过将 `Button` 的 action 提取到一个单独的方法中来解决这个问题。虽然当 action 需要多个语句时这通常是更好的解决方案,但当 action 仅是一个语句时,就像上面的例子一样,这是一项艰巨的要求。 218 | 219 | In these cases, you're permitted to disable this rule **for the declaration of a SwiftUI view** only. The rule name is `multiple_closures_with_trailing_closure`. 220 | 221 | 在这些情况下,您只能在**声明 SwiftUI 视图**时禁用此规则。规则名称是 `multiple_closures_with_trailing_closure`。 222 | 223 | ### 开源文件/Open-Source Files 224 | 225 | Occasionally, you'll find it necessary to include an unmodified open-source file in the sample project. It's a virtual certainty that these files won't comply with our style guide. Our practice has always been to leave these files in their original state. In this situation, you should disable SwiftLint for the entire file: 226 | 227 | 有时候,在示例项目中包含未修改的开源文件是必要的。这些文件遵循我们的样式指南的可能性肯定微乎其微。我们的惯例一直是将这些文件保留在原始状态。在这种情况下,您应该为整个文件禁用 SwiftLint: 228 | 229 | ``` 230 | // swiftlint:disable all 231 | ``` 232 | 233 | ## 其他注意事项/Other Notes 234 | 235 | While SwiftLint goes a long way towards making your source code compliant with our style guide, it doesn't cover everything. For example, it won't catch or force you to correct the formatting for multi-condition `guard` statements. (See [Golden Path](https://github.com/raywenderlich/swift-style-guide#golden-path) for correct formatting.) 236 | 237 | 虽然 SwiftLint 在很大程度上使您的源代码符合我们的样式指南,但它并不能涵盖所有内容。例如,它不会捕获或强制您修正多条件 `guard` 语句的格式。(请参阅 [Golden Path](https://github.com/raywenderlich/swift-style-guide#golden-path) 以获取正确的格式。) 238 | 239 | This configuration has been tested against several dozen of our most recent tutorials. A couple of rules, such as the line length limit or the limit on the length of a function, may need tweaking to fit our style. If you find yourself butting heads with SwiftLint, please reach out to the iOS Team Lead with details. 240 | 241 | 该配置已针对我们最近的几十个教程进行了测试。一些规则,比如行长度限制或函数长度限制,可能需要调整以符合我们的风格。如果您发现自己与 SwiftLint 发生冲突,请随时联系 iOS 团队负责人提供详细信息。 242 | -------------------------------------------------------------------------------- /com.raywenderlich.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - ${PWD}/Carthage 3 | - ${PWD}/Pods 4 | - ${PWD}/DerivedData 5 | 6 | disabled_rules: 7 | - discarded_notification_center_observer 8 | - notification_center_detachment 9 | - orphaned_doc_comment 10 | - todo 11 | - unused_capture_list 12 | 13 | analyzer_rules: 14 | - unused_import 15 | 16 | opt_in_rules: 17 | - array_init 18 | - attributes 19 | - closure_end_indentation 20 | - closure_spacing 21 | - collection_alignment 22 | - colon # promote to error 23 | - convenience_type 24 | - discouraged_object_literal 25 | - empty_collection_literal 26 | - empty_count 27 | - empty_string 28 | - enum_case_associated_values_count 29 | - fatal_error_message 30 | - first_where 31 | - force_unwrapping 32 | - implicitly_unwrapped_optional 33 | - indentation_width 34 | - last_where 35 | - legacy_random 36 | - literal_expression_end_indentation 37 | - multiline_arguments 38 | - multiline_function_chains 39 | - multiline_literal_brackets 40 | - multiline_parameters 41 | - multiline_parameters_brackets 42 | - operator_usage_whitespace 43 | - overridden_super_call 44 | - pattern_matching_keywords 45 | - prefer_self_type_over_type_of_self 46 | - redundant_nil_coalescing 47 | - redundant_type_annotation 48 | - strict_fileprivate 49 | - toggle_bool 50 | - trailing_closure 51 | - unneeded_parentheses_in_closure_argument 52 | - vertical_whitespace_closing_braces 53 | - vertical_whitespace_opening_braces 54 | - yoda_condition 55 | 56 | 57 | custom_rules: 58 | array_constructor: 59 | name: "Array/Dictionary initializer" 60 | regex: '[let,var] .+ = (\[.+\]\(\))' 61 | capture_group: 1 62 | message: "Use explicit type annotation when initializing empty arrays and dictionaries" 63 | severity: warning 64 | 65 | 66 | attributes: 67 | always_on_same_line: 68 | - "@IBSegueAction" 69 | - "@IBAction" 70 | - "@NSManaged" 71 | - "@objc" 72 | 73 | force_cast: warning 74 | force_try: warning 75 | function_body_length: 76 | warning: 60 77 | 78 | legacy_hashing: error 79 | 80 | identifier_name: 81 | excluded: 82 | - i 83 | - id 84 | - x 85 | - y 86 | - z 87 | 88 | indentation_width: 89 | indentation_width: 2 90 | 91 | line_length: 92 | ignores_urls: true 93 | ignores_function_declarations: true 94 | ignores_comments: true 95 | 96 | multiline_arguments: 97 | first_argument_location: next_line 98 | only_enforce_after_first_closure_on_first_line: true 99 | 100 | private_over_fileprivate: 101 | validate_extensions: true 102 | 103 | trailing_whitespace: 104 | ignores_empty_lines: false 105 | ignores_comments: true 106 | 107 | vertical_whitespace: 108 | max_empty_lines: 2 109 | -------------------------------------------------------------------------------- /screens/add-run-script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/add-run-script.png -------------------------------------------------------------------------------- /screens/empty-run-script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/empty-run-script.png -------------------------------------------------------------------------------- /screens/indentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/indentation.png -------------------------------------------------------------------------------- /screens/project_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/project_settings.png -------------------------------------------------------------------------------- /screens/swiftlint-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/swiftlint-warning.png -------------------------------------------------------------------------------- /screens/trailing-whitespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/trailing-whitespace.png -------------------------------------------------------------------------------- /screens/xcode-jump-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SwiftSIQI/swift-style-guide-by-kodeco-in-chinese/47306e8221808e1538967f27344daf2869816e18/screens/xcode-jump-bar.png --------------------------------------------------------------------------------