来自Linkedin的Swift编程风格指南 LinkedIn Swift Style Guide

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看273 回复回复3 收藏收藏1 分享淘帖 转播转播 分享分享1 微信
查看: 273|回复: 3
收起左侧

来自Linkedin的Swift编程风格指南 LinkedIn Swift Style Guide

[复制链接]
cocoaswift 发表于 2016-7-1 14:34:07 | 显示全部楼层 |阅读模式
快来登录
获取优质的苹果资讯内容
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
订阅梳理好了的知识点专辑

首先推荐阅读下:Apple's API Design Guidelines




0、目录 Table Of Contents

1. Code Formatting

2. Naming

3. Coding Style

  • 3.1 General
  • 3.2 Access Modifiers
  • 3.3 Custom Operators
  • 3.4 Switch Statements and enums
  • 3.5 Optionals
  • 3.6 Protocols
  • 3.7 Properties
  • 3.8 Closures
  • 3.9 Arrays
  • 3.10 Error Handling
  • 3.11 Using guard Statements


4. Documentation/Comments

  • 4.1 Documentation
  • 4.2 Other Commenting Guidelines



来自Linkedin的Swift编程风格指南 LinkedIn Swift Style Guide

来自Linkedin的Swift编程风格指南 LinkedIn Swift Style Guide - 敏捷大拇指 - 来自Linkedin的Swift编程风格指南 LinkedIn Swift Style Guide





1、Code Formatting 代码格式化

1.1 使用4个空格来代替Tabs

1.2 避免过长的行,可以在XCode中进行设置单行最大长度:(Xcode->Preferences->Text Editing->Page guide at column: 160 is helpful for this)

1.3 保证每个文件结尾都存在一个新行 Ensure that there is a newline at the end of every file.

1.4 避免无意义的尾随空格: (Xcode->Preferences->Text Editing->Automatically trim trailing whitespace + Including whitespace-only lines).

1.5 避免将单独的左花括号放置到一行,我们参考了:1TBS style

[Swift] 纯文本查看 复制代码
class SomeClass {
    func someMethod() {
        if x == y {
            /* ... */
        } else if x == z {
            /* ... */
        } else {
            /* ... */
        }
    }
    /* ... */
}


1.6 在写变量的类型声明、字典类型的键、函数参数、协议的声明或者父类的时候,不要在冒号前添加空格。

[Swift] 纯文本查看 复制代码
// specifying type
let pirateViewController: PirateViewController
// dictionary syntax (note that we left-align as opposed to aligning colons)
let ninjaDictionary: [String: AnyObject] = [
    "fightLikeDairyFarmer": false,
    "disgusting": true
]
// declaring a function
func myFunction<t, u: someprotocol where t.relatedtype == u>(firstArgument: U, secondArgument: T) {
    /* ... */
}
// calling a function
someFunction(someArgument: "Kitten")
// superclasses
class PirateViewController: UIViewController {
    /* ... */
}
// protocols
extension PirateViewController: UITableViewDataSource {
    /* ... */
}</t, u: someprotocol where t.relatedtype == u>


1.7 一般来说,逗号后面都要跟随一个空格。

[Swift] 纯文本查看 复制代码
let myArray = [1, 2, 3, 4, 5]


