This commit is contained in:
Chen Yang 2020-06-04 12:43:38 -07:00
Родитель f3045fbdea
Коммит d2336b5973
10 изменённых файлов: 553 добавлений и 11 удалений

52
CONTRIBUTING.md Normal file
Просмотреть файл

@ -0,0 +1,52 @@
# Contributing to the Swift Coding Guidelines
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## How to contribute
### Proposing new guidelines
These guidelines are not exhaustive. If you have a proposal for a new convention to be added to the guidelines, please create an issue in this repository to track it. Be sure to add "Convention:", "Rationale:", and "Example:" sections in the template and provide a short succinct title with the recommended page title. The title shouldn't contain the convention itself, but should explain the area of code the convention refers to. For example, a title might say `Immutability` instead of `Use constants over variables whenever possible`.
### Improve existing guidelines
There are several areas through which existing rules can be improved.
* Spelling, grammatical, and tonal errors
* Improving wording for conciseness or clarity
* Better or more realistic example code
For issues of these types, please submit a pull request with the proposed improvement and the guideline maintainers will review the changes.
### Contributing new guidelines
Pick a new unimplemented task from issues in this repository and write the conventions in a new markdown file. Follow the recommended [format and tone](#format-and-tone) for the document and don't forget to [update the table of contents as well](#updating-table-of-contents). Submit a pull request with the new proposed conventions and the guideline maintainers will review the proposal.
## Format and tone
Follow the format and tone of existing guidelines in the repository. The tone of the writing should be impersonal, third person, and succinct. Each page should include a title, an optional general introduction of the page, and the three following sections.
### Convention
Conventions should be direct and easy to follow. It is acceptable for conventions to allow for exceptions. Do not explain the reasoning for conventions in this section, simply state the convention that code should follow. This section may contain multiple conventions for a given area of code.
### Rationale
Rationale should describe why the convention is preferable to the alternative. Links to external sites are allowed and encouraged so long as the sites are trustworthy and the URLs are unlikely to change or go away. Official Swift Language guide is generally preferred. If possible use real world examples where following the convention would have prevented issues.
### Examples
Examples should be written in valid Swift snippets and not written in pseudo-code. If good and bad examples are easily mixed, use a single block of code and annotate the good examples with `// bad: explanation of the convention this fails to follow` or `// good: optional explanation of the convention this follows`. Prefer to have bad examples come before good examples and prefer to have individual examples of bad and good paired for easy comparison. For more contextual conventions that require more complex code snippets, break up the examples section into two blocks of code, one titled `Bad` followed by one titled `Good`.
## Updating table of contents
There should be no links to empty pages from either the [README.md](README.md) file or the category table of content pages. Empty pages may exist in the repository as long as no active page links to it. When adding a new convention, the contributor is responsible for adding links in the category table of contents as well as to [README.md](README.md).

55
Delegates.md Normal file
Просмотреть файл

@ -0,0 +1,55 @@
# Delegates
In general, follow the delegation section in the [official guidelines](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html).
## Convention
Implementing delegate methods should be optional.
## Rationale
Delegates augment existing behavior in the delegating object, they don't provide it directly. The delegating object must be fully self-sufficient, even if there is no delegate to augment its default behavior. It follows that if the delegate itself is not a required property on the delegating object that none of the methods on the delegate should be required. The delegate can implement as many or as few delegate callbacks as it cares about based on the context of that individual delegate.
## Implementation
### Default Implementations (preferred)
Provide an extension with default implementations after the protocol definition. This is preferred because the conforming types don't have to be Objective-C compatible.
#### Example
``` Swift
protocol DiceGameDelegate {
func gameShouldStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
extension DiceGameDelegate {
func gameShouldStart(_ game: DiceGame) -> Bool {
return false
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {}
func gameDidEnd(_ game: DiceGame) {}
}
```
### Optional methods
Define all the methods as optional. This can only be done if the protocol is available from Objective-C (has the `@objc` attribute). All conforming types will need to be Objective-C compatible.
#### Example
``` Swift
@objc protocol DiceGameDelegate {
@objc optional func gameShouldStart(_ game: DiceGame)
@objc optional func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
@objc optional func gameDidEnd(_ game: DiceGame)
}
```

74
EarlyReturn.md Normal file
Просмотреть файл

@ -0,0 +1,74 @@
# Early Return
## Convention
It's recommended to use `guard ... else` statements and early exits on top of a function to validate parameters or other variables.
## Syntax
``` Swift
func someFunction(argumentLabel parameterName: parameterType) {
guard condition else {
// log error or other actions
return
}
}
```
For more information about `guard ... else`, follow the [language guide](https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html#ID525).
## Rationale
Using a guard statement to return early prevents parentheses nesting and better preserves variable immutability.
## Examples
### Good: with early return, return value is a constant
```Swift
class ElementList {
func index(of element: Element) -> Int? {
guard let elements = self.elements else {
return nil
}
let elementIndex = elements.index(of: element)
guard elementIndex != NSNotFound else {
log("Element not found")
return nil
}
return elementIndex
}
}
var elements: ElementList?
}
```
### Bad: without early return, return value is a variable
```Swift
class ElementList {
func index(of element: Element) -> Int? {
// The following variable is a variable
var index: Int? = nil
if let elements = self.elements {
let elementIndex = elements.index(of: element)
if elementIndex != NSNotFound {
index = elementIndex
} else {
log(("Element not found")
}
}
return index
}
}
var elements: ElementList?
}
```

46
FileContentOrdering.md Normal file
Просмотреть файл

@ -0,0 +1,46 @@
# File Content Ordering
## Convention
Most "important" stuff at the top; and least "important" stuff at the bottom. The importance is usually determined by the [access level](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html); the less restrictive the access level, the more "important" it is.
## Rationale
Ordering file contents by access level ensures that the entities with the least restrictive access level get the most visibility, which are likely the components that developers interact with the most.
## Example
```Swift
// public enums, classes, interfaces etc.
public class House {
// public functions
public init {
...
}
// public variables
public static let continent = "Westeros"
// internal functions
internal func addHouseMember() {
...
}
// internal variables
internal let sigil: Animal
// private functions
private func reorganizeHouse() {
...
}
// private variables
private var size: Int = 0
}
// fileprivate extensions or constants
fileprivate maximumHouseSize: Int = 50
```

91
Immutability.md Normal file
Просмотреть файл

@ -0,0 +1,91 @@
# Immutability
## Convention
Immutability is an important concept in Swift; use constants over variables wherever possible.
## Rationale
The Swift compiler enforces that the constants will not be changed unexpectedly thus making the program safer.
## Examples
### if-statement
#### Good: the constant preserves immutability
```Swift
func spacing(for orientation: NSUserInterfaceLayoutOrientation) -> Int {
let spacing = orientation = .horizontal ? 25 : 15
return spacing
}
```
#### Bad: unnecessary use of variable does not preserve immutability
```Swift
func spacing(for orientation: NSUserInterfaceLayoutOrientation) -> Int {
var spacing: Int
if orientation = .horizontal {
spacing = 25
} else {
spacing = 15
}
return spacing
}
```
### Switch Statement
#### Good: use a closure to calculate the value of the constant
```Swift
enum Direction {
case north, south, east, west
}
func move(point: (Int, Int), towards direction: Direction) {
let newPoint = { () -> (Int, Int) in
switch direction {
case .north:
return (point.0, point.1 - 1)
case .south:
return (point.0, point.1 + 1)
case .east:
return (point.0 + 1, point.1)
case .west:
return (point.0 - 1, point.1 - 1)
}
}()
if newPoint == (0,0) {
print("Success!")
}
}
```
#### Bad: unnecessary use of variable does not preserve immutability
```Swift
enum Direction {
case north, south, east, west
}
func move(point: (Int, Int), towards direction: Direction) {
var newPoint: (Int, Int)
switch direction {
case .north:
newPoint = (point.0, point.1 - 1)
case .south:
newPoint = (point.0, point.1 + 1)
case .east:
newPoint = (point.0 + 1, point.1)
case .west:
newPoint = (point.0 - 1, point.1 - 1)
}
if newPoint == (0,0) {
print("Success!")
}
}
```

50
Naming.md Normal file
Просмотреть файл

@ -0,0 +1,50 @@
# Naming
In general, follow the [official guidelines](https://swift.org/documentation/api-design-guidelines/#naming).
## Conventions
### Naming a file
There's no three-letter prefix in Swift file naming. For example, if an Objective-C file named XYZEvent.m is to be converted to Swift, it should be renamed to Event.swift.
### Naming a class or a protocol available in Objective-C
Provide a pre-fixed Objective-C name before the class or protocol definition.
#### Example
``` Swift
@objc class Event {
// bad: exported to Objective-C as class Event without a prefix
}
@objc(XYZEvent)
class Event {
// good
}
```
### Naming a protocol
There is no need to add a prefix to a Swift protocol in order to distinguish it from a Swift class or struct.
- Protocols that describe what something is should read as nouns (e.g. Collection).
- Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting)."
#### Example
``` Swift
protocol IFooEventHandler {
// bad: the "I" prefix is not necessary
}
protocol FooEventHandler {
// bad: "er" is not a good suffix for a protocol
}
protocol FooEventHandling {
// good
}
```

85
PropertyObservers.md Normal file
Просмотреть файл

@ -0,0 +1,85 @@
# Property observers
In general, follow the [official guidelines](https://docs.swift.org/swift-book/LanguageGuide/Properties.html).
## Convention
Use guard statements to check if the property has changed in property observers.
## Rationale
Checking whether the property value has actually changed can help avoid potentially expensive work.
## Examples
### Good: with `guard` statement, avoid doing unnecessary work
``` Swift
var propWithAnObserver: Bool {
didSet {
guard oldValue != propWithAnObserver else {
return
}
expensiveWork(propWithAnObserver)
needsLayout = true
}
}
```
### Bad: without `guard` statement, do unnecessary work
``` Swift
var propWithAnObserver: Bool {
didSet {
expensiveWork(propWithAnObserver)
needsLayout = true
}
}
```
## Convention - Use `defer` statement to trigger property observers after the initializer executes
Swift does not call property observers when a class is setting its own properties in the initializer. Use a `defer` statement to wrap the property setting statements so that the property assignment and property observers are called after the initializer has been called. This can reduce duplicate code in the initializer.
### Exceptions
Since the property assignment happens after the initializer with the use of `defer`, this cannot be used on non-optional stored properties because non-optional stored properties require an assigned value before the initializer finishes. However, turning a non-optional stored property into an optional property just to avoid duplication is not recommended.
### Good: Avoid unnecessary duplicate calls to announceSymbol()
``` Swift
class House {
init(symbol: String?) {
defer {
self.symbol = symbol
}
}
var symbol: String? {
didSet {
guard oldValue != symbol else {
return
}
announceSymbol()
}
}
}
```
### Bad: Unnecessary duplicate calls to announceSymbol()
``` Swift
class House {
init(symbol: String?) {
self.symbol = symbol
announceSymbol()
}
var symbol: String? {
didSet {
guard oldValue != symbol else {
return
}
announceSymbol()
}
}
}
```

Просмотреть файл

@ -1,17 +1,20 @@
# Introduction
# Contributing
The Swift coding conventions guide documents many best practices for writing Swift. Guidance here comes from a combination of our collective practical experiences and documentation from Apple. This is meant to be a living document and all contents are perpetually open for debate and improvement. For details on how to contribute, see the [contribution guidelines](CONTRIBUTING.md).
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
## Resources
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
- [Swift official language guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html).
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
# Table of Contents
* [Delegates](Delegates.md)
* [Early Return](EarlyReturn.md)
* [File Content Ordering](FileContentOrdering.md)
* [Immutability](Immutability.md)
* [Naming](Naming.md)
* [Property Observers](PropertyObservers.md)
* [Type Inference](TypeInference.md)
# Legal Notices

87
TypeInference.md Normal file
Просмотреть файл

@ -0,0 +1,87 @@
# Type Inference
## Convention
Use type annotation as little as possible when declaring a constant or a variable; instead let the compiler infer the variable type. This convention does not apply to properties.
## Rationale
Type inference helps reduces line noise and maintain clearer code.
## Shorthand Dot Syntax
When assigning an enum or a static property to a variable or a constant, if the type of the variable or the constant is known, use the shorthand dot syntax to access the enum or static property.
### Examples
#### Good: use shorthand dot syntax
``` Swift
func layout(with direction: NSUserInterfaceLayoutOrientation) {
switch direction {
case .horizontal:
layoutHorizontally()
case .vertical:
layoutVertically()
}
}
```
#### Bad: unnecessarily verbose without shorthand dot syntax
``` Swift
func layout(with direction: NSUserInterfaceLayoutOrientation) {
switch direction {
case NSUserInterfaceLayoutOrientation.horizontal:
layoutHorizontally()
case NSUserInterfaceLayoutOrientation.vertical:
layoutVertically()
}
}
```
## Constants and Variables
Let the compiler infer the type of a constant or variable whenever possible.
### Examples
#### Good: no left-hand-side type annotations
``` Swift
let horizontalMargin = 10
let font = NSFont.systemFont(ofSize: 15.0)
```
#### Bad: with unnecessary left-hand-side type annotations
``` Swift
let horizontalMargin: Int = 10
let font: NSFont = NSFont.systemFont(ofSize: 15.0)
```
## Properties
Use type annotations instead of type inference for properties for API clarity.
### Examples
#### Good: property use type annotations
``` Swift
class CustomLabel {
let textColor: UIColor = .white
}
```
#### Bad: property does not use type annotations
``` Swift
class CustomLabel {
let textColor = UIColor.white
}
```

Просмотреть файл

@ -1 +0,0 @@
theme: jekyll-theme-midnight