参考:linkedin/swift-style-guide 因为规范主要参照谷歌的 style guide 执行,所以有一些谷歌没提到的,linkedin 提到的就补充进来。

Code Formatting

如果是多个条件判断语句,提前用变量表示判断条件。最后组合变量组合复合条件判断,而不是在一个语句中引入复杂的条件判断:

  1. let firstCondition = x == firstReallyReallyLongPredicateFunction()
  2. let secondCondition = y == secondReallyReallyLongPredicateFunction()
  3. let thirdCondition = z == thirdReallyReallyLongPredicateFunction()
  4. if firstCondition && secondCondition && thirdCondition {
  5. // do something
  6. }
  7. if x == firstReallyReallyLongPredicateFunction()
  8. && y == secondReallyReallyLongPredicateFunction()
  9. && z == thirdReallyReallyLongPredicateFunction() {
  10. // do something
  11. }

命名

  • 不要像 OC 一样在类的前面添加命名空间前缀。

  • 泛型和协议中的 associatedtype 使用帕斯卡命名方式,如果 associatedtype 是一个常见的类型,在后面加上 “Type” 后缀表示区分。

  1. protocol Modelable {
  2. associatedtype Model
  3. }
  4. protocol Sequence {
  5. associatedtype IteratorType: Iterator
  6. }
  • 命名应该要描述出含义,避免歧义。
  1. class RoundAnimatingButton: UIButton { /* ... */ }
  2. class CustomButton: UIButton { /* ... */ }
  • 不要使用别人不理解的缩写,或者只有一个字母的名称。
  1. class RoundAnimating: UIButton {
  2. let aniDur: NSTimeInterval
  3. func srtAnmating() {
  4. let v = subviews.first
  5. }
  6. }
  • 常量、变量的类型信息如果不明显并且会引起歧义,在名称中加入类型信息。
  1. class ConnectionTableViewCell: UITableViewCell {
  2. let personImageView: UIImageView
  3. let animationDuration: TimeInterval
  4. // 名字是字符串非常明显,不需要补充类型信息
  5. let firstName: String
  6. // 需要让人看出是一个 Controller,至于是不是叫 ViewController 则都可以接受
  7. let popupController: UIViewController
  8. let popupViewController: UIViewController
  9. // 需要显式让调用者知道属性的具体类型时,也把类型信息信息写到命名中
  10. let popupTableViewController: UITableViewController
  11. // 应该总是让人看出是一个类型的对象
  12. @IBOutlet weak var submitButton: UIButton!
  13. @IBOutlet weak var emailTextField: UITextField!
  14. @IBOutlet weak var nameLabel: UILabel!
  15. }
  1. class ConnectionTableViewCell: UITableViewCell {
  2. // 这样会让人以为是图片,而不是图片控件
  3. let personImage: UIImageView
  4. // 这样会让人以为是一个字符串
  5. let text: UILabel
  6. // 这个名称会让人不知道应该使用什么赋值,animation 与 interval 完全不相关。
  7. let animation: TimeInterval
  8. // 这里是一个需要展示的文本,应该命名为 `transitionText` 或者 `transitionString`
  9. let transition: String
  10. // 这会让人误以为是一个 UIView 对象
  11. let popupView: UIViewController
  12. // 不要使用缩写,所以缩写成 VC 不好
  13. let popupVC: UIViewController
  14. // 显式的让外面知道这是一个 TableViewController
  15. let popupViewController: UITableViewController
  16. // 为了保持一致性,所有的类型都写在末尾
  17. @IBOutlet weak var btnSubmit: UIButton!
  18. @IBOutlet weak var buttonSubmit: UIButton!
  19. // outlet 应该总是有类型信息在末尾,这里应该是改成 firstNameLabel
  20. @IBOutlet weak var firstName: UILabel!
  21. }

Coding Style

常规

  • 灵活运用函数式操作 map, filter, reduce 等。
  1. let stringOfInts = [1, 2, 3].flatMap { String($0) }
  2. // ["1", "2", "3"]
  3. var stringOfInts: [String] = []
  4. for integer in [1, 2, 3] {
  5. stringOfInts.append(String(integer))
  6. }
  7. let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }
  8. // [4, 8, 16, 42]
  9. var evenNumbers: [Int] = []
  10. for integer in [4, 8, 15, 16, 23, 42] {
  11. if integer % 2 == 0 {
  12. evenNumbers.append(integer)
  13. }
  14. }
  • 如果一个枚举的实参可以被推断,尽量使用缩写。
  1. imageView.imageView.setImageWithURLsetImageWithURL(url, type: .person)
  2. imageView.imageView.setImageWithURLsetImageWithURL(url, type: AsyncImageView.Type.person)
  • 正常环境下访问实例的成员不要声明 self.

  • 如果一个函数没有参数,没有副作用,只返回一个值,推荐使用计算属性。

访问修饰符

  • 访问修饰符写在修饰符的前面,默认不用写 internal

自定义操作符

优先创建函数而不是自定义操作符。

Switch、Enum

  • Switch 中的 case 语句默认就有 break 的功能,所以不需要特别声明 break。

  • 如果 Enum 关联类型,尽量给关联类型命名带上标签。比如:case hunger(hungerLevel: Int)case hunger(Int) 好。

  • 如果有一个 case 不应该到达,可以在语句中抛出异常。

  1. func handleDigit(_ digit: Int) throws {
  2. switch digit {
  3. case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9:
  4. print("Yes, \(digit) is a digit!")
  5. default:
  6. throw Error(message: "The given number was not a digit.")
  7. }
  8. }

Optional

  • 如果只是判断值是不是为空,使用 != nil 判断。
  1. if someOptional != nil {
  2. // do something
  3. }
  4. if let _ = someOptional {
  5. // do something
  6. }
  • 解包时推荐使用原有的名字。
  1. guard let myValue = myValue else { return }

在 Extension 中实现 Protocol 要慎重

选择在 Extension 中实现 Protocol 的方法不能被 override,选择这种方式要考虑到未来是否有被 override 的可能。

Guard

  • 如果解包的值后面的逻辑需要使用,推荐使用 guard 而不是 if。
  1. guard let monkeyIsland = monkeyIsland else { return }
  2. bookVacation(on: monkeyIsland)
  3. bragAboutVacation(at: monkeyIsland)
  4. if let monkeyIsland = monkeyIsland {
  5. bookVacation(on: monkeyIsland)
  6. bragAboutVacation(at: monkeyIsland)
  7. }
  8. if monkeyIsland == nil { return }
  9. bookVacation(on: monkeyIsland!)
  10. bragAboutVacation(at: monkeyIsland!)
  • 如果是一个逻辑分支,应该使用 if 。
  1. if isFriendly {
  2. print("Hello, nice to meet you!")
  3. } else {
  4. print("You have the manners of a beggar.")
  5. }
  6. guard isFriendly else {
  7. print("You have the manners of a beggar.")
  8. return
  9. }
  10. print("Hello, nice to meet you!")