我的 Swift 代码书写规范

目前使用 swift 开发 iOS App 也有一段时间了, 为了让自己的代码更统一, 更美观, 我在参考他人代码的基础上制定了一套适合我自己的 Swift 代码书写规范.

代码书写原则

  • Swift语言应符合美式英语习惯
  • 类, 函数等, 左大括号不另一起行, 并且跟前面留有空格
  • 函数, 类中间要空一行
  • 代码逻辑不同块之间, 要空一行
  • 注释符号, 与注释内容之间加空格
  • 类继承, 参数名和类型之间等, 冒号前面不加空格, 但后面跟空格
  • 自定义操作符, 声明及实现, 两边都要有空格隔开
  • if 后面的 else, 跟着上一个 if 的右括号
  • switch中, caseswitch左对齐
  • 函数体长度不超过 200 行
  • 单行不能超过 200 个字符
  • 单类体长度不超过 300 行
  • 实现每个协议时, 在单独的 extension 里来实现
  • 闭包中的单表达式, 省略 return
  • 简单闭包, 写在同一行
  • 尾随闭包, 在单闭包参数时才使用
  • 过滤, 转换等, 优先使用 filter, map 等高阶函数简化代码
  • 使用 [weak self] 修饰的闭包, 闭包开始判断 self 的有效性
  • 能推断出来的类型, 不需要加类型限定
  • 单行注释, 优先使用 //
  • 异常的分支, 提前用 guard 结束.
  • 多个嵌套条件, 能合并的, 就合并到一个 if 中
  • 尽可能使用 private, fileprivate 来限制作用域
  • 尽可能使用 private, fileprivate 来限制作用域
  • 不使用强制解包
  • 不使用强制类型转换
  • 不使用 try!
  • 不使用隐式解包
  • 无用的代码(包括官方的模板注释)都需要删除, 对代码的注释除外
  • 等号两端有空格;
  • 代码尽可能自注释;
  • 尽量使用语法糖
  • 每个文件都以一个新的空行结束, 即回车
  • 任何地方都不能以空格结尾
  • 方法间有空的一行
  • 花括号的左括号与方法体在同一行, 右括号在新的一行
  • 冒号前面无空格, 后面有一个空格, 三目运算符? : 除外和空字典 [:] 除外
  • 逗号前面无空格, 后面有一个空格
  • 二元三元运算符的前后都有一个空格
  • (的右面和)的左面不能有空格
  • 类型名字使用 Pascal 命名法(大驼峰命名法), 比如 protocols, struct, enum, class, typedef, associatedtype 等
  • 函数, 方法, 属性, 常量, 变量, 参数, 枚举值等使用 camel 命名法(小驼峰命名法)
  • 尽量避免简称或缩略词, 通用缩略词应整体大写
  • 初始化方法中必须要使用 self, 或属性名相同时使用 self, 仅此两种情况
  • 注释全部使用 // 方法,  // 后必须跟着一个空格, 且注释要单独放在一个行中
  • 尽一切可能使用类型推断以减少冗余类型信息
  • 声明一个变量可以为 nil 时使用? , 当确定一个变量不是 nil 时在后面加!
  • 用 //MARK: - 的方法将同类放到一起, 比如动作方法放在一起, 便于理解, 增加代码可读性.

Swift 代码命名规范

变量, 常量

  • 小驼峰命名
  • 名词 + 名词 + 名词 + ...
  • 不能有动词
  • Rx 中的监听序列使用 动词 + 名词 + Subject 的形式命名
  • UI 控件使用 Lb, Btn, View 等结尾

案例:

  • let maximumWidgetCount = 100
  • let urlString: URLString
  • let userID: UserID
  • var deleteStockSubject = PublishSubject<StockModel>()

方法

  • 小驼峰命名
  • 动词 + 名词 + [介词]
  • 使用全称, 避免使用缩写

案例:

  • printError(myError)
  • removeObject(object, atIndex: index)
  • setBackgroundImage(myImage)
  • func getDateFromString(dateString: String) -> NSDate
  • func convertPointAt(column column: Int, row: Int) -> CGPoint

类, 结构体

  • 大驼峰命名
  • 名词

枚举

enum Shape {
    case rectangle
    case square
    case rightTriangle
    case equilateralTriangle
}

相关常用的方法命名实例

返回真伪值的方法

位置单词意义
Prefixis对象是否符合期待的状态isValid
Prefixcan对象能否执行所期待的动作canRemove
Prefixshould调用方执行某个命令或方法是好还是不好,应不应该, 或者说推荐还是不推荐shouldMigrate
Prefixhas对象是否持有所期待的数据和属性hasObservers
Prefixneeds调用方是否需要执行某个命令或方法needsMigrate

用来检查的方法