1.8 在二元操作符譬如+, ==, 或者 ->的前后需要加上空格,但是对于( 、`)的前后不需要加空格。

[Swift] 纯文本查看 复制代码
let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
    fatalError("The universe is broken.")
}
func pancake() -> Pancake {
    /* ... */
}


1.9 我们默认使用Xcode推荐的格式化风格(CTRL-I),在声明某个函数的时候会多行排布参数。

[Swift] 纯文本查看 复制代码
// Xcode indentation for a function declaration that spans multiple lines
func myFunctionWithManyParameters(parameterOne: String,
                                  parameterTwo: String,
                                  parameterThree: String) {
    // Xcode indents to here for this kind of statement
    print("\(parameterOne) \(parameterTwo) \(parameterThree)")
}
// Xcode indentation for a multi-line `if` statement
if myFirstVariable > (mySecondVariable + myThirdVariable)
    && myFourthVariable == .SomeEnumValue {
    // Xcode indents to here for this kind of statement
    print("Hello, World!")
}


1.10 在调用多参数函数的时候,会把多个参数放置到单独的行中:

[Swift] 纯文本查看 复制代码
someFunctionWithManyArguments(
    firstArgument: "Hello, I am a string",
    secondArgument: resultFromSomeFunction()
    thirdArgument: someOtherLocalVariable)


1.11 对于大型的数组或者字典类型,应该将其分割到多行内,[ 与 ]类比于花括号进行处理。对于闭包而言也应该同样适合于该规则。

[Swift] 纯文本查看 复制代码
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)
    })


1.12 尽可能地使用本地变量的方式来避免多行的判断语句。

[Swift] 纯文本查看 复制代码
// PREFERRED
let firstCondition = x == firstReallyReallyLongPredicateFunction()
let secondCondition = y == secondReallyReallyLongPredicateFunction()
let thirdCondition = z == thirdReallyReallyLongPredicateFunction()
if firstCondition && secondCondition && thirdCondition {
    // do something
}
// NOT PREFERRED
if x == firstReallyReallyLongPredicateFunction()
    && y == secondReallyReallyLongPredicateFunction()
    && z == thirdReallyReallyLongPredicateFunction() {
    // do something
}





2、Naming 命名

2.1 Swift中不需要再使用Objective-C那样的前缀,譬如使用 GuybrushThreepwood 而不是LIGuybrushThreepwood。

2.2 对于类型名即struct, enum, class, typedef, associatedtype等等使用 PascalCase 。

2.3 对于函数名、方法名、变量名、常量、参数名等使用camelCase。

2.4 在使用首字母缩写的时候尽可能地全部大写,并且注意保证全部代码中的统一。不过如果缩写被用于命名的起始,那么就全部小写。

[Swift] 纯文本查看 复制代码
// "HTML" is at the start of a variable name, so we use lowercase "html"
let htmlBodyContent: String = "<p>Hello, World!</p>"
// Prefer using ID to Id
let profileID: Int = 1
// Prefer URLFinder to UrlFinder
class URLFinder {
    /* ... */
}


2.5 对于静态常量使用 k 前缀 + PascalCase。

[Swift] 纯文本查看 复制代码
class MyClassName {
    // use `k` prefix for constant primitives
    static let kSomeConstantHeight: CGFloat = 80.0
    // use `k` prefix for non-primitives as well
    static let kDeleteButtonColor = UIColor.redColor()
    // don't use `k` prefix for singletons
    static let sharedInstance = MyClassName()
    /* ... */
}


2.6 对于泛型或者关联类型,使用PascalCase描述泛型,如果泛型名与其他重复,那么可以添加一个Type后缀名到泛型名上。

[Swift] 纯文本查看 复制代码
class SomeClass<t> { /* ... */ }
class SomeClass<model> { /* ... */ }
protocol Modelable {
    associatedtype Model
}
protocol Sequence {
    associatedtype IteratorType: Iterator
}</model></t>


2.7 命名必须要是不模糊的并且方便表述的。

[Swift] 纯文本查看 复制代码
// PREFERRED
class RoundAnimatingButton: UIButton { /* ... */ }
// NOT PREFERRED
class CustomButton: UIButton { /* ... */ }


2.8 不要使用缩写,可以选择较为简短的单词。

[Swift] 纯文本查看 复制代码
// PREFERRED
class RoundAnimatingButton: UIButton {
    let animationDuration: NSTimeInterval
    func startAnimating() {
        let firstSubview = subviews.first
    }
}
// NOT PREFERRED
class RoundAnimating: UIButton {
    let aniDur: NSTimeInterval
    func srtAnmating() {
        let v = subviews.first
    }
}


2.9 对于不是很明显的类型需要将类型信息包含在属性名中。

[Swift] 纯文本查看 复制代码
// PREFERRED
class ConnectionTableViewCell: UITableViewCell {
    let personImageView: UIImageView
    let animationDuration: NSTimeInterval
    // it is ok not to include string in the ivar name here because it's obvious
    // that it's a string from the property name
    let firstName: String
    // though not preferred, it is OK to use `Controller` instead of `ViewController`
    let popupController: UIViewController
    let popupViewController: UIViewController
    // when working with a subclass of `UIViewController` such as a table view
    // controller, collection view controller, split view controller, etc.,
    // fully indicate the type in the name.
    let popupTableViewController: UITableViewController
    // when working with outlets, make sure to specify the outlet type in the
    // variable name.
    @IBOutlet weak var submitButton: UIButton!
    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var nameLabel: UILabel!
}
// NOT PREFERRED
class ConnectionTableViewCell: UITableViewCell {
    // this isn't a `UIImage`, so shouldn't be called image
    // use personImageView instead
    let personImage: UIImageView
    // this isn't a `String`, so it should be `textLabel`
    let text: UILabel
    // `animation` is not clearly a time interval
    // use `animationDuration` or `animationTimeInterval` instead
    let animation: NSTimeInterval
    // this is not obviously a `String`
    // use `transitionText` or `transitionString` instead
    let transition: String
    // this is a view controller - not a view
    let popupView: UIViewController
    // as mentioned previously, we don't want to use abbreviations, so don't use
    // `VC` instead of `ViewController`
    let popupVC: UIViewController
    // even though this is still technically a `UIViewController`, this variable
    // should indicate that we are working with a *Table* View Controller
    let popupViewController: UITableViewController
    // for the sake of consistency, we should put the type name at the end of the
    // variable name and not at the start
    @IBOutlet weak var btnSubmit: UIButton!
    @IBOutlet weak var buttonSubmit: UIButton!
    // we should always have a type in the variable name when dealing with outlets
    // for example, here, we should have `firstNameLabel` instead
    @IBOutlet weak var firstName: UILabel!
}


2.10 在编写函数参数的时候,要保证每个参数都易于理解其功能。

2.11 根据 Apple's API Design Guidelines, 对于protocol,如果其描述的是正在做的事情,譬如Collection,那么应该命名为名词。而如果是用于描述某种能力,譬如Equatable, ProgressReporting,那么应该添加 able, ible, 或者 ing 这样的后缀。如果你的协议并不符合上述两种情形,那么应该直接添加一个Protocol后缀,譬如:

[Swift] 纯文本查看 复制代码
// here, the name is a noun that describes what the protocol does
protocol TableViewSectionProvider {
    func rowHeight(atRow row: Int) -> CGFloat
    var numberOfRows: Int { get }
    /* ... */
}
// here, the protocol is a capability, and we name it appropriately
protocol Loggable {
    func logCurrentState()
    /* ... */
}
// suppose we have an `InputTextView` class, but we also want a protocol
// to generalize some of the functionality - it might be appropriate to
// use the `Protocol` suffix here
protocol InputTextViewProtocol {
    func sendTrackingEvent()
    func inputText() -> String
    /* ... */
}





3、Coding Style 编程风格


3.1、General

3.1.1 尽可能地使用let来代替var。

3.1.2 尽可能地使用 map, filter, reduce的组合来进行集合的转换等操作,并且尽可能地避免使用带有副作用的闭包。

[Swift] 纯文本查看 复制代码
// PREFERRED
let stringOfInts = [1, 2, 3].flatMap { String($0) }
// ["1", "2", "3"]
// NOT PREFERRED
var stringOfInts: [String] = []
for integer in [1, 2, 3] {
    stringOfInts.append(String(integer))
}
// PREFERRED
let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }
// [4, 8, 16, 42]
// NOT PREFERRED
var evenNumbers: [Int] = []
for integer in [4, 8, 15, 16, 23, 42] {
    if integer % 2 == 0 {
        evenNumbers(integer)
    }
}


3.1.3 尽可能地显式声明不方便进行类型推测的变量或者常量的类型名。

3.1.4 如果你的函数需要返回多个参数,那么尽可能地使用Tuple来代替inout参数。如果你会多次使用某个元组,那么应该使用typealias设置别名。如果返回的参数超过三个,那么应该使用结构体或者类来替代。

[Swift] 纯文本查看 复制代码
func pirateName() -> (firstName: String, lastName: String) {
    return ("Guybrush", "Threepwood")
}
let name = pirateName()
let firstName = name.firstName
let lastName = name.lastName


3.1.5 在创建delegates/protocols的时候需要小心所谓的保留环(retain cycles),这些属性需要被声明为weak。

3.1.6 在闭包中直接调用self可能会导致保留环,可以使用capture list 在这种情况下:

[Swift] 纯文本查看 复制代码
myFunctionWithClosure() { [weak self] (error) -> Void in
    // you can do this
    self?.doSomething()
    // or you can do this
    guard let strongSelf = self else {
        return
    }
    strongSelf.doSomething()
}


3.1.7 不要使用 labeled breaks。

3.1.8 不要在控制流逻辑判断的时候加上圆括号。

[Swift] 纯文本查看 复制代码
// PREFERRED
if x == y {
    /* ... */
}
// NOT PREFERRED
if (x == y) {
    /* ... */
}


3.1.9 避免在使用enum的时候写出全名。

[Swift] 纯文本查看 复制代码
// PREFERRED
imageView.setImageWithURL(url, type: .person)
// NOT PREFERRED
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)


3.1.10 在写类方法的时候不能用简短写法,应该使用类名.方法名,这样能够保证代码的可读性。

[Swift] 纯文本查看 复制代码
// PREFERRED
imageView.backgroundColor = UIColor.whiteColor()
// NOT PREFERRED
imageView.backgroundColor = .whiteColor()


3.1.11 在非必要的时候不要写self.。

3.1.12 在编写某个方法的时候注意考虑下这个方法是否有可能被复写,如果不可能被复写那么应该使用final修饰符。还要注意加上final之后也会导致无法在测试的时候进行复写,所以还是需要综合考虑。一般而言,加上final修饰符后会提高编译的效率,所以应该尽可能地使用该修饰符。

3.1.13 在使用譬如else, catch等等类似的语句的时候,将关键字与花括号放在一行,同样遵循1TBS style规范,这边列出了常见的if/else 以及 do/catch 示范代码。

[Swift] 纯文本查看 复制代码
if someBoolean {
    // do something
} else {
    // do something else
}
do {
    let fileContents = try readFile("filename.txt")
} catch {
    print(error)
}



3.2、Access Modifiers

3.2.1 在需要的时候应该将访问修饰符放在关键字的第一位。

[Swift] 纯文本查看 复制代码
// PREFERRED
private static let kMyPrivateNumber: Int
// NOT PREFERRED
static private let kMyPrivateNumber: Int


3.2.2 访问修饰符不应该单独放一行:

[Swift] 纯文本查看 复制代码
// PREFERRED
public class Pirate {
    /* ... */
}
// NOT PREFERRED
public
class Pirate {
    /* ... */
}


3.2.3 一般来说,不要显式地写默认的 internal访问修饰符。

3.2.4 如果某个变量需要在测试的时候被使用到,那么应该标识为internal来保证@testable import ModuleName。这里需要注意的是,对于某些应该被声明为private的变量因为测试用途而声明为了internal,那么应该在注释里特别地注明。

[Swift] 纯文本查看 复制代码
/**
 This variable defines the pirate's name.
 - warning: Not `private` for `@testable`.
 */
let pirateName = "LeChuck"



3.3、Custom Operators 自定义操作符

尽可能地选用命名函数来代替自定义操作符。如果你打算引入一个自定义的操作符,那么一定要有非常充分的理由来说明为啥要讲一个新的操作符引入到全局作用域,而不是使用其他一些可替代的方式。你也可以选择去复写一些现有的操作符,譬如==来适应一些新的类型,不过要保证你添加的用法一定要与语义相符。譬如== 应该只能用于表示相等性测试并且返回一个布尔值。


3.4、Switch Statements and enums

3.4.1 在使用枚举类型作为switch的参数的时候,避免引入default关键字,而应该将没有使用的情形放到下面然后使用break关键字来避免被执行。

3.4.2 Swift中默认会在每个case的结尾进行break,因此没必要的时候不需要显式地声明break关键字。

3.4.3 The case statements should line up with the switch statement itself as per default Swift standards.

3.4.4 When defining a case that has an associated value, make sure that this value is appropriately labeled as opposed to just types (e.g. case Hunger