iOS代码规范-swift

 

 

 

 

1、命名 

#协议

(1)协议描述的是 “作的事情”,命名为名词
protocol TableViewSectionProvider {
    func rowHeight(at row: Int) -> CGFloat
    var numberOfRows: Int { get }
    /* ... */
}

 

(2)协议描述的是 “能力”,需添加后缀able或 ing 。  (如  Equatable、 ProgressReporting)
protocol Loggable {
    func logCurrentState()
    /* ... */
}

protocol Equatable {
    
    func ==(lhs: Self, rhs: Self) -> bool {
        /* ... */
    }
}

  

 

(3)若是已经定义类,须要给类定义相关协议,则添加Protocol后缀
protocol InputTextViewProtocol {
    func sendTrackingEvent()
    func inputText() -> String
    /* ... */
}

  

 

#实现protocol: 

若是肯定protocol的实现不会被重写,建议用extension将protocol实现分离

 推荐:html

class MyViewController: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDataSource

extension MyViewController: UITableViewDataSource {
  // table view data source methods
}

// MARK: - UIScrollViewDelegate

extension MyViewController: UIScrollViewDelegate {
  // scroll view delegate methods
}

  

 

不推荐:git

class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
  // all methods
}

  

 

二、Bool类型命名:用is最为前缀

var isString: Bool = true

  

  

三、枚举定义尽可能简写,不要包括类型前缀

public enum UITableViewRowAnimation : Int {
    case fade

    case right // slide in from right (or out to right)

    case left

    case top

    case bottom

    case none // available in iOS 3.0

    case middle // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy

    case automatic // available in iOS 5.0.  chooses an appropriate animation style for you
}

  

 

  四、swift建议不要使用前缀

推荐
HomeViewController
Bundle

不推荐
NEHomeViewController
NSBundle

  

 

五、减小没必要要的简写

推荐
let viewFrame = view.frame
let textField = ...
let table = ...
let controller = ...
let button = ...
let label =...

不推荐
let r = view.frame
let tf = ...
let tb = ...
let vc =...
let btn = ...
let lbl =...

  

 

 

六、变量命名应该能推断出该变量类型,若是不能推断,则须要以变量类型结尾

推荐
class TestClass: class {
    // UIKit的子类,后缀最好加上类型信息
    let coverImageView: UIImageView

    @IBOutlet weak var usernameTextField: UITextField!

    // 做为属性名的firstName,明显是字符串类型,因此不用在命名里不用包含String
    let firstName: String

    // UIViewContrller以ViewController结尾
    let fromViewController: UIViewController
}

不推荐
class TestClass: class {
    // image不是UIImageView类型
    let coverImage: UIImageView

    // or cover不能代表其是UIImageView类型
    var cover: UIImageView

    // String后缀多余
    let firstNameString: String

    // UIViewContrller不要缩写
    let fromVC: UIViewController
}

  

 

七、省略全部的冗余的参数标签

func min(_ number1: int, _ number2: int) {
    /* ... */
}

min(1, 2)

  

 

八、进行安全值类型转换的构造方法能够省略参数标签,非安全类型转换则须要添加参数标签以表示类型转换方法

extension UInt32 {
  /// 安全值类型转换,16位转32位,可省略参数标签
  init(_ value: Int16)
  
  /// 非安全类型转换,64位转32位,不可省略参数标签
  /// 截断显示
  init(truncating source: UInt64)
  
  /// 非安全类型转换,64位转32位,不可省略参数标签
  /// 显示最接近的近似值
  init(saturating valueToApproximate: UInt64)
}

  

 

九、当第一个参数构成整个语句的介词时(如,at, by, for, in, to, with 等),为第一个参数添加介词参数标签

推荐
// 添加介词标签havingLength
func removeBoxes(havingLength length: int) {
    /* ... */
}

x.removeBoxes(havingLength: 12)

  

 

 #例外状况是,当后面全部参数构成独立短语时,则介词提早:

推荐
// 介词To提早
a.moveTo(x: b, y: c)

// 介词From提早
a.fadeFrom(red: b, green: c, blue: d)


不推荐
a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)

  

  

 

