스위프트 API Design Guideline을 읽고 정리한 내용입니다.
잘못된 내용이나, 개선사항이 있는 경우 피드백 부탁드립니다!
Fundamentals
사용시점의 명확성
은 매우 중요하다. 내가 디자인한 API가 다른 사람이 사용할 떄도 쉽게 사용할 수 있도록 디자인 해야 한다. 이게 가장 큰 목표이다.
명확성은 간결함보다 중요하다.
짧은 코드로 이해하기 어렵게 만드는 것보다는 코드가 조금 길더라도 사용하기 쉬운 API를 디자인 해야한다.
주석
을 잘 작성해야한다. 주석을 작성하는데에 어려움이 있다면 잘못 디자인한 API일 수도 있다.
- 항상 요약과 함께 시작해야한다. 이것만으로 사용자가 이해할 수 있기때문이다.
func reversed() -> ReverseCollection
- 단일 문장을 사용해야하며, 완전한 문장꼴은 피하자. 또한 마침표로 끝나는게 올바르다.
- 함수나 메소드가 무엇을 return하는지 서술하고, 아무 효과 없거나 Void 반환형의 경우 생략한다.
mutating func prepend(_ newHead: Int)
func prepending(_ head: Element) -> List
mutating func popFirst() -> Element?
- subscript가 무엇에 접근하는지 서술하여야 한다.
subscript(index: Int) -> Element { get set }
- 이니셜라이저가 무엇을 생성하는지 서술하어야 한다.
init(count n: Int, repeatedElement x: Element)
- 또한, 다른 모든 선언에 대해 선언된 개체가 무엇인지 서술하여야 한다.
struct List {
var first: Element?
...
- 필요하다면 빈칸으로 구분된 완전한 꼴의 여러 문단으로 서술할 수 있다.
public func print(
_ items: Any..., separator: String = " ", terminator: String = "\n")
Namings
- 모호성을 피하기위해 많은 단어를 포함시켜도 된다.
extension List {
public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)
employees.remove(x)
- 동일한 의미를 전달하거나, 반복적인 의미, 혹은 사용자가 이미 알고 있는 개념을 포함하는 단어는 생략하는 게 좋다.
public mutating func remove(_ member: Element) -> Element?
allViews.remove(cancelButton)
public mutating func removeElement(_ member: Element) -> Element?
allViews.removeElement(cancelButton)
- 변수명, 매개변수명, associatedtype은 역할에 따라 이름을 짓는 것이 바람직하다.
var greeting = "Hello"
protocol ViewController {
associatedtype ContentView : View
}
class ProductionLine {
func restock(from supplier: WidgetFactory)
}
var string = "Hello"
protocol ViewController {
associatedtype ViewType : View
}
class ProductionLine {
func restock(from widgetFactory: WidgetFactory)
}
- Any, Int, NSObject와 같은 모호한 타입을 사용할때는 분명하게 이름을 지어야한다.
func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics)
func add(_ observer: NSObject, for keyPath: String)
grid.add(self, for: graphics)
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”
x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()
- 하지만, 생성자나 팩토리메소드의 경우 영어 문법을 준수하지 않아야한다.
let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)
let ref = Link(target: destination)
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
let ref = Link(to: destination)
- 부작용이 없는 함수나 메소드는 명사형으로 읽히는 것이 좋다.
x.distance(to: y)
x.successor().
- 부작용이 있는 함수나 메소드는 동사형으로 읽히는 것이 좋다.
x.sort()
x.append(y)
- Mutating/nonmutating 메소드의 명을 일관되게 작성하는 것이 좋다.
Mutating | Nonmutating |
---|
x.sort() | z = x.sorted() |
x.append() | z = x.appending(y) |
- nonmutating 메소드의 경우 동사의 과거형으로 작성한다.
mutating func reverse()
func reversed() -> Self
...
x.reverse()
let y = x.reversed()
- 과거형으로 작성할 수 없다면,
-ing
를 붙인다.
mutating func stripNewlines()
func strippingNewlines() -> String
...
s.stripNewlines()
let oneLine = t.strippingNewlines()
- 만약 수행하는 연산의 자체가 명사형이라면,
form
접미사를 붙인다.
Nonmutating | Mutating |
---|
z = x.union() | x.formUnion(y) |
j = c.successor(i) | c.formSuccessor(y) |
Conventions
매개변수
func move(from start: Point, to end: Point)
- 매개변수 이름은 쉽게 읽을 수 있도록 만들어야 한다.
func filter(_ predicate: (Element) -> Bool) -> [Generator.Element]
mutating func replaceRange(_ subRange: Range, with newElements: [E])
func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element]
mutating func replaceRange(_ r: Range, with: [E])
- 기본 인수를 사용해서 사용자에게 부담을 덜 수 있다.
extension String {
public func compare(
_ other: String, options: CompareOptions = [],
range: Range? = nil, locale: Locale? = nil
) -> Ordering
}
extension String {
public func compare(_ other: String) -> Ordering
public func compare(_ other: String, options: CompareOptions) -> Ordering
public func compare(
_ other: String, options: CompareOptions, range: Range) -> Ordering
public func compare(
_ other: String, options: StringCompareOptions,
range: Range, locale: Locale) -> Ordering
}
인수 레이블
- 인수를 구분할 필요가 없는 경우에는 생략한다. (ex. min(a,b))
- 인수 레이블 명명은 첫번째 인수가 매우 중요하다!
- 인수 레이블은 일반적으로
전치사
로 시작해야 한다.
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.dissmiss(animated: false)
let text = words.split(maxSplits: 12)
let studentByName = students.sorted(isOrderedBefore: Student.namePrecedes)
view.dismiss(false)
words.split(12)