团队Swift编码规范分享

目录

  • 命名
  • 格式
  • 准则
  • 文件
  • 场景
  • 参考

命名

【强制】命名清晰,保持一致性

反例:displayName(返回name还是展示name)

【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式

说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。

正例:alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文

反例:DaZhePromotion [打折]、getPingfenByName() [评分] / int 某变量 = 3

【强制】类、结构体、枚举、协议名使用大驼峰风格,常用缩写除外

正例:UserManager、UMSocialAdapter、TCPManager

【强制】函数、方法、变量、常量、参数使用小驼峰命名

【强制】协议名统一规范

作用为delegate,结尾添加Delegate;描述协议做的事,用名词描述;描述行为,用形容词,例如”able”或者”ing”等;如果两者不能满足,结尾添加Protocol。

正例:ScrollToTopable、UIDataSourceTranslating

【强制】缩略词使用完整大写,如果作为命名的开始部分,且首字母需要小写,则缩略词全小写

正例:

1
2
3
4
5
let userID = "123456"
let imageURL = "http://xxxxx"
class URLHandler {
func urlConvert(urlString: String) {}
}

【强制】枚举项小写

正例:

1
2
3
4
5
6
enum CompassPoint {
case north
case south
case east
case west
}

【强制】命名先保证表达的意思准确,再考虑简短,不为了缩短书写而缩短书写

说明:比如有些属性适合定义成类属性;有些准确的需要定义成实例属性。

正例:UIDevice.current.isIphoneX

反例:UIDevice.isIphoneX

【强制】常量、变量命名若不能明显表明类型,则属性命名内要包括类型信息

正例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 变量名中可以表明类型,则命名中不需要包括类型信息
let animationDuration: NSTimeInterval
let userName: String
// Controller命名
// UIViewController、UITableViewController等简写成Controller
let popupController: UIViewController
// UITabBarController和UINavigationController保留结构缩写
let flashSaleTabBarController: UITabBarController
// 命名中需要明确类型信息
let userImage: UIImage
let userImageURL: NSURL
let userImageURLString: String
// 当使用outlets时, 确保命名中标注类型
@IBOutlet weak var submitButton: UIButton!
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var nameLabel: UILabel!

【推荐】如果使用到了设计模式,建议在类名中体现出具体模式

说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。

正例:class CommunityFollowTopicCellFactory、class TopicListCellBaseFactory

【强制】如果枚举类型的属性,其命名不能表明是枚举类型的,带上Enum后缀

正例:UserIdentityEnum、sectionType、playStatus、selectionStyle

【强制】声明属性时言简意赅,不带上类名

正例:

1
2
3
4
5
6
// 系统 UIDevice.current
// 而不是UIDevice.currentDevice
UserDefaults.standard // 而不是UserDefaults.standardDefaults
class RouteManager {
static let shared = RouteManager() // 用shared,不用sharedManager
}

【强制】区分使用default或shared

说明:default一般用于提供使用默认的参数配置的实例;shared一般用于单例。

【强制】如果省略外部参数名后会导致调用处含义模糊,则禁止省略

反例:

1
2
3
// 方法声明
class func action(_ pageName: String?, pageParam: String?, action: String, actionParam: [String: Any]?, isOutPoint: Bool = false) {}
// 调用处 UserTracker.action("搜索", pageParam: searchType.rawValue, action: "语音输入", actionParam: ["keywords": result], isOutPoint: true)

【强制】函数命名中尽量不添加介词,如of、in、on、with等

正例:

1
2
3
// 原声明:UIFont.systemFontOfSize
open class func systemFont(ofSize fontSize: CGFloat) -> UIFont // 原声明:UIView.animateWithDuration
open class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)

格式

【强制】如果语句的逻辑或长度较复杂,则使用变量保存再引用

反例:

1
2
3
AnyDataSourceService(dataSource: GoodsCommentResultDataSource(id: goodsId, offset: offset, imageOnly: imageOnly)).run(success: {
[weak self] (data: GoodsCommentResult) in
})

正例:

1
2
3
4
let dataSource = GoodsCommentResultDataSource(id: goodsId, offset: offset, imageOnly: imageOnly)
let service = AnyDataSourceService(dataSource: dataSource)
service.run(success: { [weak self] (data: GoodsCommentResult) in
})

【强制】如果大括号内为空,则简洁地写成{}即可,不需要换行

【强制】枚举每一个case操作都换行,不跟在“:”后面

1
2
3
4
5
6
7
8
switch enum {
case a:
methodA()
case b:
methodB()
case c:
methodC()
}

【推荐】如果switch内每一个case的操作大于5行,则封装成一个方法调用

【强制】变量类型,函数参数,遵循协议或继承父类,分号前不留空格

正例:

1
2
3
4
let str: String = "Test"
someFunction(someArgument: "Argument")
class ViewController: UIViewController {}
extension ViewController: UITableViewDelegate {}

【强制】逗号后面、运算符前后加空格

正例:

1
2
3
let array = [1, 2, 3, 4, 5]
let sum = 1 + 2
let isSuccess = sum == 3