十、当第一个参数构成整个语句一部分时,省略第一个参数标签,不然须要添加第一个参数标签。其他状况下,给除第一个参数外的参数都添加标签

// 参数构成语句一部分,省略第一个参数标签
x.addSubview(y)

// 参数不构成语句一部分,不省略第一个参数标签
view.dismiss(animated: false)

  

  

#不要使用冗余的单词,特别是与参数及参数标签重复

推荐
func remove(_ member: Element) -> Element?


不推荐
func removeElement(_ member: Element) -> Element?

  

  

#命名出现缩写词,缩写词要么所有大写,要么所有小写,以首字母大小写为准

推荐
let urlRouterString = "https://xxxxx"
let htmlString = "xxxx"
class HTMLModel {
    /* ... */
}
struct URLRouter {
    /* ... */
}


不推荐
let uRLRouterString = "https://xxxxx"
let hTMLString = "xxxx"
class HtmlModel {
    /* ... */
}
struct UrlRouter {
    /* ... */
}

  

  

# 参数名要准确的表达出参数类型:

// 推荐
class ConnectionTableViewCell: UITableViewCell {
    let personImageView: UIImageView
    let animationDuration: NSTimeInterval
    // 做为属性名的firstName,很明显是字符串类型,因此不用在命名里不用包含String
    let firstName: String
    // 虽然不推荐, 这里用 Controller 代替 ViewController 也能够。
    let popupController: UIViewController
    let popupViewController: UIViewController
    // 若是须要使用UIViewController的子类,如TableViewController, CollectionViewController, SplitViewController, 等,须要在命名里标名类型。
    let popupTableViewController: UITableViewController
    // 当使用outlets时, 确保命名中标注类型。
    @IBOutlet weak var submitButton: UIButton!
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var nameLabel: UILabel!
}



// 不推荐
class ConnectionTableViewCell: UITableViewCell {
    // 这个不是 UIImage, 不该该以Image 为结尾命名。
    // 建议使用 personImageView
    let personImage: UIImageView
    // 这个不是String,应该命名为 textLabel
    let text: UILabel
    // animation 不能清晰表达出时间间隔
    // 建议使用 animationDuration 或 animationTimeInterval
    let animation: NSTimeInterval
    // transition 不能清晰表达出是String
    // 建议使用 transitionText 或 transitionString
    let transition: String
    // 这个是ViewController,不是View
    let popupView: UIViewController
    // 因为不建议使用缩写,这里建议使用 ViewController替换 VC
    let popupVC: UIViewController
    // 技术上讲这个变量是 UIViewController, 但应该表达出这个变量是TableViewController
    let popupViewController: UITableViewController
    // 为了保持一致性,建议把类型放到变量的结尾,而不是开始,如submitButton
    @IBOutlet weak var btnSubmit: UIButton!
    @IBOutlet weak var buttonSubmit: UIButton!
    // 在使用outlets 时,变量名内应包含类型名。
    // 这里建议使用 firstNameLabel
    @IBOutlet weak var firstName: UILabel!
}

  

 

十一、数组和字典变量定义,定义时须要标明泛型类型,并使用更简洁的语法

推荐
var names: [String] = []
var lookup: [String: Int] = [:]


不推荐
var names = [String]()
var names: Array<String> = [String]() / 不够简洁

var lookup = [String: Int]()
var lookup: Dictionary<String, Int> = [String: Int]() // 不够简洁
 

  

 

十二、常量定义,建议尽量定义在Type类型里面,避免污染全局命名空间

推荐
class TestTabelViewCell: UITableViewCell {
    static let kCellHeight = 80.0
    
    /* ... */
}
// uses
let cellHeight = TestTabelViewCell.kCellHeight



不推荐
let kCellHeight = 80.0
class TestTabelViewCell: UITableViewCell {
    /* ... */
}
// uses
let cellHeight = kCellHeight

  

  

1四、当方法最后一个参数是Closure类型,调用时建议使用尾随闭包语法

推荐
UIView.animate(withDuration: 1.0) {
  self.myView.alpha = 0
}


不推荐
UIView.animate(withDuration: 1.0, animations: {
  self.myView.alpha = 0
})

 

  

  

  

