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

相關文章
相關標籤/搜索