单词意义
ensure检查是否为期待的状态, 不是则抛出异常或返回error codeensureCapacity
validate检查是否为正确的状态, 不是则抛出异常或返回error codevalidateInputs

按需求才执行的方法

位置单词意义
SuffixIfNeeded需要的时候执行, 不需要的时候什么都不做drawIfNeeded
Prefixmight同上mightCreate
Prefixtry尝试执行, 失败时抛出异常或是返回errorcodetryCreate
SuffixOrDefault尝试执行, 失败时返回默认值getOrDefault
SuffixOrElse尝试执行, 失败时返回实际参数中指定的值getOrElse
Prefixforce强制尝试执行. error抛出异常或是返回值forceCreate, forceStop

异步相关方法

位置单词意义
Prefixblocking线程阻塞方法blockingGetUser
SuffixInBackground执行在后台的线程doInBackground
SuffixAsync异步方法sendAsync
SuffixSync对应已有异步方法的同步方法sendSync
Prefix or AlonescheduleJob和Task放入队列schedule, scheduleJob
Prefix or Alonepost同上postJob
Prefix or Aloneexecute执行异步方法(注: 我一般拿这个做同步方法名)execute, executeTask
Prefix or Alonestart同上start, startJob
Prefix or Alonecancel停止异步方法cancel, cancelJob
Prefix or Alonestop同上stop, stopJob

回调方法

位置单词意义
Prefixon事件发生时执行onCompleted
Prefixbefore事件发生前执行beforeUpdate
Prefixpre同上preUpdate
Prefixwill同上willUpdate
Prefixafter事件发生后执行afterUpdate
Prefixpost同上postUpdate
Prefixdid同上didUpdate
Prefixshould确认事件是否可以发生时执行shouldUpdate

操作对象生命周期的方法

单词意义
initialize初始化. 也可作为延迟初始化使用initialize
pause暂停onPause , pause
stop停止onStop, stop
abandon销毁的替代abandon
destroy同上destroy
dispose同上dispose

与集合操作相关的方法

单词意义
contains是否持有与指定对象相同的对象contains
add添加addJob
append添加appendJob
insert插入到下标ninsertJob
put添加与key对应的元素putJob
remove移除元素removeJob
enqueue添加到队列的最末位enqueueJob
dequeue从队列中头部取出并移除dequeueJob
push添加到栈头pushJob
pop从栈头取出并移除popJob
peek从栈头取出但不移除peekJob
find寻找符合条件的某物findById

与数据相关的方法

单词意义
create新创建createAccount
new新创建newAccount
from从既有的某物新建, 或是从其他的数据新建fromConfig
to转换toString
update更新既有某物updateAccount
load读取loadAccount
fetch远程读取fetchAccount
delete删除deleteAccount
remove删除removeAccount
save保存saveAccount
store保存storeAccount
commit保存commitChange
apply保存或应用applyChange
clear清除数据或是恢复到初始状态clearAll
reset清除数据或是恢复到初始状态resetAll

命名中常用的单词

单词意义
get获取
set设置
add增加
remove删除
create创建
destory移除
start启动
stop停止
open打开
close关闭
read读取
write写入
load载入
save保存
create创建
destroy销毁
begin开始
end结束
backup备份
restore恢复
import导入
export导出
split分割
merge合并
inject注入
extract提取
attach附着
detach脱离
bind绑定
separate分离
view查看
browse浏览
edit编辑
modify修改
select选取
mark标记
copy复制
paste粘贴
undo撤销
redo重做
insert插入
delete移除
add加入
append添加
clean清理
clear清除
index索引
sort排序
find查找
search搜索
increase增加
decrease减少
play播放
pause暂停
launch启动
run运行
compile编译
execute执行
debug调试
trace跟踪
observe观察
listen监听
build构建
publish发布
input输入
output输出
encode编码
decode解码
encrypt加密
decrypt解密
compress压缩
decompress解压缩
pack打包
unpack解包
parse解析
emit生成
connect连接
disconnect断开
send发送
receive接收
download下载
upload上传
refresh刷新
synchronize同步
update更新
revert复原
lock锁定
unlock解锁
checkout签出
checkin签入
submit提交
commit交付
push
pull
expand展开
collapse折叠
begin起始
end结束
start开始
finish完成
enter进入
exit退出
abort放弃
quit离开
obsolete废弃
depreciate废旧
collect收集
aggregate聚集

