Swift API 가이드라인 - 2

rbw·2023년 5월 16일
0

TIL

목록 보기
81/97

Conventions

General Conventions (일반 규칙)

복잡도가 O(1)이 아닌 연산 프로퍼티는 복잡도를 기록합니다 사람들은 보통 저장 프로퍼티를 떠올리기 때문에 프로퍼티에 접근하는 것에 엄청난 연산이 필요하다고 생각하지 않습니다. 프로퍼티에 접근할 때 추가적인 비용이 발생하는 경우에는 반드시 알려야 합니다.

자유 함수보다는 메서드와 프로퍼티를 선호합니다 자유 함수는 다음과 같은 경우에만 사용됩니다.

  • 명확한 self가 없을 때: min(x,y,z)
  • 제약 없는 제네릭 함수일 때: print(x)
  • 함수 구문이 특정한 도메인 표기법의 일부일 때: sin(x)

대소문자 표기법을 따릅니다 타입과 프로토콜 이름은 UpperCamelCase, 그 외 모든 것은 lowerCamelCase로 표기합니다.

기본적인 의미가 같거나 서로 다른 도메인에서 동작할 때 메서드들은 기본이 되는 이름을 공유할 수 있습니다

extension Shape {
    /// `other`가 `self`의 영역 내에 있는 경우 `true`를 반환합니다.
    func contains(_ other: Point) -> Bool { ... }

    /// `other`가 완전히 `self`의 영역 내에 있는 경우 `true`를 반환합니다.
    func contains(_ other: Shape) -> Bool { ... }

    /// `other`가 `self`의 영역 내에 있는 경우 `true`를 반환합니다.
    func contains(_ other: LineSegment) -> Bool { ... }
}

반환 타입만 재정의 하는 방식은 타입 추론 시 모호해질 수 있으므로 피합니다

Parameters(매개변수)

문서를 쉽게 읽을 수 있도록 매개변수 이름을 선택합니다.

/// 주어진 요소의 'subRange'를 'newElements'로 변환
mutating func replaceRange(_ subRange: Range, with newElements: [E])

기본 매개변수 일반적인 사용을 단순화할 때 이점을 가집니다. 일반적으로 사용되는 단일 값을 가진 모든 매개변수는 기본값 후보입니다.

기본 인자는 관련 없는 정보를 숨김으로 가독성을 향상합니다. 예는 아래와 같습니다.

let order = lastName.compare(
royalFamilyName, options: [], range: nil, locale: nil)let order = lastName.compare(royalFamilyName)

Argument Labels(전달인자 레이블)

전달인자 레이블이 전달인자들을 유용하게 구분하지 못하는 경우 모든 전달인자 레이블을 무시합니다 min(number1, number2), zip(sequence1, sequence2)

값은 보존하고 타입만 변경하는 이니셜라이저의 경우 첫 번째 전달인자 레이블을 무시합니다. Int64(someUInt32)

첫 번째 전달인자가 전치사구의 일부라면, 전달인자 레이블을 줍니다. 이 때 이 전달인자 레이블은 반드시 전치사 위치에서 시작합니다.

예외가 발생하는 경우가 있는데 첫 번째 두 전달인자들이 단일 추상화의 일부로 표현될 때 입니다.

❌
a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)

✅
a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)

문구가 올바른 의미를 전달하고 있는지가 중요합니다. 아래 내용은 문법적으로 맞지만 잘못된 의미입니디.

❌
view.dismiss(false)  // Don't dismiss? Dismiss a Bool? (해제하지 말아라? Bool을 해제해라?)
words.split(12)       // Split the number 12?(숫자 12를 나눠라?)

또한 기본값을 가지는 전달인자는 무시될 수 있습니다. 이 경우는 문법구문의 일부가 아니기 때문에 항상 레이블을 가져야 합니다.

Special Instructions(특별 지침들)

API에 튜플과 클로저가 드러나는 곳에서, 튜플의 멤버들에 라벨을 붙여주고 클로저의 매개변수들에 이름을 지어줍시다

이러한 이름들은 설명력을 갖습니다. 이는 문서 주석들에 참조될 수 있고, 튜플의 구성요소들에 대해 유의미한 접근을 제공합니다.

/// 최소한 'requestedCapacity'만큼 유일한 참조 storage를 가지고 있다는 걸 확인합니다.
///
/// 만약 더 많은 storage가 필요하다면,
/// 할당을 위해 최대로 조정된 bytes 갯수인 'byteCount'와 함께
/// 'allocate'가 호출됩니다
///
/// - 반환값들:
///   - reallocated: 새로운 메모리 블록이 할당된 경우 `true` 
///   - capacityChanged: `capacity`가 갱신된 경우 `true`
mutating func ensureUniqueStorage(
    minimumCapacity requestedCapacity: Int, 
    allocate: (_ byteCount: Int) -> UnsafePointer<Void>
) -> (reallocated: Bool, capacityChanged: Bool)

클로저의 매개변수에서 사용된 것과 같은 이름들은, 최상위 함수들을 위해서 매개변수 이름들 처럼 사용해야 합니다. 호출부에서 표현되는 클로저의 인자들에 대한 라벨들은 대상이 아닙니다.

overload 집합들에서 애매함을 피하기 위해, 제한사항이 없는 다형성을 특별히 신경씁시다(예를 들면 Any, AnyObject, 제한사항이 없는 제네릭 매개변수들)

struct Array {
    // `self.endIndex`에 newElement를 넣는다
    public mutating func append(_ newElement: Element)

    // `self.endIndex`에 newElement의 구성요소를 순서대로 넣는다
    public mutating func append(_ newElements: S)
        where S.Generator.Element == Element
}

위 메서드들은 하나의 의미론적 집단을 형성합니다. 이어서 나타나는 첫번째 인자의 타입들은 분명하게 구별됩니다. 하지만 Element가 Any라면, 단일요소가 요소들의 시퀀스와 같은 타입을 가질 수 있습니다.

var values: [Any] = [1, "a"]
values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?

따라서 모호함을 없애기 위해 두번째 overload 보다 분명하게 이름을 지어야합니다

struct Array {
    // `self.endIndex`에 newElement를 넣는다
    public mutating func append(_ newElement: Element)

    // `self.endIndex`에 newElement의 구성요소를 순서대로 넣는다
    public mutating func append(contentsOf newElements: S)
        where S.Generator.Element == Element
}

문서 주석을 적는 행위는 실제로 API 작성자의 이목을 끌게 됩니다.

profile
hi there 👋

0개의 댓글