1五、 通常状况下,在逗号后面加一个空格

推荐
let testArray = [1, 2, 3, 4, 5]

不推荐
let testArray = [1,2,3,4,5]

  

 

 

 2、注释

#尽量使用Xcode注释快捷键(⌘⌥/) [command + option + /]

推荐
/// <#Description#>
///
/// - Parameter testString: <#testString description#>
/// - Returns: <#return value description#>
func testFunction(testString: String?) -> String? {
    /* ... */
}


不推荐
// Comment
func testFunction(testString: String?) -> String? {
    /* ... */
}

  

 

#使用// MARK: -,按功能和协议/代理分组

/// MARK顺序没有强制要求,但System API & Public API通常分别放在第一块和第二块。

// MARK: - Public

// MARK: - Request

// MARK: - Action

// MARK: - Private

// MARK: - xxxDelegate
 

  

 

#swift 注释是支持 mark down 语法的

/**
 ## 功能列表
 这个类提供下一下很赞的功能,以下:
 - 功能 1
 - 功能 2
 - 功能 3
 ## 例子
 这是一个代码块使用四个空格做为缩进的例子。
     let myAwesomeThing = MyAwesomeClass()
     myAwesomeThing.makeMoney()
 ## 警告
 使用的时候总注意如下几点
 1. 第一点
 2. 第二点
 3. 第三点
 */
class MyAwesomeClass {
    /* ... */
}

  

 

 

十二、对外接口不兼容时,使用@available(iOS x.0, *)标明接口适配起始系统版本号

@available(iOS x.0, *)
class myClass {
    
}

@available(iOS x.0, *)
func myFunction() {
    
}

  

 

  

 

 

3、关于闭包

在Closures中使用self时避免循环引用

推荐github

resource.request().onComplete { [weak self] response in
    guard let strongSelf = self else { 
        return 
    }
    let model = strongSelf.updateModel(response)
    strongSelf.updateUI(model)
}

  

 

  

不推荐swift

// 不推荐使用unowned
// might crash if self is released before response returns
resource.request().onComplete { [unowned self] response in
    let model = self.updateModel(response)
    self.updateUI(model)
}

  

 

不推荐api

// deallocate could happen between updating the model and updating UI
resource.request().onComplete { [weak self] response in
    let model = self?.updateModel(response)
    self?.updateUI(model)
}

  

 

 

Golden Path,最短路径原则  

推荐数组

func login(with username: String?, password: String?) throws -> LoginError {
  guard let username = contextusername else { 
    throw .noUsername 
  }
  guard let password = password else { 
    throw .noPassword
  }

  /* login code */
}

  

  

不推荐安全

func login(with username: String?, password: String?) throws -> LoginError {
  if let username = username {
    if let password = inputDatapassword {
        /* login code */
    } else {
        throw .noPassword
    }
  } else {
      throw .noUsername
  }
}

  

 

 

单例

class TestManager {
    static let shared = TestManager()

    /* ... */
}

  

 

4、缩进和换行

1  使用四个空格进行缩进。闭包

2 每行最多160个字符,这样能够避免一行过长。 (Xcode->Preferences->Text Editing->Page guide at column: 设置成160便可)app

3 确保每一个文件结尾都有空白行。ide

4 确保每行都不以空白字符做为结尾 (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines).

5 左大括号不用另起一行。

遵照Xcode内置的缩进格式( 若是已经遵照,按下CTRL-i 组合键文件格式没有变化)。当声明的一个函数须要跨多行时,推荐使用Xcode默认的格式

// Xcode针对跨多行函数声明缩进
func myFunctionWithManyParameters(parameterOne: String,
                                  parameterTwo: String,
                                  parameterThree: String) {
    // Xcode会自动缩进
    print("\(parameterOne) \(parameterTwo) \(parameterThree)")
}

// Xcode针对多行 if 语句的缩进
if myFirstVariable > (mySecondVariable + myThirdVariable)
    && myFourthVariable == .SomeEnumValue {
    // Xcode会自动缩进
    print("Hello, World!")
}

  

 

