参考:linkedin/swift-style-guide 因为规范主要参照谷歌的 style guide 执行,所以有一些谷歌没提到的,linkedin 提到的就补充进来。
Code Formatting
如果是多个条件判断语句,提前用变量表示判断条件。最后组合变量组合复合条件判断,而不是在一个语句中引入复杂的条件判断:
✅let firstCondition = x == firstReallyReallyLongPredicateFunction()let secondCondition = y == secondReallyReallyLongPredicateFunction()let thirdCondition = z == thirdReallyReallyLongPredicateFunction()if firstCondition && secondCondition && thirdCondition {// do something}❌if x == firstReallyReallyLongPredicateFunction()&& y == secondReallyReallyLongPredicateFunction()&& z == thirdReallyReallyLongPredicateFunction() {// do something}
命名
不要像 OC 一样在类的前面添加命名空间前缀。
泛型和协议中的 associatedtype 使用帕斯卡命名方式,如果 associatedtype 是一个常见的类型,在后面加上 “Type” 后缀表示区分。
protocol Modelable {associatedtype Model}protocol Sequence {associatedtype IteratorType: Iterator}
- 命名应该要描述出含义,避免歧义。
 
✅class RoundAnimatingButton: UIButton { /* ... */ }❌class CustomButton: UIButton { /* ... */ }
- 不要使用别人不理解的缩写,或者只有一个字母的名称。
 
❌class RoundAnimating: UIButton {let aniDur: NSTimeIntervalfunc srtAnmating() {let v = subviews.first}}
- 常量、变量的类型信息如果不明显并且会引起歧义,在名称中加入类型信息。
 
✅class ConnectionTableViewCell: UITableViewCell {let personImageView: UIImageViewlet animationDuration: TimeInterval// 名字是字符串非常明显,不需要补充类型信息let firstName: String// 需要让人看出是一个 Controller,至于是不是叫 ViewController 则都可以接受let popupController: UIViewControllerlet popupViewController: UIViewController// 需要显式让调用者知道属性的具体类型时,也把类型信息信息写到命名中let popupTableViewController: UITableViewController// 应该总是让人看出是一个类型的对象@IBOutlet weak var submitButton: UIButton!@IBOutlet weak var emailTextField: UITextField!@IBOutlet weak var nameLabel: UILabel!}
❌class ConnectionTableViewCell: UITableViewCell {// 这样会让人以为是图片,而不是图片控件let personImage: UIImageView// 这样会让人以为是一个字符串let text: UILabel// 这个名称会让人不知道应该使用什么赋值,animation 与 interval 完全不相关。let animation: TimeInterval// 这里是一个需要展示的文本,应该命名为 `transitionText` 或者 `transitionString`let transition: String// 这会让人误以为是一个 UIView 对象let popupView: UIViewController// 不要使用缩写,所以缩写成 VC 不好let popupVC: UIViewController// 显式的让外面知道这是一个 TableViewControllerlet popupViewController: UITableViewController// 为了保持一致性,所有的类型都写在末尾@IBOutlet weak var btnSubmit: UIButton!@IBOutlet weak var buttonSubmit: UIButton!// outlet 应该总是有类型信息在末尾,这里应该是改成 firstNameLabel@IBOutlet weak var firstName: UILabel!}
Coding Style
常规
- 灵活运用函数式操作 map, filter, reduce 等。
 
✅let stringOfInts = [1, 2, 3].flatMap { String($0) }// ["1", "2", "3"]❌var stringOfInts: [String] = []for integer in [1, 2, 3] {stringOfInts.append(String(integer))}✅let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }// [4, 8, 16, 42]❌var evenNumbers: [Int] = []for integer in [4, 8, 15, 16, 23, 42] {if integer % 2 == 0 {evenNumbers.append(integer)}}
- 如果一个枚举的实参可以被推断,尽量使用缩写。
 
✅imageView.imageView.setImageWithURLsetImageWithURL(url, type: .person)❌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 不应该到达,可以在语句中抛出异常。
func handleDigit(_ digit: Int) throws {switch digit {case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9:print("Yes, \(digit) is a digit!")default:throw Error(message: "The given number was not a digit.")}}
Optional
- 如果只是判断值是不是为空,使用 != nil 判断。
 
✅if someOptional != nil {// do something}❌if let _ = someOptional {// do something}
- 解包时推荐使用原有的名字。
 
✅guard let myValue = myValue else { return }
在 Extension 中实现 Protocol 要慎重
选择在 Extension 中实现 Protocol 的方法不能被 override,选择这种方式要考虑到未来是否有被 override 的可能。
Guard
- 如果解包的值后面的逻辑需要使用,推荐使用 guard 而不是 if。
 
✅guard let monkeyIsland = monkeyIsland else { return }bookVacation(on: monkeyIsland)bragAboutVacation(at: monkeyIsland)❌if let monkeyIsland = monkeyIsland {bookVacation(on: monkeyIsland)bragAboutVacation(at: monkeyIsland)}❌if monkeyIsland == nil { return }bookVacation(on: monkeyIsland!)bragAboutVacation(at: monkeyIsland!)
- 如果是一个逻辑分支,应该使用 if 。
 
✅if isFriendly {print("Hello, nice to meet you!")} else {print("You have the manners of a beggar.")}❌guard isFriendly else {print("You have the manners of a beggar.")return}print("Hello, nice to meet you!")
