swift代码统一编码规范
编码规范
let footer: ControlEvent
}
struct Output {
let state: Observable
}
func transform(input: Input) -> Output {
let state = input.header.flatMap{
//coding
}
return .init(state: state)
}
}
class YPHomeListVC: YPBaseVMController{
override func bindViewModel() {
let out = viewModel.transform(input: .init(header: tableView.rx.header, footer: tableView.rx.footer))
out.state.bind(to: tableView.rx.endRefresh).disposed(by: viewModel.rx.disposeBag)
}
}
如上所示,YPHomeListVC不需要在其它地方访问刷新的状态。VM不需要在其它地方监听下拉和上拉时间,这种情况就通过input和output进行交互
(value: .newest)
//不推荐
/// 排序类型
var sort = BehaviorRelay(value: .newest)
private(set) var sort = BehaviorRelay(value: .newest)
背景
随着团队扩大,人员增多。需要统一编码规范规范
命名-明确的使用含义
- 请使用驼峰命名规则
- 首字母大写
- 用统一的标识开头:
YP
- 控制器
VC
结尾
- 命名应该具有标识性
- 不能使用拼音
- 不能使用过于简单的缩写
- 视图命名
- 常规以View结尾:
UIContentView
- tableView的cell以TCell为后缀:
YPBaseTCell
- UICollectionView的cell以Cell为后缀:
YPBaseCCell
- vm
- 所有命名应该具有描述性
- 常规以View结尾:
- 属性应该是一个名词
- 局部变量
- 需要遵守命名规范
- 使用具有代表意义的名词
model
、item
、temp
、dataSource
//推荐
for i in dataSource {}
为了减少不必要的属性申明
for model in models{}
models.foreach{$0.name = ""}
models.foreach{ model in
model.name = "1"
model.id = "2"
}
//不推荐
for a in dataSource {}
for m in models{}
models.foreach{ model in
model.name = ""
}
models.foreach{ f in
f.name = ""
f.id = ""
}
- 常量
- 常量的名字需要大写首字母并保持驼峰:
KLastChoosedOccsInRecruitList
- 避免使用全局常量,转而使用结构体和类
- 常量的名字需要大写首字母并保持驼峰:
KLastChoosedOccsInRecruitList
= "KLastChoosedOccsInRecruitList
"
}
//不推荐
let KLastChoosedOccsInRecruitList
= "KLastChoosedOccsInRecruitList
"
- 枚举
- 以enum结尾
- Case的命名
- 小写字母开头
- 名词或者动词
- 驼峰规则
- 类型
- 根据变量、参数、关联类型的作用来命名,而不是基于它们的类型
- 协议
- 描述事物的协议,读起来应该像名词(例如,
Collection
) - 描述能力的协议,应该使用后缀
able
,ible
或ing
- 描述事物的协议,读起来应该像名词(例如,
- 协议方法,第一个未命名参数应该是委托数据源
方法
- 方法或者函数名最好能在调用处形成符合语法规范的英语短语
- 省略无用的单词。每个单词都需要传达出相应的关键信息
- 为了使用起来更流畅,可以从第二个或者第三个参数开始降低命名要求,前提是这些参数不影响整个 API 的语义
- 工厂方法用
make
开头:x.makeWidget(cogCount: 47)
- 构造器和工厂方法的第一个参数命名不应该考虑方法名,应该独立命名,如:
x.makeWidget(cogCount: 47)
- 没有副作用的方法和函数读起来应该像名词短语
x.distance(to: y)
,i.successor()
- 有副作用的方法和函数读起来应该像祈使动词
print(x)
,x.sort()
,x.append(y)
-
可变/不可变方法的命名要成对出现。一个可变方法通常都有一个不可变方法与之对应,二者的语义相近,区别在于前者更新实例,而后者返回一个新值
- 当一项操作恰好能够被一个动词描述时,使用动词原形为可变方法命名;使用动词的过去分词 (ed) 或现在分词 (ing) 为不可变方法命名
- 命名不可变方法,最好使用过去分词(通常是增加后缀 “ed”)
- 如果由于动词后面直接跟随一个对象,无法添加 “ed” 时,使用现在分词命名不可变方法,即后缀 “ing”
- 当一项操作恰好能够被一个名词描述时,使用名词本身为不可变方法命名;使用名词前加 “form” 的方式为可变方法命名。
-
对于返回值是布尔类型的方法和属性,读起来应该像是对被调用对象的断言,其使用场景是不可变方法。例如,
x.isEmpty
,line1.intersects(line2)
。
- 避免使用全局函数,转而使用方法和属性。以下情况例外
- 没有明显的self:
min(x, y, z)
- 没有明显的self:
- 函数是不受限的范型函数:
print(x)
- 在特定的领域中已经有约定俗成函数语法在:
sin(x)
- 对于没有参数的方法
注释
/// 类注释[会在代码提示中显示]:用户信息模型 class YPUserInfoModel{ /// 属性注释[会在代码提示中显示]:用户id var id: String? /// 属性注释[会在代码提示中显示]:用户昵称 var name: String? } //MARK: - 代码模块注释[在文件目录中显示] extension YPUserInfoModel{ /// 方法注释[会在代码提示中显示]:更新用户昵称 /// - Parameters: /// - userName: 用户昵称 /// - Returns: model func update(userName: String) -> Self{ //内部说明: 逻辑说明 name = userName } } class YPHomeListVC: UIViewController{ //MARK: 业务属性 /// 操作类型 let operation: Operation = .normal /// 页码 var page: Int = 0 ... //MARK: UI属性[懒加载] /// 头视图 lazy var headView: UIView = { let view = UIView() //coding return view }() /// 表格 lazy var tableView: UITableView = { let view = UITableView() //coding return view }() }- 所有的类必须添加类注释
- 所有属性必须加注释
- 方法必须加注释,方法中的参数和返回需要有注释
- 方法内部,复杂逻辑需要添加逻辑说明
- 方法中的参数需要添加明确的作用说明,如果有返回值也需要说明
- 复杂逻辑 注释在代码处
代码组织结构
目录结构
- Main
- 标签模块1
- Home
- View
- Cell
- Controller
- ViewModel
- View
- 功能模块1
- 。。。
- 功能模块2
- 。。。
- Home
- 标签模块2
- 标签模块3
- 。。。
- 标签模块1
- Model
- Resource
- 标签模块
- name.svg
- 标签模块
控制器中枚举和结构体的声明
访问域
- 明确属性、方法、类的访问域:
private
、fileprivate
、internal
、public
和open
- 同访问域的方法应该通过
extension
的代码块进行整合【不合理】
- 只开放get的权限
属性申明
- 对于不需要修改的内容使用
let
代码
空格
- 等号前后需有空格
- if的判断条件前后需有空格
换行
- 代码块
写法
- 应该使用 +=, -=, *=, /=
懒加载
- controller中的UI必须使用懒加载
- 懒加载的内部视图 统一使用view,不要与其本身相同
- 请添加合适且明确的访问域
- 在懒加载中不要直接
addSubView
addSubView
(view)
return view
}()
}
内存
Block
- 类型申明
weak 和 unowned
//推荐 let call: Call = {[weak self] in guard let weakSelf = self else {return} //coding } //不推荐 let call: Call = {[weak self] in guard let self = self else {return} //coding }podfile
- 接入的第三方库,必须直接指定版本
弹窗
- fYPProgressHUD
- YPLableAlertView
- YPStateAlertView
MVVM
input、output、transform
为了减少不必要的属性申明,在VC和VM的交互中。部分逻辑和 事件应该通过下面的方式进行交互 不支持在 Docs 外粘贴 block f- VC不需要引用目标,例如提交方法、登录方法
VM中的Rx
- 使用let
- 避免对controller的引用,需要使用controller的时候,请用回调和Rx的方式放在controller中
业务
网络库
errCode
在业务场景中,建议不要直接使用字符串,改用枚举类型 /// 推荐 if YPWhiteListEnum.paidIssue.rawValue == response.errCode {// "paid_issue" 付费发布提示 paidSendAlert(response: response) } /// 不推荐 if "integral_lack" == response.errCode {// 付费发布积分不足 integralLackAlert(response: response) }推荐
推荐使用isEmpty
//推荐 "sadasdsa".isEmpty [1,2].isEmpty //不推荐 "sadasdsa".count == 0 [1,2].count == 0禁止强制解包
//推荐 guard let value = values2 as? String else {return} //不推荐 let value = value2 as! String数组取值,需要判断数组是否下标越界
//推荐 let source: [String] = ["1"] if source.count > 1{ let value: String = source[1] } let value: String? = source.safe(idx: 1) //不推荐 let value: String = source[1]获取系统版本号,禁止强制直接转数值类型
let versionString = "14.2.1" //推荐 let versions:[Int] = versionString.components(separatedBy: '.').map{Int($0) ?? 0} //不推荐 let vaersion: CGFloat = CGFloat(versionString)不推荐使用public、fileprivate等修饰符 修饰cextension扩展
//推荐 extension YPHomeViewModel{ fileprivate func medthod(){} fileprivate func medthod(){} } //不推荐 fileprivate extension YPHomeViewModel{ func medthod(){} func medthod(){} }SnpKit
- 约束的代码尽量精简
- 适配刘海屏
- 路由