Skip to content

Latest commit

 

History

History

Swift

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

크로키닷컴 Swift 스타일 가이드

Swift API Design Guidelines를 참고합니다.

Lint

SwiftLint를 사용해 Lint를 합니다. 설정은 .swiftlint.yml를 참고합니다.

1. 코드 레이아웃

  • 들여쓰기에는 4개의 space를 사용합니다.

  • 줄바꿈 (추천 사항)

    • 줄바꿈의 경우 아래의 규칙을 꼭 지키지 않아도 괜찮습니다.

    • 배열 혹은 딕셔너리의 경우 아래와 같이 줄바꿈합니다.

      let array: [String] = [
      	  "hello",
      	  "world"
      ]
      let dict: [String: String] = [
      	  "dictKey1": "value1",
      	  "dictKey2": "value2"
      ]
    • 함수를 호출하는 코드에서 파라미터가 두 개 이상인 경우에는 파라미터를 기준으로 줄바꿈합니다.

      someFunctionWithManyArguments(firstArgument: "Hello, I am a string",
                                    secondArgument: resultFromSomeFunction(),
                                    thirdArgument: someOtherLocalProperty)
    • if-let 구문, guard-let 구문은 아래와 같이 줄바꿈하고 한 칸 들여씁니다.

      if let user = self.veryLongFunctionNameWhichReturnsOptionalUser(),
      	let name = user.veryLongFunctionNameWhichReturnsOptionalName(),
      	user.gender == .female {
        // ...
      }
    • 종료 할 때의 guard 구문은 아래와 같이 줄바꿈합니다.

      guard let error = error else { return }
  • 콜론(:)은 오른쪽에만 공백을 둡니다. (예외: 빈 딕셔너리[:] )

    // 좋은 예
    var dict1: [String: String] = [:]
    var dict2: [String: String] = [
    	  "A": "1",
    	  "B": "2"
    ]
    class PirateViewController: UIViewController { /* ... */ }
    
    // 나쁜 예
    var dict1: [String : String] = [: ]
    var dict2 :[String:String] = [
    	  "A" : "1",
    	  "B" :"2"
    ]
    class PirateViewController : UIViewController { /* ... */ }
  • 모듈 임포트는 알파벳 순으로 정렬합니다. 내장 프레임워크를 먼저 임포트하고, 빈 줄로 구분하여 서드파티 프레임워크를 임포트합니다.

    import UIKit
    
    import FLAnimatedImage
    import RxSwift
    import ObjectMapper

2. 네이밍

  • 파일 명은 PascalCase 를 사용합니다.

  • 폴더 명은 snake_case 를 사용하고, 단수형을 사용합니다.

  • 타입은 PascalCase 를 사용합니다. (e.g. struct, enum, class, typedef)

  • 변수, 상수, 함수, enum의 각 case의 이름은 camelCase 를 사용합니다.

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

      // 좋은 예
      var imageURL: String = ""
      var shopID: String = ""
      var id: String = ""
      
      // 나쁜 예
      var imageUrl: String = ""
      var shopId: String = ""
      let ID: String = ""
    • 변수의 이름을 줄여쓰는 것은 지양합니다.

      // 좋은 예
      let position = gestureRecognizer.location(in: goodsCollectionView)
      
      // 나쁜 예
      let p = gestureRecognizer.location(in: mGoodsCollectionView)
  • 의미가 불분명한 이름을 사용하는 것은 지양합니다.

    // 좋은 예
    class RoundAnimatingButton: UIButton { /* ... */ }
    
    // 나쁜 예
    class CustomButton: UIButton { /* ... */ }
  • Outlet 변수들은 이름 마지막에 타입 명을 명시해줍니다.

    // 좋은 예
    @IBOutlet weak var submitButton: UIButton!
    @IBOutlet weak var emailTextField: UITextField!
    
    // 나쁜 예
    @IBOutlet weak var submit: UIButton!
    @IBOutlet weak var emailTF: UITextField!
  • UICollectionViewCell, UITableViewCell의 클래스 이름에서 TableView, CollectionView는 생략합니다.

    // 나쁜 예
    class ConnectionTableViewCell: UITableViewCell { /* ... */ }
    
    // 좋은 예
    class ConnectionCell: UITableViewCell { /* ... */ }
  • 프로토콜

    • 가능성(capability)를 설명하는 프로토콜은 able 로 끝나야합니다.

    • 속성을 가지는 프로토콜은 명사 + Type 을 사용합니다.

      // here, the protocol is a capability, and we name it appropriately
      protocol Loggable {
          func logCurrentState()
          /* ... */
      }
      
      protocol ModelType {
          var id: Int { get }
      }
    • Extension 은 특정 기능에만 활용되는 경우에만 뒤에 +를 붙입니다. (e.g. UINavigationBar+Border)

