Introduction
之前有一個 Objective-C style guide. ,這一篇是針對swift的一個補充。
Credits
API Design Guidelines 是蘋果專門針對API的一個規范,本規范涉及到一些相關的內容,大都是保持和蘋果的規范一致。
它絕大部分內容,來自 The Official raywenderlich.com Swift Style Guide. ,并加入了自己的一些偏好。
同時,Swift語言在快速的發展中,這個規范也會隨著Swift的發展、以及我們對Swift更多的使用和了解,持續地進行修改和完善。
Table of Contents
Naming
classes, structures, enumerations 和 protocols采用首字母 大 寫的駝峰命名法,method names and variables采用首字母 小 寫的駝峰命名法。
Preferred:
private let maximumWidgetCount = 100 class WidgetContainer { var widgetButton: UIButton let widgetHeightPercentage = 0.85 }
Not Preferred:
let MAX_WIDGET_COUNT = 100 class app_widgetContainer { var wBut: UIButton let wHeightPct = 0.85 }
縮寫應該避免,但如URL、ID這種常見的縮寫可以使用。
API Design Guidelines 中提到,如果使用這些縮寫,字母應該全為大寫或者小寫。Examples:
Preferred
let urlString: URLString let userID: UserID
Not Preferred
let uRLString: UrlString let userId: UserId
使用argument label,替代注釋,使代碼self-documenting,可讀性更強。
func convertPointAt(column: Int, row: Int) -gt; CGPoint func timedAction(afterDelay delay: NSTimeInterval, perform action: SKAction) -gt; SKAction! // would be called like this: convertPointAt(column: 42, row: 13) timedAction(afterDelay: 1.0, perform: someOtherAction)
Protocols
按照蘋果的API Design Guidelines,Protocols名字可以使用名詞來描述這個Protocols的內容,比如 Collection
, WidgetFactory
。
或以-ing、-able結尾來描述Protocols實現的一些功能,比如: Equatable
, Resizing
。
Enumerations
按照蘋果的API Design Guidelines,枚舉值使用小寫開頭的駝峰命名法,也就是lowerCamelCase。
enum Shape { case rectangle case square case rightTriangle case equilateralTriangle }
Class Prefixes
在Swift里面,每一個module都是一個namesapce。而在ObjC里沒有namespace的概念,只是在每個類名前面添加前綴,比如NSArray。
當不同的module有同名的類名時,需要指明module name。
import SomeModule let myClass = MyModule.UsefulClass()
Generics
范型的類型名,應該是描述性的名詞、upperCamelCase。如果不能起一個有意義的關系或角色名稱,可以使用 T
、 U
或 V
。
Preferred:
struct Stacklt;Elementgt; { ... } func writeTolt;Target: OutputStreamgt;(inout target: Target) func maxlt;T: Comparablegt;(x: T, _ y: T) -gt; T
Not Preferred:
struct Stacklt;Tgt; { ... } func writeTolt;target: OutputStreamgt;(inout t: target) func maxlt;Thing: Comparablegt;(x: Thing, _ y: Thing) -gt; Thing
Language
使用美國英語。
Preferred:
let color = quot;redquot;
Not Preferred:
let colour = quot;redquot;
Code Organization
多使用extensions來組織代碼。每個 extensions使用 // MARK: -
來分隔開。
Protocol Conformance
當一個類遵守一個協議時,使用extensions來組織代碼。
Preferred:
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 }
Not Preferred:
class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate { // all methods }
Unused Code
能刪除的代碼,都刪掉。
Not Preferred:
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func numberOfSectionsInTableView(tableView: UITableView) -gt; Int { // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -gt; Int { // #warning Incomplete implementation, return the number of rows return Database.contacts.count }
Preferred:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -gt; Int { return Database.contacts.count }
Minimal Imports
盡可能減少依賴和imports。
Spacing
- 縮進使用2個空格:
-
方法體的花括號需要在新的一行開啟,在新的一行關閉
。而其它花括號(
if
/else
/switch
/while
etc.),加入一個空格后在行尾開啟,在新一行關閉(Xcode默認)。 - 提示:⌘A選中代碼后使用Control-I (或者菜單Editor\Structure\Re-Indent)來調整縮進.
Preferred:
if user.isHappy { // Do something } else { // Do something else }
Not Preferred:
if user.isHappy { // Do something } else { // Do something else }
- methods之間只留一個空行。methods內部,使用空行來分隔不同功能的代碼,為不同的section。一個method內部的section不宜太多,否則應該考慮分拆。
-
冒號左邊沒有空格,右邊有一個空格。
Exception:
? :
和[:]
。
Preferred:
class TestDatabase: Database { var data: [String: CGFloat] = [quot;Aquot;: 1.2, quot;Bquot;: 3.2] }
Not Preferred:
class TestDatabase : Database { var data :[String:CGFloat] = [quot;Aquot; : 1.2, quot;Bquot;:3.2] }
Comments
盡可能避免大量使用注釋,好的代碼應該盡可能是self-documenting。
如果需要注釋,它只用來解釋 為什么 這段代碼要這么寫,而不是解釋代碼的邏輯。
并且是正確的,代碼變化時也需要馬上更新,不能有誤導。
Exception: 上面兩條不適用于生成文檔用的注釋.
Classes and Structures
Which one to use?
Structs是 value semantics 。
而Classes是 reference semantics .
有些時候,只需要Structs就夠了。但有些Class,由于歷史原因,被弄成類,如 NSDate
、 NSSet
等。
Example definition
下面是一個比較規范的Class定義:
class Circle: Shape { var x: Int, y: Int var radius: Double var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } init(x: Int, y: Int, radius: Double) { self.x = x self.y = y self.radius = radius } convenience init(x: Int, y: Int, diameter: Double) { self.init(x: x, y: y, radius: diameter / 2) } func describe() -gt; String { return quot;I am a circle at \(centerString()) with an area of \(computeArea())quot; } override func computeArea() -gt; Double { return M_PI * radius * radius } private func centerString() -gt; String { return quot;(\(x),\(y))quot; } }
上面的例子,給我們演示了這些規范:
- 冒號在用于指明類型時,左邊沒有空格,右邊有一個空格。
- 當一組變量、常量有關聯時,定義在一行里。
-
函數修飾符
internal
是缺省值,可以省略。重載一個函數時,訪問修飾符也可以省略掉。
Use of Self
避免使用self來訪問屬性。除非需要區分函數參數和屬性。
class BoardLocation { let row: Int, column: Int init(row: Int, column: Int) { self.row = row self.column = column let closure = { print(self.row) } } }
Computed Properties
For conciseness, if a computed property is read-only, omit the get clause. The get clause is required only when a set clause is provided.
Computed property一般是只讀,同時省略get clause。get clause只是當set clause存在時才需要寫。
Preferred:
var diameter: Double { return radius * 2 }
Not Preferred:
var diameter: Double { get { return radius * 2 } }
Final
當一個類不想被繼承時,使用 final
關鍵字。Example:
// Turn any generic type into a reference type using this Box class. final class Boxlt;Tgt; { let value: T init(_ value: T) { self.value = http://www.tuicool.com/articles/value } }
Closure Expressions
方法的參數列表最后一參數類型為閉包時,可以使用尾閉包。但只在只存在一個閉包參數時才使用尾閉包。
Preferred:
UIView.animateWithDuration(1.0) { self.myView.alpha = 0 } UIView.animateWithDuration(1.0, animations: { self.myView.alpha = 0 }, completion: { finished in self.myView.removeFromSuperview() } )
Not Preferred:
UIView.animateWithDuration(1.0, animations: { self.myView.alpha = 0 }) UIView.animateWithDuration(1.0, animations: { self.myView.alpha = 0 }) { f in self.myView.removeFromSuperview() }
For single-expression closures where the context is clear, use implicit returns:
只有一個表達式的、用來返回值的閉包,可以省略return。
attendeeList.sort { a, b in a gt; b }
Types
盡可能使用Swift原生類型,而不是使用ObjC的NS類型。
Preferred:
let width = 120.0 // Double let widthString = (width as NSNumber).stringValue // String
Not Preferred:
let width: NSNumber = 120.0 // NSNumber let widthString: NSString = width.stringValue // NSString
但是有些情況例外,比如在寫Sprite Kit代碼時,用 CGFloat
可以減少轉換。
Constants
盡可能使用let,只有在需要使用變量的時候使用var。
Tip:有一個辦法能達到上面的目的,就是自己只寫let,讓編譯器幫你確定哪些需要改成var。
You can define constants on a type rather than an instance of that type using type properties. To declare a type property as a constant simply use static let
. Type properties declared in this way are generally preferred over global constants because they are easier to distinguish from instance properties. Example:
Preferred:
enum Math { static let e = 2.718281828459045235360287 static let pi = 3.141592653589793238462643 } radius * Math.pi * 2 // circumference
Note:The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.
Not Preferred:
let e = 2.718281828459045235360287 // pollutes global namespace let pi = 3.141592653589793238462643 radius * pi * 2 // is pi instance data or a global constant?
Optionals
當訪問一個optional value前,需要訪問多個optional value時,使用optional chaining:
self.textContainer?.textLabel?.setNeedsDisplay()
訪問一個optional value后,需要執行多個操作,可以使用optional binding:
if let textContainer = self.textContainer { // do many things with textContainer }
給optional變量命名時,不要使用類似 optionalString
或 maybeView
這樣的方式,因為optional-ness已經在類型聲明中體現。
相對應的,使用unwrapped value時,避免使用unwrappedView 或
actualLabel`,使用optional變量名就可以了。
Preferred:
var subview: UIView? var volume: Double? // later on... if let subview = subview, volume = volume { // do something with unwrapped subview and volume }
Not Preferred:
var optionalSubview: UIView? var volume: Double? if let unwrappedSubview = optionalSubview { if let realVolume = volume { // do something with unwrappedSubview and realVolume } }
Struct Initializers
使用Swfit原生的struct initializers,而不是遺留的CGGeometry constructors。
Preferred:
let bounds = CGRect(x: 40, y: 20, width: 120, height: 80) let centerPoint = CGPoint(x: 96, y: 42)
Not Preferred:
let bounds = CGRectMake(40, 20, 120, 80) let centerPoint = CGPointMake(96, 42)
同樣的,使用struct-scope constants CGRect.infinite
, CGRect.null
, etc, 而不是global constants CGRectInfinite
, CGRectNull
, etc。
Type Inference
對于適用于類型推斷的地方,使用類型推斷。
Preferred:
let message = quot;Click the buttonquot; let currentBounds = computeViewBounds() var names = [quot;Micquot;, quot;Samquot;, quot;Christinequot;] let maximumWidth: CGFloat = 106.5
Not Preferred:
let message: String = quot;Click the buttonquot; let currentBounds: CGRect = computeViewBounds() let names = [String]()
Syntactic Sugar
盡可能使用語法糖。
Preferred:
var deviceModels: [String] var employees: [Int: String] var faxNumber: Int?
Not Preferred:
var deviceModels: Arraylt;Stringgt; var employees: Dictionarylt;Int, Stringgt; var faxNumber: Optionallt;Intgt;
Functions vs Methods
不依附于任何class或type的函數被稱為free function,應該盡量避免使用,因為不太好找到這個方法。
Preferred
let sorted = items.mergeSort() // easily discoverable rocket.launch() // clearly acts on the model
Not Preferred
let sorted = mergeSort(items) // hard to discover launch(rocket)
Free Function Exceptions
let tuples = zip(a, b) // feels natural as a free function (symmetry) let value = http://www.tuicool.com/articles/max(x,y,z) // another free function that feels natural
Memory Management
用下面prefered的方式,避免循環引用。
Preferred
resource.request().onComplete { [weak self] response in guard let strongSelf = self else { return } let model = strongSelf.updateModel(response) strongSelf.updateUI(model) }
Not Preferred
// might crash if self is released before response returns resource.request().onComplete { [unowned self] response in let model = self.updateModel(response) self.updateUI(model) }
Not Preferred
// 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) }
Access Control
訪問修飾符應該放在靠前的位置,前面只能有 static
、 @IBAction
和 @IBOutlet
。
Preferred:
class TimeMachine { private dynamic lazy var fluxCapacitor = FluxCapacitor() }
Not Preferred:
class TimeMachine { lazy dynamic private var fluxCapacitor = FluxCapacitor() }
Golden Path
嵌套的 if
會讓代碼的縮進層次不齊(整齊的縮進被稱作Golden Path),會讓代碼可讀性變差,使用 guard
來做函數輸入合法性檢查,可以減少if嵌套。
Preferred:
func computeFFT(context: Context?, inputData: InputData?) throws -gt; Frequencies { guard let context = context else { throw FFTError.noContext } guard let inputData = http://www.tuicool.com/articles/inputData else { throw FFTError.noInputData } // use context and input to compute the frequencies return frequencies }
Not Preferred:
func computeFFT(context: Context?, inputData: InputData?) throws -gt; Frequencies { if let context = context { if let inputData = http://www.tuicool.com/articles/inputData { // use context and input to compute the frequencies return frequencies } else { throw FFTError.noInputData } } else { throw FFTError.noContext } }
Preferred:
guard let number1 = number1, number2 = number2, number3 = number3 else { fatalError(quot;impossiblequot;) } // do something with numbers
Not Preferred:
if let number1 = number1 { if let number2 = number2 { if let number3 = number3 { // do something with numbers } else { fatalError(quot;impossiblequot;) } } else { fatalError(quot;impossiblequot;) } } else { fatalError(quot;impossiblequot;) }
Failing Guards
Guard檢查失敗執行的語句應該退出當前方法,并且應該只有一條語句,如 return
, throw
, break
, continue
, and fatalError()
。如果需要多行語句,考慮使用 defer
。
Parentheses
保住條件語句的圓括號應該省略。
Preferred:
if name == quot;Helloquot; { print(quot;Worldquot;) }
Not Preferred:
if (name == quot;Helloquot;) { print(quot;Worldquot;) }
Other Swift Style Guides
上面的規范可能你不太喜歡,或者沒有涉及到你需要的某些方面,可以參考下面的內容:
Tags: Swift
文章來源:http://www.jianshu.com/p/32e62ac53377