Swift Style Guide (번역)
API Design Guidelines
Fundamentals (기본 사항)
사용 시점의 명확성이 가장 중요한 목표입니다.
메서드나 프로퍼티와 같은 엔티티는 한 번만 선언되지만 반복적으로 사용됩니다. 이러한 사용처를 명확하고 간결하게 만들 수 있도록 API를 설계하세요. 설계를 평가할 때 선언문을 읽는 것만으로는 충분하지 않으며, 항상 사용 사례를 검토하여 문맥상 명확하게 보이는지 확인해야 합니다.
간결함보다 더 중요한 것은 명확성입니다.
Swift 코드는 간결할 수 있지만, 최소한의 문자로 가능한 한 가장 작은 코드를 구현하는 것은 목표가 아닙니다. Swift 코드의 간결성은 강력한 타입 시스템과 상용구를 자연스럽게 줄여주는 기능의 부작용입니다.
모든 선언에 대해 문서 주석을 작성하세요.
문서 작성을 통해 얻은 인사이트는 디자인에 큰 영향을 미칠 수 있으므로 미루지 마세요. > API의 기능을 간단한 용어로 설명하는 데 어려움이 있다면 잘못된 API를 설계한 것일 수 있습니다.
DETAIL1
스위프트 마크다운을 사용하세요.
엔티티를 설명하는 요약부터 시작하세요. 주로 API는 선언과 요약을 통해 완전히 이해할 수 있습니다.
1 2 3
/// Returns a "view" of `self` containing the same elements in /// reverse order. func reversed() -> ReverseCollection
DETAIL2
요약에 집중하세요. 요약이 가장 중요한 부분입니다. 훌륭한 문서 댓글은 대부분 훌륭한 요약으로 구성되어 있습니다.
가능하면 마침표로 끝나는 단일 문장 조각을 사용하세요. 완전한 문장을 사용하지 마세요.
함수나 메서드가 수행하는 작업과 반환하는 결과를 설명하며, 널 효과나 void 반환은 생략합니다.
1 2 3 4 5 6 7 8 9 10
/// Inserts `newHead` at the beginning of `self`. mutating func prepend(_ newHead: Int) /// Returns a `List` containing `head` followed by the elements /// of `self`. func prepending(_ head: Element) -> List /// Removes and returns the first element of `self` if non-empty; /// returns `nil` otherwise. mutating func popFirst() -> Element?
참고: 위의 popFirst와 같이 드물게 주석이 세미콜론으로 구분된 여러 문장으로 구성되는 경우가 있습니다.
사용자가 접근하는 항목을 설명합니다.
1 2
/// Accesses the `index`th element. subscript(index: Int) -> Element { get set }
이니셜라이저가 생성하는 내용을 설명합니다.
1 2
/// Creates an instance containing `n` repetitions of `x`. init(count n: Int, repeatedElement x: Element)
다른 모든 선언에 대해서 선언된 엔티티가 무엇인지 설명합니다.
1 2 3 4 5 6 7 8
/// A collection that supports equally efficient insertion/removal /// at any position. struct List { /// The element at the beginning of `self`, or `nil` if self is /// empty. var first: Element? ...
원하는 경우 하나 이상의 단락과 글머리 기호 항목을 계속 작성합니다.
단락은 빈 줄로 구분하고 완전한 문장을 사용합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/// Writes the textual representation of each ← Summary /// element of `items` to the standard output. /// ← Blank line /// The textual representation for each item `x` ← Additional discussion /// is generated by the expression `String(x)`. /// /// - Parameter separator: text to be printed ⎫ /// between items. ⎟ /// - Parameter terminator: text to be printed ⎬ Parameters section /// at the end. ⎟ /// ⎭ /// - Note: To print without a trailing ⎫ /// newline, pass `terminator: ""` ⎟ /// ⎬ Symbol commands /// - SeeAlso: `CustomDebugStringConvertible`, ⎟ /// `CustomStringConvertible`, `debugPrint`. ⎭ public func print( _ items: Any..., separator: String = " ", terminator: String = "\n")
DETAIL3
알려진 기호 문서 마크업 요소를 사용하여 요약 이외의 정보를 추가할 수 있습니다.
기호 명령 구문으로 인식되는 글머리 기호 항목을 파악하고 사용하세요.
Xcode와 같이 널리 사용되는 개발 도구는 다음 키워드로 시작하는 글머리 기호 항목을 특별히 처리합니다.
Naming
Promote Clear Usage (명확한 사용법 지향)
이름을 사용하는 코드를 읽는 사람이 모호하지 않게 하기 위해 필요한 모든 단어를 포함하세요.
DETAIL1
예를 들어 컬렉션 내에서 지정된 위치에 있는 요소를 제거하는 메서드를 생각해 보겠습니다.
1 2 3 4 5
// Right Example extension List { public mutating func remove(at position: Index) -> Element } employees.remove(at: x)
메서드 서명에서 at이라는 단어를 생략하면 독자에게 메서드가 제거할 요소의 위치를 나타내기 위해 x를 사용하는 것이 아니라 x와 같은 요소를 검색하여 제거한다는 것을 암시할 수 있습니다.
1 2
// Wrong Example employees.remove(x) // unclear: are we removing x?
불필요한 단어는 생략하세요. 이름에 포함된 모든 단어는 사용 사이트에서 중요한 정보를 전달해야 합니다.
DETAIL2
의도를 명확히하기 위해 더 많은 단어가 필요할 수 있지만, 독자가 이미 알고 있는 정보와 중복되는 단어는 생략해야 합니다. 특히 단순히 타입 정보를 반복하는 단어는 생략합니다.
1 2 3 4
// Wrong Example public mutating func removeElement(_ member: Element) -> Element? allViews.removeElement(cancelButton)
이 경우 Element라는 단어는 호출 쪽에서 의미를 더해주지 않습니다. 따라서 이 API가 더 좋을 것입니다.
1 2 3 4
// Right Example public mutating func remove(_ member: Element) -> Element? allViews.remove(cancelButton) // clearer
모호성을 피하기 위해 타입 정보를 반복해야 하는 경우도 있지만, 일반적으로는 타입보다는 매개변수의 역할을 설명하는 단어를 사용하는 것이 좋습니다. 자세한 내용은 다음 항목을 참조하세요.
변수, 매개변수 및 관련 타입은 타입 제약 조건이 아닌 역할에 따라 이름을 지정합니다.
DETAIL3
1 2 3 4 5 6 7 8
// Wrong Example var **string** = "Hello" protocol ViewController { associatedtype **ViewType** : View } class ProductionLine { func restock(from **widgetFactory**: WidgetFactory) }
이런 식으로 타입 이름을 작성하면 명확성과 표현력이 떨어집니다. 대신 엔티티의 역할을 표현할 수 있는 이름을 선택하도록 노력하세요.
1 2 3 4 5 6 7 8
// Right Example var **greeting** = "Hello" protocol ViewController { associatedtype **ContentView** : View } class ProductionLine { func restock(from **supplier**: WidgetFactory) }
associatedtype이 프로토콜 제약 조건에 너무 강하게 연관되어 프로토콜 이름이 역할인 경우, 프로토콜 이름에 프로토콜을 추가하여 충돌을 피하세요.
1 2 3 4
protocol Sequence { associatedtype Iterator : Iterator**Protocol** } protocol Iterator**Protocol** { ... }
타입 정보를 보완하여 매개변수의 역할을 명확히 합니다.
DETAIL4
특히 매개변수 유형이 NSObject, Any, AnyObject이거나 Int 또는 String과 같은 기본 유형인 경우 사용 시점의 유형 정보 및 컨텍스트가 의도를 완전히 전달하지 못할 수 있습니다. 이 예에서는 선언은 명확할 수 있지만 사용하는 곳에서는 모호합니다.
1 2 3 4
// Wrong Example func add(_ observer: NSObject, for keyPath: String) grid.add(self, for: graphics) // vague
명확성을 회복하려면 의미가 약한 각 매개변수 앞에 그 역할을 설명하는 명사를 붙이세요.
1 2 3
// Right Example func addObserver(_ observer: NSObject, forKeyPath path: String) grid.addObserver(self, forKeyPath: graphics) // clear
Strive for Fluent Usage (능숙한 사용을 의한 노력)
사용하는 쪽에서 문법적으로 영어 구문을 형성하는 메서드 및 함수 이름을 선호합니다.
DETAIL1
1 2 3 4
// Right Example x.insert(y, at: z) “x, insert y at z” x.subViews(havingColor: y) “x's subviews having color y” x.capitalizingNouns() “x, capitalizing nouns”
1 2 3 4
// Wrong Example x.insert(y, position: z) x.subViews(color: y) x.nounCapitalize()
매개변수가 사용 의미의 핵심이 아닌 경우 한두 개의 인수 이후에는 유창성이 저하되는 것은 허용됩니다.
1 2 3
AudioUnit.instantiate( with: description, options: [.inProcess], completionHandler: stopProgressBar)
- 팩토리 메서드의 이름은 “make”로 시작합니다(예: x.makeIterator()).
이니셜라이저 및 팩토리 메서드 호출의 첫 번째 인수는 기본 이름으로 시작하는 문장을 형성해서는 안 됩니다(예: x.makeWidget(cogCount: 47)).
DETAIL2
예를 들어 이러한 호출의 첫 번째 인수는 기본 이름으로서 같은 구문의 일부로 읽히지 않습니다.
1 2 3 4
// Right Example let foreground = Color(red: 32, green: 64, blue: 128) let newPart = factory.makeWidget(gears: 42, spindles: 14) let ref = Link(target: destination)
아래에서는 API 작성자가 첫 번째 인수를 사용하여 문법적 연속성을 만들려고 시도했습니다.
1 2 3 4
// Wrong Example let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128) let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14) let ref = Link(to: destination)
실제로 이 지침은 인자 레이블에 대한 지침과 함께 호출이 값 보존 유형 변환을 수행하지 않는 한 첫 번째 인자에 레이블을 갖는다는 것을 의미합니다.
1
let rgbForeground = RGBColor(cmykForeground)
사이드 이펙트에 따라 함수와 메서드에 이름을 붙입니다.
사이드 이펙트가 없는 함수는 명사 구문으로 읽어야 합니다(예:
x.distance(to: y), i.successor())
사이드 이펙트가 있는 것은
print(x), x.sort(), x.append(y)
와 같은 명령형 동사 구문으로 읽어야 합니다.mutating/nonmutating 메서드 쌍의 이름을 일관되게 지정합니다.
mutating 메서드에는 종종 비슷한 의미를 가진 nonmutating 변형이 있지만 인스턴스를 제자리에서 업데이트하지 않고 새 값을 반환합니다.
연산이 동사로 자연스럽게 설명되는 경우, 동사의 명령형을 돌연변이 메서드에 사용하고 “ed” 또는 “ing” 접미사를 적용하여 돌연변이가 일어나지 않는 상대방의 이름을 지정합니다.
Mutating Nonmutating x.sort() z = x.sorted() x.append(y) z = x.appending(y) DETAIL3
동사의 과거 분사를 사용하여 nonmutating 이름을 지정하는 것을 선호합니다(일반적으로 “ed”를 추가)
1 2 3 4 5 6 7 8
/// Reverses `self` in-place. mutating func reverse() /// Returns a reversed copy of `self`. func reversed() -> Self ... x.reverse() let y = x.reversed()
동사에 직접 목적어가 있어 “ed”를 추가하는 것이 문법적으로 맞지 않는 경우 동사의 현재 분사를 사용하여 “ing”를 추가하여 nonmutating에 이름을 지정합니다.
1 2 3 4 5 6 7 8
/// Strips all the newlines from `self` mutating func stripNewlines() /// Returns a copy of `self` with all the newlines stripped. func strippingNewlines() -> String ... s.stripNewlines() let oneLine = t.strippingNewlines()
- 연산이 명사로 자연스럽게 설명되는 경우, 비변이 메서드에 명사를 사용하고 “form” 접두사를 적용하여 변이 메서드에 해당하는 이름을 지정합니다.
Nonmutating Mutating x = y.union(z) y.formUnion(z) j = c.successor(i) c.formSuccessor(&i)
- 부울 메서드 및 프로퍼티의 사용은 mutating이 발생하지 않는 경우 수신자에 대한 주장으로 읽어야 합니다(예: x.isEmpty, line1.intersects(line2)).
- 어떤 것이 무엇인지 설명하는 프로토콜은 명사로 읽어야 합니다(예: 컬렉션).
- 기능을 설명하는 프로토콜은 접미사 able, ible 또는 ing를 사용하여 이름을 지정해야 합니다(예: Equatable, ProgressReporting).
- 타입, 속성, 변수 및 상수의 이름은 명사로 읽어야 합니다.
Use Terminology Well (올바른 용어 사용)
더 일반적인 단어로도 충분히 의미를 전달할 수 있다면 모호한 용어는 피하세요. ‘피부’가 목적에 부합한다면 ‘표피’라고 말하지 마세요. 전문 용어는 필수적인 커뮤니케이션 도구이지만, 놓칠 수 있는 중요한 의미를 포착할 때만 사용해야 합니다.
전문 용어를 사용할 경우 정해진 의미에 충실하세요.
DETAIL1
일반적인 단어 대신 전문 용어를 사용하는 유일한 이유는 모호하거나 불분명할 수 있는 내용을 정확하게 표현하기 위해서입니다. 따라서 API는 해당 용어가 허용되는 의미에 따라 엄격하게 사용해야 합니다.
- 전문가를 놀라게 하지 마세요. 이미 이 용어에 익숙한 사람이라면 우리가 새로운 의미를 만들어낸 것처럼 보이면 놀랄 것이고 아마도 분노할 것입니다.
- 초보자를 혼동하지 마세요. 이 용어를 알려고 하는 사람은 누구나 웹 검색을 통해 기존 의미를 찾을 수 있습니다.
약어는 피하세요. 약어, 특히 비표준 약어는 줄임말이 아닌 형태로 정확하게 번역해야 이해가 가능하기 때문에 사실상 전문 용어에 해당합니다.
사용하는 약어의 의도된 의미는 웹 검색을 통해 쉽게 찾을 수 있어야 합니다.
선례를 수용하세요. 기존 문화에 순응하는 대신 초보자를 위해 용어를 최적화하지 마세요.
DETAIL2
초보자가 List의 의미를 더 쉽게 이해할 수 있더라도 List와 같은 단순화된 용어를 사용하는 것보다 연속적인 데이터 구조의 이름을 Array로 지정하는 것이 더 낫습니다.
배열은 최신 컴퓨팅의 기본이므로 모든 프로그래머는 배열이 무엇인지 알고 있거나 곧 알게 될 것입니다. 대부분의 프로그래머가 익숙한 용어를 사용하면 웹 검색과 질문에 대해 이점을 얻을 수 있습니다.
수학과 같은 특정 프로그래밍 영역에서는 PositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)와 같은 설명 문구보다는 sin(x)와 같이 널리 사용되는 용어를 사용하는 것이 좋습니다. 이 경우 축약어를 피하라는 지침보다 선례가 더 중요하다는 점에 유의하세요. 완전한 단어는 사인이지만 “sin(x)”는 수십 년 동안 프로그래머와 수학자 사이에서 일반적으로 사용되어 왔습니다.
Conventions (규칙)
General Conventions (일반 규칙)
O(1)이 아닌 모든 계산된 프로퍼티의 복잡도를 문서화합니다.
사람들은 프로퍼티를 멘탈 모델로 저장해 두었기 때문에 프로퍼티 액세스에 큰 계산이 필요하지 않다고 생각하는 경우가 많습니다. 이러한 가정이 깨질 수 있는 경우 반드시 경고하세요.
자유 함수(멤버 함수가 아닌 일반 함수)보다 메서드와 프로퍼티를 선호하세요. 자유 함수는 특별한 경우에만 사용됩니다:
DETAIL
- 뚜렷한 자아가 없을 때
self
:min(x, y, z)
- 함수가 제약되지 않은 제네릭인 경우
print(x)
- 함수 구문이 기존 도메인 표기법의 일부인 경우
sin(x)
- 뚜렷한 자아가 없을 때
대소문자 규칙을 따릅니다.
유형과 프로토콜의 이름은 대문자 대소문자를 사용합니다. 그 외 모든 것은 소문자입니다. - DETAIL 미국 영어에서 일반적으로 모두 대문자로 표시되는 약어와 이니셜은 대소문자 규칙에 따라 대문자 또는 소문자로 통일해야 합니다.
1 2 3
var utf8Bytes: [UTF8.CodeUnit] var isRepresentableAsASCII = true var userSMTPServer: SecureSMTPServer
그 외의 약어는 일반 단어로 취급해야 합니다:
1 2
var radarDetector: RadarScanner var enjoysScubaDiving = true
메서드는 동일한 기본 의미를 공유하거나 서로 다른 도메인에서 작동하는 경우 기본 이름을 공유할 수 있습니다.
DETAIL
예를 들어, 다음과 같은 방법은 본질적으로 동일한 작업을 수행하므로 권장됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
extension Shape { /// Returns `true` if `other` is within the area of `self`; /// otherwise, `false`. func contains(_ other: Point) -> Bool { ... } /// Returns `true` if `other` is entirely within the area of `self`; /// otherwise, `false`. func contains(_ other: Shape) -> Bool { ... } /// Returns `true` if `other` is within the area of `self`; /// otherwise, `false`. func contains(_ other: LineSegment) -> Bool { ... } }
또한 Shape 타입과 컬렉션은 별도의 도메인이므로 동일한 프로그램에서도 사용할 수 있습니다.
1 2 3 4 5 6
// Right Example extension Collection where Element : Equatable { /// Returns `true` if `self` contains an element equal to /// `sought`; otherwise, `false`. func contains(_ sought: Element) -> Bool { ... } }
그러나 이러한 인덱스 메서드는 서로 다른 의미를 가지므로 이름을 다르게 지정해야 합니다.
1 2 3 4 5 6 7 8
// Wrong Example extension Database { /// Rebuilds the database's search index func index() { ... } /// Returns the `n`th row in the given table. func index(_ n: Int, inTable: TableID) -> TableRow { ... } }
마지막으로, 타입 추론이 있는 경우 모호성을 유발하므로 ‘반환 유형에 대한 오버로딩’을 피하세요.
1 2 3 4 5 6 7 8 9 10
// Wrong Example extension Box { /// Returns the `Int` stored in `self`, if any, and /// `nil` otherwise. func value() -> Int? { ... } /// Returns the `String` stored in `self`, if any, and /// `nil` otherwise. func value() -> String? { ... } }
Parameters (매개변수)
1
func move(from start: Point, to end: Point)
문서를 제공할 매개변수 이름을 선택합니다. 매개변수 이름은 함수나 메서드의 사용 지점에 나타나지 않더라도 중요한 설명 역할을 합니다.
DETAIL1
이러한 이름을 선택하면 문서를 읽기 쉽게 만들 수 있습니다. 예를 들어, 이러한 이름을 사용하면 문서를 자연스럽게 읽을 수 있습니다.
1 2 3 4 5 6 7
// Right Example /// Return an `Array` containing the elements of `self` /// that satisfy `predicate`. func filter(_ predicate: (Element) -> Bool) -> [Generator.Element] /// Replace the given `subRange` of elements with `newElements`. mutating func replaceRange(_ subRange: Range, with newElements: [E])
하지만 이렇게 하면 문서가 어색하고 문법에 맞지 않게 됩니다.
1 2 3 4 5 6 7 8
// Wrong Example /// Return an `Array` containing the elements of `self` /// that satisfy `includedInResult`. func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element] /// Replace the range of elements indicated by `r` with /// the contents of `with`. mutating func replaceRange(_ r: Range, with: [E])
디폴트 매개변수는 일반적인 사용을 단순화할 때 활용하세요. 일반적으로 사용되는 단일 값이 있는 모든 매개변수는 기본값을 적용할 여지가 있습니다.
DETAIL2
기본 인수는 관련 없는 정보를 숨겨 가독성을 향상시킵니다. 예를 들어
1 2 3
// Wrong Example let order = lastName.compare( royalFamilyName, options: [], range: nil, locale: nil)
위 예제가 훨씬 더 간단해질 수 있습니다.
1 2
// Right Example let order = lastName.compare(royalFamilyName)
기본 인수는 일반적으로 메서드 패밀리를 사용하는 것보다 선호되는데, 이는 API를 이해하려는 모든 사람에게 인지적 부담을 덜 주기 때문입니다.
1 2 3 4 5 6 7 8
// Right Example extension String { /// ...description... public func compare( _ other: String, options: CompareOptions = [], range: Range? = nil, locale: Locale? = nil ) -> Ordering }
위의 내용은 간단하지 않을 수 있지만 아래 예제에 비해 훨씬 간단합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Wrong Example extension String { /// ...description 1... public func compare(_ other: String) -> Ordering /// ...description 2... public func compare(_ other: String, options: CompareOptions) -> Ordering /// ...description 3... public func compare( _ other: String, options: CompareOptions, range: Range) -> Ordering /// ...description 4... public func compare( _ other: String, options: StringCompareOptions, range: Range, locale: Locale) -> Ordering }
메서드 패밀리의 모든 멤버는 개별적으로 문서화되어 사용자가 이해할 수 있어야 합니다. 사용자는 모든 메서드를 이해해야 하며, 때로는 예상치 못한 관계(예: foo(bar: nil)와 foo()가 항상 동의어가 아닌 경우)로 인해 대부분 동일한 문서에서 사소한 차이점을 찾아내는 지루한 과정을 거쳐야 합니다. 기본값이 있는 단일 메서드를 사용하면 훨씬 더 뛰어난 프로그래머 환경을 제공합니다.
- 기본값이 있는 매개변수는 매개변수 목록의 맨 끝에 위치하는 것이 좋습니다. 기본값이 없는 매개변수는 일반적으로 메서드의 의미론에 더 필수적이며, 메서드가 호출될 때 안정적인 초기 사용 패턴을 제공합니다.
- API가 프로덕션 환경에서 실행될 경우 다른 대안보다 #fileID를 선호하세요. #fileID는 공간을 절약하고 개발자의 개인정보를 보호합니다. 전체 경로가 개발 워크플로를 간소화하거나 파일 I/O에 사용되는 경우 최종 사용자가 실행하지 않는 API(예: 테스트 헬퍼 및 스크립트)에는 #filePath를 사용하세요. Swift 5.2 이하 버전과의 소스 호환성을 유지하려면 #file을 사용하세요.
Argument Labels (매개변수 레이블)
1
2
func move(from start: Point, to end: Point)
x.move(from: x, to: y)
- 인수를 유용하게 구분할 수 없는 경우 모든 레이블을 생략합니다 (예: min(number1, number2), zip(sequence1, sequence2)).
값 보존 타입 변환을 수행하는 이니셜라이저에서는 첫 번째 인자 레이블을 생략합니다(예: Int64(일부UInt32)).
DETAIL1
첫 번째 매개변수는 항상 변환의 주체여야 합니다.
1 2 3 4 5 6 7 8 9
extension String { // Convert `x` into its textual representation in the given radix init(_ x: BigInt, radix: Int = 10) ← Note the initial underscore } text = "The value is: " text += String(veryLargeNumber) text += " and in hexadecimal, it's" text += String(veryLargeNumber, radix: 16)
하지만 narrowing 타입 변환(더 작은 타입으로의 변환을 의미)에서는 를 설명하는 레이블을 사용하는 것이 좋습니다.
1 2 3 4 5 6 7 8 9
extension UInt32 { /// Creates an instance having the specified `value`. init(_ value: Int16) ← Widening, so no label /// Creates an instance having the lowest 32 bits of `source`. init(truncating source: UInt64) /// Creates an instance having the nearest representable /// approximation of `valueToApproximate`. init(saturating valueToApproximate: UInt64) }
값 보존 타입 변환은 소스 값의 모든 차이가 결과 값의 차이를 초래하는 단형성입니다. 예를 들어 Int8에서 Int64로의 변환은 모든 고유한 Int8 값이 고유한 Int64 값으로 변환되므로 값 보존형입니다. 그러나 다른 방향으로의 변환은 값 보존이 불가능합니다: Int64는 Int8로 표현할 수 있는 값보다 가능한 값이 더 많기 때문입니다.
참고: 원래 값을 검색할 수 있는 기능은 변환이 값 보존인지 여부와 관련이 없습니다.
첫 번째 인수가 전치사 구의 일부를 구성하는 경우 인자 레이블을 지정합니다. 인자 레이블은 일반적으로 전치사에서 시작해야 합니다(예: x.removeBoxes(havingLength: 12)).
DETAIL2
처음 두 인수가 단일 추상화의 일부를 나타내는 경우 예외가 발생합니다.
1 2 3
// Wrong Example a.move(toX: b, y: c) a.fade(fromRed: b, green: c, blue: d)
이러한 경우 추상화를 명확하게 하기 위해 전치사 뒤에 인수 레이블을 시작하세요.
1 2 3
// Right Example a.moveTo(x: b, y: c) a.fadeFrom(red: b, green: c, blue: d)
반면 첫 번째 인수가 문법 구의 일부를 구성하는 경우 해당 레이블을 생략하고 기본 이름에 앞의 단어를 추가합니다(예: x.addSubview(y)).
DETAIL3
이 가이드라인은 첫 번째 인수가 문법 구의 일부를 구성하지 않는 경우 레이블이 있어야 한다는 것을 의미합니다.
1 2 3 4
// Right Example view.dismiss(animated: false) let text = words.split(maxSplits: 12) let studentsByName = students.sorted(isOrderedBefore: Student.namePrecedes)
문구가 정확한 의미를 전달하는 것이 중요하다는 점에 유의하세요. 다음은 문법적으로는 맞지만 잘못된 것을 표현할 수 있습니다.
1 2
view.dismiss(false) Don't dismiss? Dismiss a Bool? words.split(12) Split the number 12?
기본값이 있는 인수는 생략할 수 있으며, 이 경우 문법 구문의 일부를 구성하지 않으므로 항상 레이블이 있어야 합니다.
- 이외의 모든 매개변수에 레이블을 지정합니다.
Special Instructions (특별 지침)
튜플 멤버에 레이블을 지정하고 API에 표시되는 클로저 매개변수에 이름을 지정합니다.
DETAIL1
이러한 이름은 설명력이 있고, 문서 주석에서 참조할 수 있으며, 튜플 멤버에 대한 표현식 액세스를 제공합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/// Ensure that we hold uniquely-referenced storage for at least /// `requestedCapacity` elements. /// /// If more storage is needed, `allocate` is called with /// `byteCount` equal to the number of maximally-aligned /// bytes to allocate. /// /// - Returns: /// - reallocated: `true` if a new block of memory /// was allocated; otherwise, `false`. /// - capacityChanged: `true` if `capacity` was updated; /// otherwise, `false`. mutating func ensureUniqueStorage( minimumCapacity requestedCapacity: Int, allocate: (_ byteCount: Int) -> UnsafePointer<Void> ) -> (reallocated: Bool, capacityChanged: Bool)
클로저 매개변수에 사용되는 이름은 최상위 함수의 매개변수 이름처럼 선택해야 합니다. 호출하는 쪽에 표시되는 클로저 인수의 레이블은 지원되지 않습니다.
제약이 없는 다형성(예: Any, AnyObject 등)을 사용할 때는 오버로드의 모호성을 피하기 위해 각별히 주의하세요.
DETAIL2
예를 들어 이 오버로드를 살펴보겠습니다.
1 2 3 4 5 6 7 8 9 10
// Wrong Example struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append(_ newElements: S) where S.Generator.Element == Element }
이러한 메서드는 의미적 계열을 형성하며 인자 유형은 처음에는 뚜렷하게 구분되는 것처럼 보입니다. 그러나 Element가 Any인 경우 단일 엘리먼트는 엘리먼트 시퀀스와 동일한 유형을 가질 수 있습니다.
1 2 3
// Wrong Example var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
모호함을 없애려면 두 번째 오버로드의 이름을 더 명확하게 지정하세요.
1 2 3 4 5 6 7 8 9
struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append(contentsOf newElements: S) where S.Generator.Element == Element }
새 이름이 문서 주석과 어떻게 더 잘 어울리는지 보세요. 이 경우, 문서화된 주석을 작성하는 행위 자체가 API 작성자의 주의를 해당 문제로 끌어들였습니다.