Swift 代码分区书写规范

  • // MARK: - 123 此为标记(特殊的注释), 前面的一个 - 可以在 JumpBar 中显示加粗分割线. 与此相类似的还有 // TODO:// FIXME: ,TODO表示代码还需完善, FIXME表示将相关代码标记, 以便重新或修复 bug.
  • 大驼峰, 每一个单词的首字母都大写, 例如: AnamialZoo, JavaScript中构造函数用的是大驼峰式写法; 小驼峰, 第一个单词的首字母小写, 后面的单词的首字母全部大写, 例如: fontSize, backgroundColor.
  • class 中书写顺序:
      // MARK: - IBOutlet
      @IBOutlet var ...
      
      // MARK: - Variable
      var ...
      
      // MARK: - Life Cycle
      func viewDidLoad()
      func viewWillAppear(_ animated: Bool)
      func viewDidAppear(_ animated: Bool)
      func viewWillDisappear(_ animated: Bool)
      func viewDidDisappear(_ animated: Bool)
      
      // MARK: - Customize Method
      func ...
      
      // MARK: - IBAction
      @IBAction func ...
     
    

Swift 文档注释规范

单行注释

光标放在需要注释的变量上按下组合键 ⌥ ⌘ / 即可自动生成

/// 为变量进行单行注释
let str = "hanleylee.com"

多行注释

一般注释

光标放在需要注释的变量上按下组合键 ⌥ ⌘ / 即可自动生成

/// 对菜单进行排序, swift 中默认是常量, 因为需要使用变量要使用 inout 关键字
/// - Parameters:
///   - foo: 传入的 FoodMO 类型数组
///   - property: 按照哪种属性进行排序

/// 可添加详细说明(必须隔一行)
func sortFoodsArray(from foo: inout [FoodMO], by property: String) {
    if property == "foodName" {
        foo.sort { (food1, food2) -> Bool in
            food1.foodName! < food2.foodName!
        }
    } else if property == "selectedTime" {
        foo.sort { (food1, food2) -> Bool in
            food1.selectedTime! > food2.selectedTime!
        }
    }
}

使用 Markdown 格式进行多行注释

总的来说, markdown 格式的文档注释结构如下

  1. 第一部分(必要!)
    • Summary
  2. 第二部分(必要! 系统自动生成)
    • Declaration
  3. 第三部分(不必要)
    • Disscussion(默认不需使用关键字声明的)
    • Remark
    • Precondition
    • Requires
    • Todo
    • Warning
    • Version
    • Author
    • Note
    • Important
    • # Customize Title(与上面的关键字低一级别, 需放在最后声明)
  4. 第四部分(不必要)
    • Parameter
      • Parameter 1:
      • Parameter 2:
    • Return

示例如下:

/**
 这里是 Summary, 与下方空一行

 现在是 discussion 正文, 下面是 Discussion 部分关键字部分
 - Remark: There's a counterpart function .
    text 内容
    分割线↓
    ---
    分割线↑
 - Precondition: `fullname` should not be nil.
 - Requires: Both first and last name should be parts of the full name.
 - Todo: Support middle name in the next version.
 - Warning: A wonderful **crash** will be the result of a `nil` argument.
 - Version: 1.1
 - Author: Myself Only
 - Note: Too much documentation for such a small function.
 - Important: important
 # Customize Title(与上面的关键字同一级别)

 - Parameter fullname: The fullname that will be broken into its parts.
 - Returns: A *tuple* with the first and last name.
 */

func breakFullName(fullname: String) -> (firstname: String, lastname: String) {
    let fullnameInPieces = fullname.componentsSeparatedByString(" ")
    return (fullnameInPieces[0], fullnameInPieces[1])
}

使用 Jazzy 导出文档注释

Jazzy 让你可以通过命令行将工程中的所有文档注释导出为一个可离线浏览的网页.

安装

[sudo] gem install jazzy

使用

  1. cd 至工程所在目录
  2. 使用命令jazzy导出文档注释, 默认只会导出使用 public 及以上级别修饰的方法和属性的文档注释. 如果需要导出全部则使用 jazzy --min-acl internal

常用命令

  • jazzy: 导出 public 及以上权限级别的文档注释
  • jazzy --min-acl internal: 导出最小权限级别为 internal 的文档注释
  • jazzy -help: 查看 jazzy 所有可用命令

Swift 文档批注规范

非渲染型文档批注

多行批注

/*

*/

单行批注

//

渲染型文档批注(Playground 中使用)

多行可渲染文档批注

playground 中可以进行多行批注(并不是文档注释), 使用方法与 markdown 文档书写方式相同, 具体如下

/*:
# Summary

## Example

- Example:

    第一行

    第二行

也可用代码格式实现多行 Example 第一行 第二行

Official Link

https://leetcode-cn.com/problems/reverse-integer

Other

  • Experiment:
  • callout(Checkpoint):
  • Note:
  • Important:

单行可渲染文档批注

//: # 一级标题
//: 一般文本
//: - Note:
//: - Example:
//: callout(Checkpoint)
//: - Important:

参考

本博客文章采用 CC 4.0 协议,转载需注明出处和作者。

鼓励作者