Add Initial Guidelines
This commit is contained in:
Родитель
f3045fbdea
Коммит
d2336b5973
|
@ -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).
|
|
@ -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)
|
||||
}
|
||||
|
||||
```
|
|
@ -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?
|
||||
}
|
||||
|
||||
```
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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
|
||||
}
|
||||
```
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
23
README.md
23
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче