每行代码长度限制
Swift 每行代码长度应该小于 100 个字符,或者说阅读的时候不应该需要滚动屏幕,在正常的实现范围里可以完整看到代码。除了以下列出的情况,否则超过这个长度应该换行。
例外的情况:
如果是一串有意义的连续文本不需要换行,比如一句注释里的一个很长的 URL。
import 声明。
生成的代码。
括号
非空的 block 花括号默认使用 K&R style。比如:
while (x == y) {something()somethingelse()}
除了一些 Swift 特别要求的情况:
左花括号( { )前的代码不会换行,除非超过前面提到的代码长度超过限制。
左花括号后是一个换行,除非:
后面要声明闭包的参数,改为在 in 关键字后面换行。
符合每行只声明一件事里情况,忽略换行,把内容写在一行里。
如果是空的 block ,直接声明为
{ }。
如果右花括号( } )结束了一个声明,后面接上一个换行。比如如果右花括号后面跟的是 else ,那么后面就不会跟换行,而会写成这样
} else {衔接。
分号
无论是终止或者隔开声明都不会使用分号。也就是说分号只可能出现在文本中。
✅func printSum(_ a: Int, _ b: Int) {let sum = a + bprint(sum)}
❌func printSum(_ a: Int, _ b: Int) {let sum = a + b;print(sum);}
每行只声明一件事
每行最多只声明一件事,每行结尾用换行分隔。除非结尾跟的是一个总共只有一行声明的闭包。
✅guard let value = value else { return 0 }defer { file.close() }switch someEnum {case .first: return 5case .second: return 10case .third: return 20}let squares = numbers.map { $0 * $0 }var someProperty: Int {get { return otherObject.property }set { otherObject.property = newValue }}var someProperty: Int { return otherObject.somethingElse() }required init?(coder aDecoder: NSCoder) { fatalError("no coder") }
如果闭包是提前返回一个值,写在一行里可读性就会好一些。如果是一个正常的操作,可以视情况是否写在一行里。因为未来也有可能里面再增加代码的操作。
代码换行
代码中的空格
除了语言或者其他样式的要求,文字和注释之外,一个Unicode空格也只出现在以下地方:
条件关键字后面和跟着的括号
✅if (x == 0 && y == 0) || z == 0 {// ...}❌if(x == 0 && y == 0) || z == 0 {// ...}
如果闭包中的代码在同一行,左花括号的前面、后面,右花括号的前面有空格
✅let nonNegativeCubes = numbers.map { $0 * $0 * $0 }.filter { $0 >= 0 }❌let nonNegativeCubes = numbers.map { $0 * $0 * $0 } .filter { $0 >= 0 }❌let nonNegativeCubes = numbers.map{$0 * $0 * $0}.filter{$0 >= 0}
在任何二元或三元运算符的两边
还有以下的情况:
- 使用于赋值,初始化变量、属性,默认参数的等号两边。
 
✅var x = 5func sum(_ numbers: [Int], initialValue: Int = 0) {// ...}❌var x=5func sum(_ numbers: [Int], initialValue: Int=0) {// ...}
- 表示在协议中表示合成类型的 & 两边。
 
✅func sayHappyBirthday(to person: NameProviding & AgeProviding) {// ...}❌func sayHappyBirthday(to person: NameProviding&AgeProviding) {// ...}
- 自定义运算符的两边。
 
✅static func == (lhs: MyType, rhs: MyType) -> Bool {// ...}❌static func ==(lhs: MyType, rhs: MyType) -> Bool {// ...}
- 表示返回值的 -> 两边。
 
✅func sum(_ numbers: [Int]) -> Int {// ...}❌func sum(_ numbers: [Int])->Int {// ...}
- 例外:表示引用值、成员的点两边没有空格。
 
✅let width = view.bounds.width❌let width = view . bounds . width
- 例外:表示区域范围的 ..< 和 …两边没有空格。
 
✅for number in 1...5 {// ...}❌let substring = string[index..<string.endIndex]for number in 1 ... 5 {// ...}❌let substring = string[index ..< string.endIndex]
参数列表、数组、tuple、字典里的逗号后面有一个空格
✅let numbers = [1, 2, 3]❌let numbers = [1,2,3]let numbers = [1 ,2 ,3]let numbers = [1 , 2 , 3]
冒号的后面有一个空格
✅// 类型声明struct HashTable: Collection {// ...}struct AnyEquatable<Wrapped: Equatable>: Equatable {// ...}// 参数标签let tuple: (x: Int, y: Int)func sum(_ numbers: [Int]) {// ...}// 变量声明let number: Int = 5// 字典声明var nameAgeMap: [String: Int] = []// 字典字面量let nameAgeMap = ["Ed": 40, "Timmy": 9]
代码后的注释符号 // 与代码有两个空格距离
✅let initialFactor = 2 // Warm up the modulator.❌let initialFactor = 2 // Warm up the modulator.
表示字典、数组字面量的中括号外面有一个空格
✅let numbers = [1, 2, 3]❌let numbers = [ 1, 2, 3 ]
禁止变量、属性水平对齐
水平对齐是明确禁止的,除非是在写明显的表格数据时,省略对齐会损害可读性。引入水平对齐后,如果添加一个新的成员可能会需要其他成员再对齐一次,这给维护增加了负担。
✅struct DataPoint {var value: Intvar primaryColor: UIColor}❌struct DataPoint {var value: Intvar primaryColor: UIColor}
空白行(垂直方向的空白)
空白行出现在以下几种情况:
两组不同类型的连续成员之间。
以下两种情况除外:如果是两个存储属性,或者两个枚举的选项当做一个整体声明在一行里,可以不用加空行。空行可以用于这些属性间的逻辑分组。
如果两个属性强相关,也可以不用空行分割。比如一个是私有的存储属性,另个一个是封装了换个私有属性的计算属性。
在组织代码逻辑关系时,可以用空行隔开进行分组。
第一个成员前、最后一个成员后的空行不做要求,既不鼓励,也不反对。
规范里其他地方要求有空行的地方。
使用多行空白行也可以接受,但是如果没有特别的需求不建议这样做。
括号
最顶级的 if、guard、while、switch 的条件不使用括号。
✅if x == 0 {print("x is zero")}if (x == 0 || y == 1) && z == 2 {print("...")}❌if (x == 0) {print("x is zero")}if ((x == 0 || y == 1) && z == 2) {print("...")}
在有复杂的条件表达式,只有作者和 review 的人同时认为省略括号不会影响代码的可读性才会省略。不能假设每个读者都完全了解对 swift 的运算符优先级,所以这种情况下的括号提示用户的计算优先级是合理的。
