Skip to content

Code Convention

Rey edited this page Jul 26, 2022 · 13 revisions

코드 레이아웃

들여쓰기 및 띄어쓰기

  • 들여쓰기에는 탭(tab)을 사용합니다.

  • 콜론(:)을 쓸 때에는 콜론의 오른쪽에만 공백을 둡니다.

    let names: [String: String]?

줄바꿈

  • 함수 정의가 최대 길이를 초과하는 경우에는 아래와 같이 줄바꿈합니다.

    func collectionView(
      _ collectionView: UICollectionView,
      cellForItemAt indexPath: IndexPath
    ) -> UICollectionViewCell {
      // doSomething()
    }
    
    func animationController(
      forPresented presented: UIViewController,
      presenting: UIViewController,
      source: UIViewController
    ) -> UIViewControllerAnimatedTransitioning? {
      // doSomething()
    }
  • 함수 호출 코드가 최대 길이를 초과하는 경우에는 파라미터 이름을 기준으로 줄바꿈합니다.

    let actionSheet = UIActionSheet(
      title: "정말 계정을 삭제하실 건가요?",
      delegate: self,
      cancelButtonTitle: "취소",
      destructiveButtonTitle: "삭제해주세요"
    )
  • if let 구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다.

    if let user = self.veryLongFunctionNameWhichReturnsOptionalUser(),
      let name = user.veryLongFunctionNameWhichReturnsOptionalName(),
      user.gender == .female {
      // ...
    }
  • guard let 구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다. else는 guard와 같은 들여쓰기를 적용합니다.

    guard let user = self.veryLongFunctionNameWhichReturnsOptionalUser(),
      let name = user.veryLongFunctionNameWhichReturnsOptionalName(),
      user.gender == .female
    else {
      return
    }

최대 줄 길이

  • 한 줄은 최대 120자를 넘지 않아야 합니다.

    • 단, if let, guard let 구문은 최대 70자로 제한

    Xcode의 Preferences → Text Editing → Display의 'Page guide at column' 옵션을 활성화하고 120자로 설정하면 편리합니다.

Import

모듈 임포트는 알파벳 순으로 정렬합니다.

내장 프레임워크를 먼저 임포트하고, 빈 줄로 구분하여 서드파티 프레임워크를 임포트합니다.

네이밍

클래스

  • 클래스 이름에는 UpperCamelCase를 사용합니다.
  • 클래스 이름에는 접두사를 붙이지 않습니다.

함수

  • 함수 이름에는 lowerCamelCase를 사용합니다.

  • 함수 이름 앞에는 되도록이면 get을 붙이지 않습니다.

    • 좋은 예:

      func name(for user: User) -> String?
    • 나쁜 예:

      func getName(for user: User) -> String?
  • Action 함수의 네이밍은 '주어 + 동사 + 목적어' 형태를 사용합니다.

    • Tap(눌렀다 뗌)*은 UIControlEvents의 .touchUpInside에 대응하고, *Press(누름)*는 .touchDown에 대응합니다.
    • will~은 특정 행위가 일어나기 직전이고, did~는 특정 행위가 일어난 직후입니다.
    • should~는 일반적으로 Bool을 반환하는 함수에 사용됩니다.

    좋은 예:

    func backButtonDidTap() {
      // ...
    }

    나쁜 예:

    func back() {
      // ...
    }
    
    func pressBack() {
      // ...
    }

변수

  • 변수 이름에는 lowerCamelCase를 사용합니다.

상수

  • 상수 이름에는 lowerCamelCase를 사용합니다.

    좋은 예:

    let maximumNumberOfLines = 3

    나쁜 예:

    let MaximumNumberOfLines = 3
    let MAX_LINES = 3

열거형

  • enum의 각 case에는 lowerCamelCase를 사용합니다.

    좋은 예:

    enum Result {
      case .success
      case .failure
    }

    나쁜 예:

    enum Result {
      case .Success
      case .Failure
    }

약어

  • 약어로 시작하는 경우 소문자로 표기하고, 그 외의 경우에는 항상 대문자로 표기합니다.

    좋은 예:

      let userID: Int?
      let html: String?
      let websiteURL: URL?
      let urlString: String?

    나쁜 예:

      let userId: Int?
      let HTML: String?
      let websiteUrl: NSURL?
      let URLString: String?

개념적 네이밍

  • View, ViewModel에 해당하는 개념은 끝에 View, ViewModel을 붙인다.

    ex)
    class SoundViewModel
    class SettingView
  • Model에 해당하는 개념은 Model을 붙이지 않고, 개념 그대로를 이름으로 사용한다.

    ex)
    struct Sound
    struct PlayList
  • Singleton 객체는 Manager, Controller, Service 중 하나를 붙인다.

    ex)
    enum NetworkManager
    struct AppController
    struct BackgroundSoundController
    class NotificationService