3. 코딩 스타일

General

  • 클래스와 구조체 내부에서 self. 는 필수가 아닌 이상 붙이지 않습니다.

  • 클로저 내부에서 self 는 아래와 같이 사용합니다.

    // 좋은 예
    myFunctionWithEscapingClosure() { [weak self] error -> Void in
        guard let self = self else { return }
        // 한줄 띄기
        self.doSomething()
    }
    
    // 나쁜 예
    myFunctionWithEscapingClosure() { [weak self] error -> Void in
        self?.doSomething()
    }
  • 열거형은 가능한 생략합니다.

    // 좋은 예
    imageView.backgroundColor = .white
    
    // 나쁜 예
    imageView.backgroundColor = UIColor.white
  • 함수의 전달 인자가 Void 형태를 가지는 경우에는 Void() 를 넘깁니다.

    // 좋은 예
    Observable<Void>.just(Void())
    
    // 나쁜 예
    Observable<Void>.just(())

프로토콜

  • 프로토콜은 별도의 Extension 으로 적용하는 것을 지향합니다.

    // 좋은 예
    class MyViewController: UIViewController {
      // class stuff here
    }
    
    // MARK: - UITableViewDataSource
    extension MyViewController: UITableViewDataSource {
      // table view data source methods
    }
    
    // MARK: - UIScrollViewDelegate
    extension MyViewController: UIScrollViewDelegate {
      // scroll view delegate methods
    }
    
    // 나쁜 예
    class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
      // all methods
    }

클로저

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

  • 클로저 정의시 파라미터에는 괄호를 사용하지 않습니다.

    // 좋은 예
    { operaion, responseObject in
      // doSomething()
    }
    
    // 나쁜 예
    { (operaion, responseObject) in
      // doSomething()
    }
  • 클로저 정의시 타입 정의는 생략합니다.

// 좋은 예
...,
completion: { finished in
  // doSomething()
}

// 나쁜 예
...,
completion: { (finished: Bool) -> Void in
  // doSomething()
}
  • 클로저 호출시 또 다른 유일한 클로저를 마지막 파라미터로 받는 경우, 파라미터 이름을 생략합니다.

    // 좋은 예
    UIView.animate(withDuration: 0.5) {
      // doSomething()
    }
    
    // 나쁜 예
    UIView.animate(withDuration: 0.5, animations: { () -> Void in
      // doSomething()
    })

타입

  • 타입은 되도록이면 명시합니다.

    // 좋은 예
    var str: String = ""
    var messages: [String] = []
    
    // 나쁜 예
    var str = ""
    var messages = []
  • Array<T>Dictionary<T: U> 보다는 [T], [T: U]를 사용합니다.

    // 좋은 예
    var messages: [String]?
    var names: [Int: String]?
    
    // 나쁜 예
    var messages: Array<String>?
    var names: Dictionary<Int, String>?
  • 빈 배열과 딕셔너리의 경우, 타입 주석(annotation)을 사용합니다.

    // 좋은 예
    var names: [String] = []
    var lookup: [String: Int] = [:]
    
    // 나쁜 예
    var names = [String]()
    var lookup = [String: Int]()

옵셔널

  • viewDidLoad에서 초기화되는 인스턴스 변수들은 ! 으로 암시적으로 언래핑된 타입을 사용합니다.

    // 괜찮은 예
    let view = Bundle(for: BrowserCouponCodeBar.self).loadNibNamed(BrowserCouponCodeBar.xibName, owner: self, options: nil)!.first as! UIView
  • 옵셔널 체크는 if-let 구문을 사용합니다.

    // 좋은 예
    if let _ = error {
        fatalError()
    }
    
    // 나쁜 예
    if error != nil {
        fatalError()
    }
  • 선언된 변수가 IUO 타입이면 명시적으로 !를 사용하고, Optional 타입이면 let을 사용해 언래핑하여 사용합니다.

    // 좋은 예
    @IBOutlet weak var goodsView: UIView!
    ..
    NSLayoutConstraint(item: goodsView!,...)
    
    // 나쁜 예
    @IBOutlet weak var goodsView: UIView!
    ..
    NSLayoutConstraint(item: goodsView as Any,...)

4. 주석

  • 주석 사용의 제한은 없으나 불명확한 의도를 가진 주석의 사용은 지양합니다.