Swift API GuideLines?
애플은 Swift사용자들에게 일관된 룰에 따라 코드를 작성하게하여 스위프트 생태계에 더 가까이 접근할 수 있는 기회를 제공합니다.
즉, 가이드라인을 숙지하여 Swift를 사용한다면 다른 Swift유저가 내 코드를 처음 봐도 비교적 그 의미를 쉽게 파악할 수 있으며 스위프트가 추구하는 방향을 이해할 수 있습니다.
Swift를 계속 사용할 유저라면 필수적으로 익혀야할 명명법 규약에 대한 지침서라고 생각합니다.
Fundamentals
- 사용 시점에서의 명료성
코드 내에서 메소드와 프로퍼티는 한번만 선언이 되지만 반복적으로 사용됩니다. 따라서 선언을 한 뒤 API내에서 용도에 따라 사용되는 시점에서 그 의미가 모호하지 않게 코드의 의미가 잘 전달되게 작성해야 합니다.
- 명료성은 갈결성보다 중요하다
간결하게 코드를 작성할 수 있지만 스위프트에서는 단순히 코드를 짧게 쓰는게 목적이 아닙니다. 오히려 코드를 짧게 작성하여 모호한 의미를 가지는 것보다는 길더라도 의미를 파악하기 쉽게 명료하게 쓰는것이 중요합니다.
명명법(Naming)
-
모호성을 피하고자 필요한 모든 단어를 포함해라.
파라미터 ‘position’을 받는다고 명시되어있다. 이럴 경우 label값으로 at을 명시하여 remove 함수가 호출되어 사용될 때 x라는 파라미터가 어떻게 사용되는지 쉽게 이해하도록 도울 수 있다.
extension List { public mutating func remove(at position: Index) -> Element } employees.remove(at: x)
-
명확한 의도전달을 하기위하여 많은 단어가 필요할 수도 있지만 그 의미가 중첩되는 단어는 생략해라.
Element가 함수명에도 명시되어있고 파라미터타입에 반복되어 명시되어있다. 이럴 경우에는 Element의 의미가 중첩되므로 한쪽을 제거해줘야한다.
public mutating func removeElement(member: Element) -> Element? allViews.removeElement(cancelButton)
이 예시에서는 호출시 Element를 노출하지 않기 위해 함수명에서 제외해줬지만 상황과 쓰임에 따라 달라질 수 있다.
public mutating func remove(_ member: Element) -> Element? allViews.remove(cancelButton) // clearer
때때로 의미가 모호해지는것을 방지하기 위해 반복되는 값이 있을 수 있지만 파라미터의 타입보다는 그 역할을 명시해주는게 더 좋다.
-
단순히 변수, 타입, 매개변수의 정의된 형태를 명시해주는 것 보다는 영할에 따라 명명해라.
- “Hello”라는 인사를 저장하는 변수의 이름에 타입을 명시해주는것은 비효율적이다.
- 프로토콜 또한 “associatedtype”을 명명할때 타입자체를 설명하는 것은 비효율적이다.
(associatedtype:프로토콜을 정의해줄때 프로토콜에서 정의한 변수의 타입이 특정타입이 될수도 있고 아닐수도 있을경우 associatedtype으로써 정의해준다.) - 파라미터를 사용할때도 파라미터명을 파라미터의 타입자체로 명명하는 것은 비효율적이다.
var string = "Hello" protocol ViewController { associatedtype ViewType : View } class ProductionLine { func restock(from widgetFactory: WidgetFactory) }
- “greeting”과 같이 변수가 해주는 역할로써
- “ContentView”라는 역할로써 명명한다.
- “suppier”라는 역할로써 명명한다.
var greeting = "Hello" protocol ViewController { associatedtype ContentView : View } class ProductionLine { func restock(from supplier: WidgetFactory) }
- “Hello”라는 인사를 저장하는 변수의 이름에 타입을 명시해주는것은 비효율적이다.
-
사용 시점에서 파라미터의 이름만으로 명확히 역할을 파악하기 힘들다면 파라미터의 의미를 더 쉽게 파악할 수있도록 함수명과 레이블을 적절히 명명해라.
“graphics”를 위해 뭘 추가하라는 뜻인건 알겠지만 어떤것을 추가하고 graphics가 무엇을 뜻하는지 사용시점에서 의미가 모호하다
func add(observer: NSObject, for keyPath: String) grid.add(self, for: graphics) // vague
더욱 명확하게 명시해주기 위해 “addObserver”로 어떤것을 추가 하는지 알 수있도록하고 두번째 인자의 레이블에 “graphics”의 의미를 알기 쉽도록 해준다.
func addObserver(_ observer: NSObject, forKeyPath path: String) grid.addObserver(self, forKeyPath: graphics) // clear
전문용어를 적절히 사용하기(Use Terminology Well)
-
더욱 일반적인 단어가 의미를 잘 전달할 수 있다면 애매한 용어는 피해라. “피부(skin)”가 충분히 목적을 전달할 수 있다면 “표피(epidermis)를 사용하면 더 혼란스러워 질 수 있다.
-
특정 분야의 전문 용어를 명명법으로 사용할때는 그 분야의 전문가의 입장과 초보자의 입장을 충분히 고려해야한다. 전문가가 봤을때 의미가 잘못된 단어를 사용해서 불편하게 해서도 안되고 초보자가 봤을때 이해하기에 너무 어려워서도 안된다.
-
약어를 되도록 피하고 축약되지 않은 상태로 사용해라.
-
기존 유저들의 코드와 쓰임을 충분히 관찰한 후 특정 상황에 쓰이는 명명법을 따라라. 다른 명명으로도 의미를 확실히 알 수 있다하더라도 이미 많은 유저들이 따르고 있는 암묵적인 룰을 발견한다면 따르는 것이 좋다.
매개변수
-
읽을 때 더 자연스럽고 영어 문법에 맞도록 매개변수를 명명해라.
매개 변수를 잘 선언하면 아래와 같이 읽기 편하고 의미를 파악하기 쉽다.
/// `predicate`를 만족하는 `self`의 요소를 포함하는 `Array`를 반환. func filter(_ predicate: (Element) -> Bool) -> [Generator.Element] /// 주어진 요소의 `subRange`를 `newElements`로 치환. mutating func replaceRange(_ subRange: Range, with newElements: [E])
아래와 같은 명명은 읽기에도 부자연스러우며 문법에도 맞지 않는다.
/// `includedInResult`를 만족하는 `self`의 요소를 포함하는 `Array를 반환. func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element] /// `r`로 표시된 요소의 range를 `with`의 컨텐츠로 치환. mutating func replaceRange(_ r: Range, with: [E])
-
어떠한 메서드나 함수에서 옵셔널과 같이 전달하지 않아도되는 매개변수가 존재할 경우 한번에 사용해라.
이처럼 옵셔널을 사용하여 선언해주면 호출시 필요없는 파라미터는 nil이므로 지정해주지 않아도 괜찮다.
extension String { /// ...description... public func compare( other: String, options: CompareOptions = [], range: Range? = nil, locale: Locale? = nil ) -> Ordering }
아래와 같은 경우 쓸때 없이 여러번 선언해주며 읽기도 힘들다.
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 }
-
default값을 갖는 파라미터는 인자값을 받아야 하는 파라미터보다 함수나 메서드를 선언해줄 때 뒤쪽에 표기해라
default값을 갖지않는 파라미터 즉, 함수나 메서드 호출시 인자를 명시해줘야하는 의미상으로 보통 더 중요하다. 그러면 호출될때 좀 더 안정적인 형태로 사용될 수 있다.