클로저

  • 파라미터와 리턴 타입이 없는 Closure 정의시에는 () -> Void를 사용합니다.

    let completionBlock: (() -> Void)?
  • Closure 정의시 개행하여 코드 블럭을 작성합니다.

    좋은 예

    intArray.map {
        $0 * 2
    }

    나쁜예

    intArray.map { $0 * 2 }
  • Closure 인자 작성 시, 가능한 경우 타입 정의를 생략합니다.

    intArray.map {
      $0 * 2
    }
    • Closure 인자 작성 시, 코드블럭이 2줄 이상인 경우 반드시 파라미터 라벨을 사용합니다.

      intArray.map { intValue in
          let isOdd = (intValue % 2 == 1)
          return isOdd
      }
  • Closure 정의시 파라미터에는 괄호를 사용하지 않습니다.

    { operation, responseObject in
      // doSomething()
    }
  • Closure 호출시 또 다른 유일한 Closure를 마지막 파라미터로 받는 경우(후행 클로저), 파라미터 이름을 생략합니다. 좋은 예:

    UIView.animate(withDuration: 0.5) {
      // doSomething()
    }

    단, 파라미터에 후행 클로저가 2개 이상 존재하는 경우에는 아래 두 가지 조건을 준수합니다.

    • 모든 클로저는 축약 표현을 사용합니다.
    • 두번째 클로저부터는 앞에 있는 클로저의 끝} 에서 줄바꿈을 하지 않고 라벨을 표시합니다.
    // UIView.animate(withDuration: TimeInterval, animations: () -> Void, completion: ((Bool) -> Void)?)
    UIView.animate(withDuration: 0.25) {
        // doSomething() about animations
      } completion: { finished in
        // doSomething() 
      }
    
    // SwiftUI Button
    Button {
    
    } label: {
    
    }

클래스와 구조체

  • 클래스와 구조체 내부에서는 self를 명시적으로 사용합니다.
  • 내장 라이브러리가 존재한다면, 불필요한 초기화 함수를 재구성 하지 않습니다.
let frame = CGRect(x: 0, y: 0, width: 100, height: 100)

타입

  • Array<T>와 Dictionary<T: U> 보다는 [T][T: U]를 사용합니다.

    // 좋은 예:
    var messages: [String]?
    var names: [Int: String]?
    
    // 나쁜 예:
    var messages: Array<String>?
    var names: Dictionary<Int, String>?
  • Int, Float, String, Bool 타입은 변수/상수 선언 시, 명시하지 않습니다.

    var isShowAnyView = false
    var referenceCount = 0

주석

  • 한줄 주석

    한줄 주석에는//만을 사용합니다.

  • 여러줄 주석

    두 줄 이상의 주석을 작성할때는 /* */ 를 사용합니다.

  • 주석의 들여쓰기는 주석과 관련된 코드와 일치 시킵니다.

    단, 여러줄 주석의 경우 주석 기호를 기준으로 2개의 space를 사용합니다.

    // 한줄 주석
    
    /*
    	주석이
    	두개지요.
    */
  • ///를 사용해서 문서화에 사용되는 주석을 남깁니다.

    • 문서화에 사용되는 주석이란, option key를 누른 채로 해당 함수 또는 프로퍼티를 클릭했을 때 볼 수 있는 Summary의 내용입니다.
    /// 사용자 프로필을 그려주는 뷰
    class ProfileView: UIView {
    
      /// 사용자 닉네임을 그려주는 라벨
      var nameLabel: UILabel!
    }

MARK

  • // MARK:를 사용해서 연관된 코드를 구분짓습니다.

  • Objective-C에서 제공하는 #pragma mark와 같은 기능으로, 연관된 코드와 그렇지 않은 코드를 구분할 때 사용합니다.

  • 구문 위와 아래에는 공백이 필요합니다.

    // MARK: Init
    
    override init(frame: CGRect) {
      // doSomething()
    }
    
    deinit {
      // doSomething()
    }
    
    // MARK: Layout
    
    override func layoutSubviews() {
      // doSomething()
    }
    
    // MARK: Actions
    
    override func menuButtonDidTap() {
      // doSomething()
    }
  • Mark Label

    • SwiftUI View
    // MARK: State ProPerties
    -> property Wrapper가 있는 let, var
    -> 순서: AppStorage, Environment, EnvironmentObject, State, Binding, ObservedObject, StateObject
    -> 순서에 명시되지 않은 Wrapper는 가장 마지막에 오름차순으로 작성
    
    // MARK: General ProPerties
    -> property Wrapper가 없는 let, var
    
    // MARK: Methods
    
    // MARK: ViewBuilder
    -> @ViewBuilder가 wrapping된 모든 것
    ->, ProPerties, Methods 순서로 작성
    
    // MARK: Life Cycles
    -> init, body
    • OberverableObject Class
    // MARK: State ProPerties
    // SwiftUI View와 동일
    
    // MARK: Published ProPerties
    -> @Published let, var
    
    // MARK: General ProPerties
    
    // MARK: Methods
    
    // MARK: Life Cycles
    • UIViewController
    // UIViewController
    // MARK: - ProPerties
    
    // MARK: - Methods
    
    // MARK: - IBOutlets
    
    // MARK: - IBActions
    
    // MARK: - Delegates And DataSources
    
    // MARK: - Life Cycles

프로그래밍 권장사항

  • 상수를 정의할 때에는 enum를 만들어 비슷한 상수끼리 모아둡니다. 재사용성과 유지보수 측면에서 큰 향상을 가져옵니다. struct 대신 enum을 사용하는 이유는, 생성자가 제공되지 않는 자료형을 사용하기 위해서입니다.

  • 더이상 상속이 발생하지 않는 클래스는 항상 final 키워드로 선언합니다.

  • 프로토콜을 적용할 때에는 extension을 만들어서 관련된 메서드를 모아둡니다.

    좋은 예

    final class MyViewController: UIViewController {
      // ...
    }
    
    // MARK: UITableViewDataSource
    
    extension MyViewController: UITableViewDataSource {
      // ...
    }
    
    // MARK: UITableViewDelegate
    
    extension MyViewController: UITableViewDelegate {
      // ...
    }

    나쁜 예

    final class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
      // ...
    }

Reference

[GitHub - StyleShare/swift-style-guide: StyleShare에서 작성한 Swift 한국어 스타일 가이드]

[GitHub - DeveloperAcademy-POSTECH/swift-style-guide: 아카데미에서 사용 하는 코딩 스타일 가이드 입니다.]

Clone this wiki locally