当调用的函数有多个参数时,每个参数另起一行,并比函数名多一个缩进。
someFunctionWithManyArguments(
    firstArgument: "Hello, I am a string",
    secondArgument: resultFromSomeFunction()
    thirdArgument: someOtherLocalVariable)

  

 

当遇到须要处理的数组或字典内容较多须要多行显示时,需把 [ 和 ] 相似于方法体里的括号, 方法体里的闭包也要作相似处理。
someFunctionWithABunchOfArguments(
    someStringArgument: "hello I am a string",
    someArrayArgument: [
        "dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
        "string one is crazy - what is it thinking?"
    ],
    someDictionaryArgument: [
        "dictionary key 1": "some value 1, but also some more text here",
        "dictionary key 2": "some value 2"
    ],
    someClosure: { parameter1 in
        print(parameter1)
    })

  

 

应尽可能避免出现多行断言,可以使用本地变量或其余策略。

// 推荐
let firstCondition = x == firstReallyReallyLongPredicateFunction()
let secondCondition = y == secondReallyReallyLongPredicateFunction()
let thirdCondition = z == thirdReallyReallyLongPredicateFunction()
if firstCondition && secondCondition && thirdCondition {
    // 你要干什么
}


// 不推荐
if x == firstReallyReallyLongPredicateFunction()
    && y == secondReallyReallyLongPredicateFunction()
    && z == thirdReallyReallyLongPredicateFunction() {
    // 你要干什么
}

  

 

 当在写一个变量类型,一个字典里的主键,一个函数的参数,听从一个协议,或一个父类,不用在分号前添加空格。
// 指定类型
let pirateViewController: PirateViewController
// 字典语法(注意这里是向左对齐而不是分号对齐)
let ninjaDictionary: [String: AnyObject] = [
    "fightLikeDairyFarmer": false,
    "disgusting": true
]
// 声明函数
func myFunction<t, u: someprotocol where t.relatedtype == u>(firstArgument: U, secondArgument: T) {
    /* ... */
}
// 调用函数
someFunction(someArgument: "Kitten")
// 父类
class PirateViewController: UIViewController {
    /* ... */
}
// 协议
extension PirateViewController: UITableViewDataSource {
    /* ... */
}

  

 

 

 

5、操做符


二元运算符(+, ==, 或->)的先后都须要添加空格,左小括号后面和右小括号前面不须要空格。

let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
    fatalError("The universe is broken.")
}
func pancake() -> Pancake {
    /* ... */
}

  

 

 

 

 

 

其余语法规范

1 多使用let,少使用var

2 少用!去强制解包

3 可选类型拆包取值时,使用if let判断

4 不要使用 as! 或 try!

5 数组访问尽量使用 .first 或 .last, 推荐使用 for item in items 而不是 for i in 0..

6 若是变量可以推断出类型,则不建议声明变量时指明类型

7 若是变量可以推断出类型,则不建议声明变量时指明类型

8 switch case选项不须要使用break关键词

9 访问控制

10 对于私有访问,若是在文件内不能被修改,则标记为private;若是在文件内可修改,则标记为fileprivate

11 对于公有访问,若是不但愿在外面继承或者override,则标记为public,不然标明为open

12 访问控制权限关键字应该写在最前面,除了@IBOutletIBAction@discardableResultstatic 等关键字在最前面

13 如调用者能够不使用方法的返回值,则须要使用@discardableResult标明

14 使用==!=判断内容上是否一致

15 使用===!==判断class类型对象是否同一个引用,而不是用 ==!=

16 Runtime兼容

17 Swift语言自己对Runtime并不支持,须要在属性或者方法前添加dynamic修饰符才能获取动态型,继承自NSObject的类其继承的父类的方法也具备动态型,子类的属性和方法也须要加dynamic才能获取动态性

 

 

 Objective-C兼容

1 Swift接口不对Objective-C兼容,在编译器或者Coding中就会出现错误

2 暴漏给Objective-C的任何接口,须要添加@objc关键字,若是定义的类继承自NSObject则不须要添加

3 若是方法参数或者返回值为空,则须要标明为可选类型

 

 

 

[参考文献]:

Swift Programming Language

Swift API Design Guidelines

Raywenderlich Swift Style Guide

Linkedin Swift Style Guide

相关文章
相关标签/搜索