Swift開發代碼規范

Swift開發規范

此文檔與Apple官方Swift代碼規范文檔不沖突,只是在官方文檔的基礎上增加了的部分規范。

  • 命名規范
  • 編碼風格
  • 語法規范
  • 框架使用
  • 網絡請求

命名規范

  • 類名
    采用駝峰命名法,首字母大寫, 示例:HomeViewController
  • 結構體名
    采用駝峰命名法,首字母大寫,示例:Coupon
  • 變量名
    采用駝峰命名法,首字母小寫,示例:pageNumber
  • 枚舉名
    采用駝峰命名法,首字母大寫,示例:OrderStatus
  • 枚舉值名
    采用駝峰命名法,首字母小寫。一般情況下一個單詞就可以。示例:
  enum OrderStatus: Int {
      case normal
      case expired
      case paid
  }
  • 全局常量名
    采用駝峰命名法,首字母大寫,并用小寫的項目縮寫名為前綴。如“微信”項目名的小寫縮寫為“wx”, 示例:
let weAnimationDuration: TimeInterval = 0.25
  • 全局函數名
    采用駝峰命名法,首字母小寫,并用小寫的項目縮寫名+下劃線為前綴。如“微信”項目名的小寫縮寫為“wx”, 前綴即“wx_”,示例:
    func we_checkLogin() -> Bool {
      ...
    }
  • 協議名
  1. 如果是單純的協議,則采用駝峰命名法,首字母大寫,后綴為"protocol"。示例:SomeProtocol
  2. 如果是用來做代理的,則采用駝峰命名法,首字母大寫,后綴為"delegate"。示例:OrderCellDelegate
  • 協議方法名
    采用駝峰命名法,首字母小寫。在創建一個代理方法時,第一個未命名的參數應該是代理源。(UIKit中有很多這樣的例子),示例:
  // 推薦:
  func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
  func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

  // 不推薦
  func didSelectName(namePicker: NamePickerViewController, name: String)
  func namePickerShouldReload() -> Bool
  • 可選協議方法名
    使用@objc+optional關鍵字,示例:
  @objc protocol OrderCellDelegate: class {
      // 除了自身對象之外,還有操作的控件作為參數
      func orderCell(cell: weOrderCell, didClick checkButton: UIButton)
      // 只有自身對象作為參數
      @objc optional func orderCellDidClickCheckButton(cell: weOrderCell)
    }
  • 代理變量名
    統一使用"delegate",并用weak修飾(如果需要除class之外的類型實現,則可不用)。示例:
    weak var delegate: OrderCellDelegate?
  • 控制器類名
    要求同類名,但要以“ViewController”結尾,示例:OrderViewController
  • 視圖類名
    要求同類名,但要以“View”結尾,示例:OrderDetailView
  • 模型類名
    要求同類名,無需特殊后綴,示例:Order
  • 控件名
    需要在名稱中指明控件的類型,不要使用lblbtn等縮寫,示例:nameLabel, confirmButton
  • 資源文件名
  1. 圖片資源需要在名稱中加上功能模塊名,防止重復,示例:img_home_right_arrowimg_order_locate
  2. 聲音資源名稱表明用途即可,示例:qr_sound
  • 閉包名
    要求同類名,但要以"closure"結尾,示例:OrderViewClosure
  • 擴展文件名
    文件名后加上項目縮寫,示例:UILabel+Extension(we).swift
  • 擴展方法名
    采用駝峰命名法,首字母小寫,并用小寫的項目縮寫名+下劃線為前綴。如“微信”項目名的小寫縮寫為“wx”, 前綴即“wx_”,示例:
  extension UIView {
      /// 移除所有子控件
      func we_removeAllSubviews() {
          ...
      }
  }
  • 首字母縮略詞在命名中一般來說都是全部大寫,例外的情形是如果首字母縮略詞是一個命名的開始部分,而
    這個命名需要小寫字母作為開頭,這種情形下首字母縮略詞全部小寫。示例:
  // "HTML" 是變量名的開頭, 需要全部小寫 "html"
  let htmlBodyContent: String = "<p>Hello, World!</p>"
  // 推薦使用 ID 而不是 Id
  let profileID: Int = 1
  // 推薦使用 URLFinder 而不是 UrlFinder
  class URLFinder {
      ...
  }
  • 命名應該具有描述性和清晰的。
// 推薦
class RoundAnimatingButton: UIButton { /* ... */ }
// 不推薦
class CustomButton: UIButton { /* ... */ }
  • 不要縮寫,簡寫命名,或用單個字母命名。
  // 推薦
  class RoundAnimatingButton: UIButton {
    let animationDuration: NSTimeInterval
    func startAnimating() {
      let firstSubview = subviews.first
    }
  }

  // 不推薦
  class RoundAnimating: UIButton {
    let aniDur: NSTimeInterval
    func srtAnmating() {
      let v = subviews.first
    }
  }

編碼格式

  • 盡可能的多使用let,少使用var
  • 如果變量類型可以依靠推斷得出,不建議聲明變量時指明類型。示例:
    var name = "jack"
  • 二元運算符(+, ==, 或->)的前后都需要添加空格,左小括號后面和右小括號前面不需要空格。
  let myValue = 20 + (30 / 2) * 3

  if 1 + 1 == 3 {
    fatalError("The universe is broken.")
  }

  func pancake() -> Pancake {
    ...
  }
  • 逗號后面要加一個空格,示例:
    var nums = [1, 2, 3, 4]
  • 左大括號不用另起一行,并與之前的元素相隔一個空格。
  class SomeClass {
    func someMethod() {
      if x == y {
        ...
      } else {
        ...
      }
    }
  }
  • 注釋的雙斜杠跟注釋內容之間隔一個空格。示例:// 我是注釋
  • 盡量不使用self.,除非方法參數名與屬性同名。
  • 使用 // MARk: -按功能為一個文件中的代碼分塊, 下面一行保留為空行。示例:
  class Pirate {

    // MARK: - 實例屬性

    private let pirateName: String

    // MARK: - 初始化

    init() {
        /* ... */
    }
  }
  • 使用擴展來實現協議方法。示例:
  extension ViewController: OrderCellDelegate {

      func orderCell(cell: OrderCell, didClick checkButton: UIButton) {
          ...
      }
  }
  • 使用@available(iOS 10.0, *)來標明起始系統版本號
  • 為同一對象的各屬性賦值時,等號‘=’對齊。示例:
  let my = MyClass()
  my.name     = "張四"
  my.age      = 10
  my.address  = "望京綠地中心"
  • 盡可能避免使用強制轉換和強制解包。
  • 使用4個空格進行縮進。
  • 每行最多160個字符,避免一行過長。(Xcode->Preferences->Text Editing->Page guide at column: 設置成160即可)
  • 在使用一些語句如elsecatch等緊隨代碼塊的關鍵詞的時候,確保代碼塊和關鍵詞在同一行。下面if/elsedo/catch的例子。示例:
  if someBoolean {
    // something you want
  } else {
    // something you don't want
  }

  do {
    let fileContents = try readFile("filename.txt")
  } catch {
    print(error)
  }
  • 推薦把訪問修飾符放到第一個位置。
  // 推薦
  private static let kMyPrivateNumber: Int
  // 不推薦
  static private let kMyPrivateNumber: Int
  • case 語句 應和 switch 語句左對齊,并在 標準的 default 上面。
  switch problem {
  case .attitude:
    print("At least I don't have a hair problem.")
  case .hair:
    print("Your barber didn't know when to stop.")
  case .hunger(let hungerLevel):
    print("The hunger level is \(hungerLevel).")
  }
  • 當在寫一個變量類型,一個字典里的主鍵,一個函數的參數,遵從一個協議,或一個父類,不用在分號前添加空格。
  // 指定類型
  let pirateViewController: PirateViewController
  // 字典語法(注意這里是向左對齊而不是分號對齊)
  let ninjaDictionary: [String: AnyObject] = [
    "fightLikeDairyFarmer": false,
    "disgusting": true
  ]
  // 調用函數
  someFunction(someArgument: "Kitten")
  // 父類
  class PirateViewController: UIViewController {
    ...
  }
  // 協議
  extension PirateViewController: UITableViewDataSource {
    ...
  }