【强制】左大括号不换行,左边保留空格

【强制】流程控制不使用小括号

正例:if x == y { }

【强制】使用枚举时用简写

正例:imageView.setImageWithURL(url, type: .person)

【强制】使用一些语句如 else,catch等紧随代码块的关键词的时候,确保代码块和关键词在同一行

正例:

1
2
3
4
5
6
7
8
9
10
do {
try canThrowAnError() // no error was thrown
} catch {
// an error was thrown
}
if name == "world" {
print("hello, world")
} else {
print("I'm sorry \(name), but I don't recognize you")
}

【强制】switch与case对齐

正例:

1
2
3
4
5
6
7
8
switch some value to consider {
case value 1:
respond to value 1
case value 2, value 3:
respond to value 2 or 3
default:
otherwise, do something else
}

【强制】不注释无用代码,直接删掉。若想保留代码以防以后用到,请使用git

【强制】文件末尾必须留且只留一行空白行

【强制】“//”注释符号后面要保留空格

【强制】求高度/字符串等较复杂时需按一下格式定义,清晰指明含义,方便他人维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let factor1Top = 20
var factor1Height = 40
var factor2Height = 40
let bottomPadding = 30
if lineCount > 0 {
let lineHeight = lineCount * 10
factor1Height += lineHeight
}
if lineCount > 2 {
let lineHeight = lineCount * 20
factor2Height += lineHeight
}
let height = factor1Top + factor1Height + factor2Height + bottomPadding
return CGSize(width: width, height: height)

准则

【强制】若变量类型可以依靠推断得出,则声明时不要指明类型

正例:

1
let π = 3.14159

【强制】模型中需要指明数据类型

正例:

1
2
3
4
5
6
7
8
struct DiamondPackage {
var id: Int = 0
var count: Int = 0
var price: Double = 0.0
var desc: String?
var descIconURL: NSURL?
var iapProductId: String?
}

【强制】使用隐式拆包可选类型的场景只能是@IBOutlets和网络层Service(保证使用之前肯定有值非空时),其余情况禁止使用“!”隐式拆包

【强制】若需要判断当前值是否为nil,直接和nil比较

正例:if someOptional != nil {}

反例:if let _ = someOptional {}

【强制】使用属性时不用self.修饰

【强制】使用guard代替if提前返回

【强制】使用guard拆包多个可选值

正例:

1
2
3
guard let thingOne = thingOne, let thingTwo = thingTwo, let thingThree = thingThree else {     
return
}

【强制】严格设置访问权限open/public/internal/fileprivate/private

【强制】不使用的库不import进文件

说明:一般新建文件之后都会有默认代码:import Foundation,不需要则删除

文件

【强制】Controller文件结构

说明:Controller包含较多代码,需要适量划分,使得代码查找更方便

一般包含以下内容:

1
2
3
4
5
6
7
import
@IBOutlet
@IBAction
override
配置数据源(configSection和enum Row
私有属性 开放属性 私有函数 开放函数
delegate/protocol

正例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// import放最前面,先import系统库
import UIKit
import Alamofire
protocol ViewControllerDelegate: class {}
class ViewController: UIViewController {
// 仅包含@IBOutlet、@IBAction、私有属性、公有属性、override方法
// 顺序依次如下
// IBOutlet
@IBOutlet var imageView: UIImageView!
// 公有属性
var showBottom: Bool = false
// 私有属性
private weak var delegate: ViewControllerDelegate?
private var rows: [Row] = []
override func viewDidLoad() {
super.viewDidLoad()
configVM()
doSomething()
// Do any additional setup after loading the view, typically from a nib.
}
// override按生命周期顺序
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
// 每个extension内第一行空行
// @IBAction
extension ViewController {

@IBAction func clickButton() {}
}
// 类方法、开放的方法
extension ViewController {
class func classMethod() {}
func openMethod() {}
}
// 私有的方法
private extension ViewController {
func doSomething() {}
}
// 协议,每个协议分开写

extension ViewController: UITableViewDelegate {}
extension ViewController: ScrollToTop {}
// 数据源配置相关放最后的extension
private extension ViewController {
enum Row {
case row1
case row2
case row3
}
func configVM() {
rows = []
rows.append(.row1)
}
}

场景

  • 定义模型

【强制】唯一标识统一用id,不使用类似jobId写法

【强制】不参与运算的String不需要初始化,如name,desc等仅用于显示的字段

【强制】数组必须初始化,不使用optional写法

  • 使用CocoaPods

【强制】导入库需要指定某一个确定的版本号,禁止使用大于等于之类的指定

【强制】修改第三方库(禁止修改,除非特殊情况)需要新建一个Pod,并且在提交podfile修改的commit中注释原因

【强制】不轻易引入第三方库,除了网络库、JSON转模型库、路由库等

【强制】保持相同作用的库仅有一个

【推荐】团队开发的库尽量引入framework,不引入源码

参考

swift-style-guide

最详尽的 Swift 代码规范指南

17条 Swift开发规范 最佳实践

The Swift Programming Language (Swift 4)

-END-
欢迎到我的博客交流:https://zackzheng.info