|
| 1 | +# Style Guide |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +이전에도 한번 정독하였지만, 시간이 지나니 잊혀지게 되어 점점 제멋대로 코딩을 하고 있는 것 같은 기분이 들었습니다. 복습도 하고 정리도 할 겸, Style Guide에 나온 것과 다르게 종종 놓치는 부분과 다시 한번 상기해봐야할 것들에 대해서 정리하였습니다. |
| 6 | + |
| 7 | +[Ray Wenderlich's Swift Style Guide](https://github.com/raywenderlich/swift-style-guide#access-control) 와 [Github's Swift Style Guide](https://github.com/github/swift-style-guide) 를 읽고 작성하였습니다. |
| 8 | + |
| 9 | +## Table of Contents |
| 10 | + |
| 11 | +- [Delegates](#Delegates) |
| 12 | +- [Inferred Context and Type Inference](#Inferred-Context-and-Type-Inference) |
| 13 | +- [User of Self](#User-of-Self) |
| 14 | +- [Final](#Final) |
| 15 | +- [Access Control](#Access-Control) |
| 16 | +- [Function Declarations](#Function-Declarations) |
| 17 | +- [Golden Path](#Golden-Path) |
| 18 | +- [Reference](#Reference) |
| 19 | + |
| 20 | +## Delegates |
| 21 | + |
| 22 | +커스텀 delegate method를 만들 때, **이름이 없는 첫 parameter는 delegate source**여야합니다. |
| 23 | + |
| 24 | +```swift |
| 25 | +❌ func didSelectName(namePicker: NamePickerViewController, name: String) |
| 26 | +✅ func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) |
| 27 | + |
| 28 | +❌ func namePickerShouldReload() -> Bool |
| 29 | +✅ func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool |
| 30 | +``` |
| 31 | + |
| 32 | +## Inferred Context and Type Inference |
| 33 | + |
| 34 | +짧고 명확하게 코드를 작성하기 위하여 Compiler에서 추론된 context를 사용합니다. |
| 35 | + |
| 36 | +```swift |
| 37 | +❌ let selector = #selector(ViewController.viewDidLoad) |
| 38 | +✅ let selector = #selector(viewDidLoad) |
| 39 | + |
| 40 | +❌ view.backgroundColor = UIColor.red |
| 41 | +✅ view.backgroundColor = .red |
| 42 | + |
| 43 | +❌ let message: String = "Click the button" |
| 44 | +✅ let message = "Click the button" |
| 45 | + |
| 46 | +❌ let currentBounds: CGRect = computeViewBounds() |
| 47 | +✅ let currentBounds = computeViewBounds() |
| 48 | +``` |
| 49 | + |
| 50 | +## User of Self |
| 51 | + |
| 52 | +Swift는 Object의 properties나 method를 호출할 필요가 없으므로 `self`의 사용을 피합니다. |
| 53 | + |
| 54 | +단, Compiler가 요구한 경우에는 사용합니다. |
| 55 | + |
| 56 | +> 개인적으로 지키지 않았던 내용입니다. |
| 57 | +> |
| 58 | +> closure 내에서 아래와 같은 구문을 사용해왔었는데, 일종의 compiler 버그라고 합니다. |
| 59 | +> |
| 60 | +> https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md#relying-on-a-compiler-bug |
| 61 | +> |
| 62 | +> ```swift |
| 63 | +> ❌ |
| 64 | +> guard let `self` = self else { |
| 65 | +> return |
| 66 | +> } |
| 67 | +> |
| 68 | +> ✅ |
| 69 | +> guard let self = self else { |
| 70 | +> return |
| 71 | +> } |
| 72 | +> ``` |
| 73 | +
|
| 74 | +## Final |
| 75 | +
|
| 76 | +`final` 키워드를 사용하여 method, property, 또는 subscript에 대한 override를 compile time에 error를 보내줘 막을 수 있습니다. (참고 : [The Swift Programming Language - Inheritance](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html)) |
| 77 | +
|
| 78 | +`final` 키워드를 class나 members에 사용하는 것은 주요 주제로 부터 산만하게 하게 할 수 있고, 필요하지 않을 수도 있습니다. 그럼에도 불구하고 `final`은 가끔 의도를 명확히 할 수 있으며 그럴만한 가치가 있습니다. |
| 79 | +
|
| 80 | +## Access Control |
| 81 | +
|
| 82 | +모든 access control은 주요 주제로 부터 산만하게 하게 할 수 있고, 필요하지 않을 수도 있습니다. `private` 과 `fileprivate` 을 적절하게 사용한다면 명확성을 추가하고 encapsulation을 촉진할 수 있습니다. |
| 83 | +
|
| 84 | +> Using `private` and `fileprivate` appropriately, however, adds clarity and promotes encapsulation. 음.. promote의 뜻이 애매합니다. |
| 85 | +
|
| 86 | +access control을 선행 property specifier로 지정합니다. `@IBAction`, `@IBOutlet`, `@discardableResult` 등과 같은 속성이나 `static` 만이 access control 앞에 올 수 있습니다. |
| 87 | +
|
| 88 | +```swift |
| 89 | +🤔 fileprivate let message = "Great Scott!" |
| 90 | +✅ private let message = "Great Scott!" |
| 91 | +
|
| 92 | +class TimeMachine { |
| 93 | +❌ lazy dynamic private var fluxCapacitor = FluxCapacitor() |
| 94 | +✅ private dynamic lazy var fluxCapacitor = FluxCapacitor() |
| 95 | +} |
| 96 | +``` |
| 97 | +
|
| 98 | +## Function Declarations |
| 99 | +
|
| 100 | +긴 함수의 경우, 각 parameter를 작성할 때 새로운 줄과 들여쓰기를 합니다. |
| 101 | +
|
| 102 | +```swift |
| 103 | +func reticulateSplines( |
| 104 | + spline: [Double], |
| 105 | + adjustmentFactor: Double, |
| 106 | + translateConstant: Int, comment: String |
| 107 | +) -> Bool { |
| 108 | + // reticulate code goes here |
| 109 | +} |
| 110 | +``` |
| 111 | +
|
| 112 | +`(Void)`를 사용하지 않습니다. 또한 closure와 function의 output에 대해서는 `()` 대신 `Void`를 사용합니다. |
| 113 | +
|
| 114 | +```swift |
| 115 | +❌ func updateConstraints() -> () {} |
| 116 | +✅ func updateConstraints() -> Void {} |
| 117 | +
|
| 118 | +
|
| 119 | +❌ typealias CompletionHandler = (result) -> () |
| 120 | +✅ typealias CompletionHandler = (result) -> Void |
| 121 | +``` |
| 122 | +
|
| 123 | +## Golden Path |
| 124 | +
|
| 125 | +조건문에 대하여 코딩할 때, 좌측 여백의 코드는 "golden" 또는 "happy" path여야합니다. |
| 126 | +
|
| 127 | +즉, `if` 구문으로 감싸지말고, `guard` 구문을 활용하면 됩니다. |
| 128 | +
|
| 129 | +```swift |
| 130 | +func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies { |
| 131 | +
|
| 132 | + guard let context = context else { |
| 133 | + throw FFTError.noContext |
| 134 | + } |
| 135 | + guard let inputData = inputData else { |
| 136 | + throw FFTError.noInputData |
| 137 | + } |
| 138 | +
|
| 139 | + // use context and input to compute the frequencies |
| 140 | + return frequencies |
| 141 | +} |
| 142 | +``` |
| 143 | +
|
| 144 | +## Reference |
| 145 | +
|
| 146 | +- https://github.com/raywenderlich/swift-style-guide |
| 147 | +- https://github.com/github/swift-style-guide |
| 148 | +- https://github.com/minsOne/swift-style-guide/blob/master/README_KR.md |
0 commit comments