語法規范

  • 使用顯式類型和空集合。類型在賦值操作符的左邊,空實例在賦值操作符的右邊。

錯誤示例:

  var x = [String: Int]()
  var y = [Double]()
  var z = Set<String>()
  var mySet = MyOptionSet()

正確示例:

  var x: [String: Int] = [:]
  var y: [Double] = []
  var z: Set<String> = []
  var mySet: MyOptionSet = []
  • 可選類型拆包時,使用if letguard let判斷。
  • 多個可選類型拆包取值時,將多個if letguard let判斷合并。示例:
  if let name = person.name, let age = person.age {
  }
  • 盡量不要使用as!try!,使用if let as?判斷。示例:
  if let name = person.name as? String {
  }
  • 非全局常量要定義在類的里面,不要定義在類的外面。示例:
  class ViewController: UIViewController {

    let cellID = "GirlCell"
    ...
  }
  • 跨多行函數聲明縮進時,參數名左對齊(不是冒號對齊)。示例:
  func myFunctionWithManyParameters(parameterOne: String,
                                    parameterTwo: String,
                                    parameterThree: String) {

      print("\(parameterOne) \(parameterTwo) \(parameterThree)"
  }
  • 多if語句時的縮進,看示例:
  if myFirstVariable > (mySecondVariable + myThirdVariable)
    && myFourthVariable == .SomeEnumValue {
      // Xcode會自動縮進
      print("Hello, World!")
  }
  • 基本上不要通過下標直接訪問數組內容,如果可能使用如 .first.last, 因為這些方法是非強制類型并不會崩潰。(如果需要通過下標訪問數組內容,在使用前要做邊界檢查)
  • 推薦盡可能使用 for item in items 而不是 for i in 0..<items.count 遍歷數組。
  • 不要使用 +=+ 操作符給數組添加新元素,使用性能較好的.append().appendContentsOf()
  • 如果需要聲明數組基于其他的數組并保持不可變類型, 使用 let myNewArray = [arr1, arr2].flatten(),而不是let myNewArray = arr1 + arr2
  • 總體上,我們推薦使用提前返回的策略,而不是if語句的嵌套。使用guard語句可以改善代碼的可讀性。示例:
  // 推薦
  func eatDoughnut(atIndex index: Int) {
      guard index >= 0 && index < doughnuts else {
        // 如果 index 超出允許范圍,提前返回。
        return
      }
      let doughnut = doughnuts[index]
      eat(doughnut)
  }

  // 不推薦
  func eatDoughnuts(atIndex index: Int) {
      if index >= 0 && index < donuts.count {
          let doughnut = doughnuts[index]
          eat(doughnut)
      }
  }
  • 在解析可選類型時,推薦使用guard語句,而不是if語句,因為guard語句可以減少不必要的嵌套縮進。示例:
  // 推薦
  guard let monkeyIsland = monkeyIsland else {
      return
  }
  bookVacation(onIsland: monkeyIsland)
  bragAboutVacation(onIsland: monkeyIsland)

  // 不推薦
  if let monkeyIsland = monkeyIsland {
      bookVacation(onIsland: monkeyIsland)
      bragAboutVacation(onIsland: monkeyIsland)
  }

  // 禁止
  if monkeyIsland == nil {
      return
  }
  bookVacation(onIsland: monkeyIsland!)
  bragAboutVacation(onIsland: monkeyIsland!)
  • 如果你不確定if語句 和guard語句哪一個可讀性更強,建議使用guard
  // if 語句更有可讀性
  if operationFailed {
    return
  }
  // guard 語句這里有更好的可讀性
  guard isSuccessful else {
    return
  }
  // 雙重否定不易被理解 - 不要這么做
  guard !operationFailed else {
    return
  }
  • 如果需要在2個狀態間做出選擇,建議使用if語句,而不是使用guard語句。
  // 推薦
  if isFriendly {
      print("你好, 遠路來的朋友!")
  } else {
      print("窮小子, 哪兒來的?")
  }

  // 不推薦
  guard isFriendly else {
      print("窮小子, 哪兒來的?")
      return
  }
  print("你好, 遠路來的朋友!")
  • 使用類型推斷上下文,使用編譯器推斷上下文來編寫簡潔的代碼。
  // 推薦
  let selector = #selector(viewDidLoad)
  view.backgroundColor = .red
  let toView = context.view(forKey: .to)
  let view = UIView(frame: .zero)

  // 不推薦
  let selector = #selector(ViewController.viewDidLoad)
  view.backgroundColor = UIColor.red
  let toView = context.view(forKey: UITransitionContextViewKey.to)
  let view = UIView(frame: CGRect.zero)

框架使用

  • 項目中所有控件(UIButton、UILabel等)必須使用DJExtension中封裝的初始化方法。
// 帶布局
let checkAllLabel = UILabel(text: "查看全部", font: dj_semiboldFont(14), color: .white, alignment: .right, superView: originalView) { (make) in
    make.center.equalTo(thirdImageView)
    make.width.equalTo(56)
    make.height.equalTo(20)
}
// 不帶布局
let checkAllLabel = UILabel(text: "查看全部", font: dj_semiboldFont(14), color: .white, alignment: .right)
  • 項目中所有view或控制器相關操作,必須使用DJExtension中封裝的擴展方法。
// UIView
view.dj_addBottomLine()             // 在底部添加分割線
view.dj_addShadow()                 // 添加陰影
view.dj_removeAllSubviews()         // 移除所有子控件
view.dj_getParentViewController()   // 獲取父控制器
...
// 控制器
vc.dj_push(TestViewController())
vc.dj_present(TestViewController())
vc.dj_pop()
vc.dj_showActionSheet()
...
  • 項目中所有常用的設置值、獲取某個值或判斷,使用DJExtension中封裝的公共函數。如果沒有,可以完善DJExtension庫。
Functions Comment
dj_hexColor("00ff00") get a color with a hex value.
dj_pingSemiboldFont(15) get a font from the font family "PingFangSC-Semibold".
dj_isCameraAllowed() the camera authorization is allowed or not.
dj_navigationBarHeight() get the navigation bar height(adapted iPhone X or later).
dj_isEmpty(obj) an object is empty or not.
dj_isIPhoneX() the phone is iPhone X,Xs,Xs Max or not.
dj_toJson(obj) convert an object to json string.
dj_callPhone("13288889990") call a number
dj_postNotification("LoginSuccessNotification") post a notification.
more...

網絡請求

  • 項目中的網絡請求數據架構采用Alamofire + Moya + SwiftyJSON + ObjectMapper。
  • 項目中處理網絡請求的類,統一命名為: 'xxService',比如‘HomeService’。
  • 用來解析數據的模型優先使用Struct,Struct不能滿足要求時,使用Class

后記

  • 此規范主要是自己工作時的總結,可以提高代碼可讀性、可維護性,并可提高開發效率。如果有建議或發現有問題的地方,歡迎批評指正。

Have fun.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一. 格式規范 1.1 使用4個空格進行縮進 推薦 1.2 二元運算符(+, ==, 或->)的前后都需要添加空格...
    與偉大LEE同行閱讀 3,592評論 2 22
  • 持續更新,歡迎大家評論建議結合現在項目的原因,統一的規范在一個項目上是必須的,有利于后來人代碼閱讀,也有利于自身的...
    JasonL閱讀 235評論 0 1
  • Android編碼規范 源文件基礎 文件名 源文件以其最頂層的類名來命名,大小寫敏感,文件擴展名為.java。 文...
    呼呼哥閱讀 976評論 0 0
  • web開發約束 項目結構: vue項目結構 vue命名約束 語義化和命名 應以功能或內容命名,不以表現形式命名;命...
    儂姝沁兒閱讀 4,150評論 0 7
  • 些許的傷感也是如初見的醉,情不在意歲月的痕跡,只容納能夠相處的點滴,遺忘在不經意間的角落留下,偶爾也會路過,或者不...
    云郎閱讀 155評論 0 0