开发规范的目的是保证统一项目成员的编码风格,并使代码美观,希望该份规范能给大家起到借鉴作用。
1. 项目简介
采用 MVC 框架开发,开发语言为 Swift。
2. 项目结构
2.1 工程目录
- SupportingFiles(包含 AppDelegate、Assets、Info.plist等工程入口及配置文件)
- Utils(工具类,Extension<扩展可以按类放在不同文件夹内>、各类 Manager 工具等)
- Classes(类文件主目录,包含 App 内所有功能模块)
- Components (自定义控件)
- Base(基类,包含一些定制化的内容,例如页面样式)
- ThirdPart(第三方的类库
和对第三方封装,比如第三方登录、支付、IM等
) - LocalResource (图片、音频、视频等资源文件)
- CoreData(数据库相关的表)
<strong>注:项目全部采用实体文件夹,不要在项目中新建 New Group 虚拟文件夹</strong>
2.2 MVC 业务逻辑
类 | 说明 |
---|---|
controller | 1.处理页面展示逻辑,提示框封装在基类 BaseViewController 中 2.页面跳转必须在 controller 中进行,不要直接放在 view 中 3.当 UITableView 或者 UICollectionView 的 dataSource 包含较多代码时,可以为该 controller 新建一个 dataSource 类 |
viewModel | 1.逻辑计算(如订单支付页面, 应收金额计算的计算逻辑) 2.网络请求,JSON数据 → 数据模型(SwiftyJSON 解析) |
handler | 将 contoller 的部分业务逻辑抽离到该类中,例如 AlertController 弹出、权限授权检测等等,目的是为了保证 controller 代码整洁, 避免过度臃肿,不利于后期维护 |
view | 对页面相关联的 view 进行封装,不要在 controller 中直接添加单个控件 |
model | 数据模型,项目中数据传递建议使用解析后的 model, 不要直接传递 Json,不利于代码阅读及理解 |
3. 代码结构
controller 中的代码使用
// MARK: -- <#注释#> --
标记进行分段,便于代码快速查找
// MARK: -- Life Cycle --
// MARK: -- Delegate --
// MARK: -- Private Methods --
// MARK: -- Event Resopnse --
// MARK: -- Other --
4. 命名规则
- 类名、Protocol、结构体和枚举加上前缀 —— “Hex”
- 全局常量命名使用 k 前缀 + UpperCamelCase 命名
4.1 类、结构体
controller、Struct、enum 采用大驼峰(UpperCamelCase)命名方式, 目录结构可以采用图示
4.2 成员变量、属性
- 采用驼峰式命名(lowerCamelCase),并使用完整的单词
- 不建议使用 tempArr、array1、dic1 之类的命名
正确示例:userName、birthdayImg、subtitleLabel
注:1.只读属性可以添加修饰 private(set) 2.不对外公开的属性, 可以添加修饰 private 例如: class Person { private(set) var name: String}
4.3 方法名
- 以小写字母开头,每一个后续的单词首字母大写,使用完整的单词
- 方法名不要随意命名, 命名要精简且语义清晰(可读性高),不能为了精简牺牲可读性
4.4 图标文件命名规则
图标命名规则采用单词小写加下划线的方式命名,前缀统一为icon_,对应大小的图标名称后加上@1x、@2x、@3x后缀,表明该图标对应几倍图
资源类型 | 前缀 | 示例 |
---|---|---|
图标 | icon_ | icon_star@2x |
功能引导 | icon_guide | icon_guide_first@2x |
标签图标 | icon_tabbar | icon_tabbar_homepage@2x |
分享类 | icon_share | icon_share_qq@2x |
按钮不同状态命名规则
资源类型 | 后缀 | 示例 |
---|---|---|
正常 | _normal | icon_searchBtn_normal@2x |
高亮 | _highlighted | icon_searchBtn_highlighted@2x |
不可用 | _disabled | icon_searchBtn_disabled@2x |
选中 | _selected | icon_searchBtn_selected@2x |
4.5 控件命名规则
控件类型 | 后缀(控件全称) | 示例 |
---|---|---|
UITextView | TextView | contentTextView |
UITextField | TextField | titleTextField |
UILabel | Label | subtitleLabel |
UIButton | Button | searchButton |
UIImageView | ImageView | sexImageView |
UISlider | Slider | progressSlider |
UISwitch | Swith | msgPushSwitch |
5.代码格式化
5.1 空格
方法参数与类型注释、多个参数之间、返回值类型之间需要添加空格
// Bad
func functionName(paraA:Int,paraB:String)-><#return type#> {
<#function body#>
}
// OK
func functionName(paraA: Int, paraB: String) -> <#return type#> {
<#function body#>
}
变量名与类型注释之间、赋值号左右需要添加空格
// Bad
var value:Type=defaultValue
// OK
var value: Type = defaultValue
类继承,遵循接协议等,冒号前面不加空格, 但后面跟空格,如遵循多个协议, 以英文逗号隔开, 逗号前面不加空格, 但后面要加
class Student: Person, Sendable, Codable {
func doSomething() {}
}
关系运算符(如 >=、!=)和逻辑运算符(如 &&、||)两边要有空格
// OK
(someValue > 100) true : false
// OK
(items) ?: []
5.2 花括号
func functionName(value: Int) {
↑空格,此处花括号不用换行
if value == 10 {
空格↑ ↑空格,花括号不要另起一行
} else if value >= 100 {
空格↑ ↑空格,花括号不要另起一行
} else {
花括号不要另起一行
}
}
5.3 换行符
当方法参数为三个及三个以上时,参数可以另起一行,
private func updateLogTaskStatus(_ logTask: UpLoadLogTask,
_ status: HexUpLoadLogTaskStaus,
_ objectName: String,
context: NSManagedObjectContext) {
}
定义数组或字典中包含多个元素时, 每个元素可以另起一行
let para = ["partnerId": HexTokenModel.partnerID,
"partnerName": HexTokenModel.partnerName,
"storeId": HexTokenModel.storeId,
"storeName": HexTokenModel.storeName,
"posId": HexTokenModel.posId,
"uploadDate": Date.getYMDDateStr(timeIntervalSince1970: Date().timeIntervalSince1970),
"ossObjectName": objcName,
"logType": logType]
6.代码注释
清晰的代码注释更有益于代码阅读、后期代码维护、bug修复。
- 定义的属性、方法等,采用快捷键 Command + Option + / 进行标注
例如:
// MARK: -- 渠道信息 --
struct HexOrderProductChannelModel: HandyJSON {
/// 来源 区分是POS系统产生或被推送进入
var source: String?
/// 设备类型 POS系统内部的设备类型,比如PAD或者KIOSK
var deviceType: String?
/// 订单类型 区分堂食、外带和外送
var orderType: HexOrderProductChannelOrderType?
/// 配送方式 区分是预约单还是实时单
var deliveryType: String?
/// 渠道名 第三方平台名称,比如小程序,美团和饿了么
var tpName: String?
/// 编码 渠道编码,后台配置的业务编码
var code: String?
}
/// 获取当前营业日前几个营业日的日期, 例如当前营业日是 2022-09-06
/// - Parameter days: 例如获取近3个营业日, days = -3, 负数表示当前日期往前算, 正数表示从当前日期往后数
/// - Returns: 例如: [2022-09-05, 2022-09-04, 2022-09-03]
private mutating func businessDates(since days: Int) -> [String] {
var dates: [String] = []
guard let currentBusinessDate = self.dateFormatter.date(from: HexTokenModel.busDate) else { return dates }
let isEarlier = days < 0
for i in 1...abs(days) {
if let date = currentBusinessDate.dateByAddingDays(isEarlier (i * -1) : i) {
dates.append(self.dateFormatter.string(from: date))
}
}
return dates
}
- <strong>若某部分功能逻辑处理较复杂,关键部分代码请写出相应实现逻辑,便于其他同事更快速理解业务流程</strong>
7. 其他
- 对于工程内引用的第三方库,建议进行二次封装,便于后期做迁移
- 函数参数最多不得超过 8 个;寄存器数目问题,超过 8 个会影响效率
- 图形化的字面量,#colorLiteral(...), #imageLiteral(...)禁止在项目工程中使用
- 避免强制解包以及强制类型映射,尽量使用if let 或 guard let进行解包,禁止try!形式处理异常,避免使用隐式解包
- 使用 guard 来提前结束条件,避免形成判断嵌套;
- 类似注解的修饰词单独占一行,如@objcMembers,@propertyWrapper, @discardableResult 等
- 在闭包中使用 self 时使用捕获列表[weak self]避免循环引用,闭包开始判断 self 的有效性
- 使用委托和协议时,避免循环引用,定义属性的时候使用 weak 修饰
- 表示单例的静态属性,一般命名为 shared 或者 default
- 尽量消除 warning