Skip to content

Code Convention

Hyungyu Kim edited this page Apr 18, 2023 · 3 revisions

StyleShare - swift style guide 을 기본으로 따라간다.

StyleShare 의 Swift Style Guide 를 기본으로 해서 추가하거나 수정하였습니다. SwiftLint 를 통해서 통일성있는 클린코드를 추구합니다.

➡️ 네이밍

📌 클래스

  • UpperCamelCase 사용

📌 함수

  • lowerCamelCase 사용하고 동사로 시작

@IBAction

- 동사원형 + 목적어 ex) touchBackButton

뷰 전환

- pop, push, present, dismiss
- 동사 + To + 목적지 뷰 (다음에 보일 뷰)
- dismiss는 dismiss + 현재 뷰

register

- register + 목적어 ex) registerXib

서버통신

- 서비스함수명 + WithAPI ex) getListWithAPI

애니메이션

- 동사원형 + 목적어 + WithAnimation ex)showButtonsWithAnimation

델리게이트

delegate 메서드는 프로토콜명으로 네임스페이스를 구분

좋은 예:

protocol UserCellDelegate {
  func userCellDidSetProfileImage(_ cell: UserCell)
  func userCell(_ cell: UserCell, didTapFollowButtonWith user: User)
}

나쁜 예:

protocol UserCellDelegate {
  func didSetProfileImage()
  func followPressed(user: User)

  // `UserCell`이라는 클래스가 존재할 경우 컴파일 에러 발생
  func UserCell(_ cell: UserCell, didTapFollowButtonWith user: User)
}

📌 변수, 상수

  • lowerCamelCase 사용

📌 열거형

  • 각 case 에는 lowerCamelCase 사용

📌 약어

약어로 시작하는 경우 소문자로 표기, 그 외에는 항상 대문자

// 좋은 예:
  let userID: Int?
  let html: String?
  let websiteURL: URL?
  let urlString: String?

// 나쁜 예:
  let userId: Int?
  let HTML: String?
  let websiteUrl: NSURL?
  let URLString: String?

📌 기타 네이밍

setUI() : @IBOutlet 속성 설정
initCell() : 셀 데이터 초기화
initList() : 배열 항목 세팅. 컬렉션뷰 에서 리스트 초기 세팅할때
setLayout() : 멀라 레이아웃
setCollectionView() : 컬렉션뷰 관련 세팅
setDelegate() : delegate, datasource 모음
setAddTarget() : addtarget 모음

➡️ 코드 레이아웃

들여쓰기 및 띄어쓰기

  • 들여쓰기에는 탭(tab) 대신 2개의 space를 사용합니다.

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

    let names: [String: String]?

  • 연산자 오버로딩 함수 정의에서는 연산자와 괄호 사이에 한 칸 띄어씁니다.

    func ** (lhs: Int, rhs: Int)

📌 줄바꿈

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

    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: "삭제해주세요"
    )

    단, 파라미터에 클로저가 2개 이상 존재하는 경우에는 무조건 내려쓰기합니다.

    UIView.animate(
      withDuration: 0.25,
      animations: {
        // doSomething()
      },
      completion: { finished in
        // doSomething()
      }
    )
  • 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
    }

📌 빈 줄

  • 모든 파일은 빈 줄로 끝나도록 합니다.

  • MARK 구문 위에는 공백이 필요합니다.

    // MARK: Layout    
    override func layoutSubviews() {
      // doSomething()
    }
    
    // MARK: Actions
    override func menuButtonDidTap() {
      // doSomething()
    }

📌 임포트

모듈 임포트는 알파벳 순으로 정렬합니다. 내장 프레임워크를 먼저 임포트하고, 빈 줄로 구분하여 서드파티 프레임워크를 임포트합니다.

import UIKit

import SwiftyColor
import SwiftyImage
import Then
import URLNavigator

➡️ 클로저

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

    좋은 예:

    let completionBlock: (() -> Void)?
    

    나쁜 예:

    let completionBlock: (() -> ())? let completionBlock: ((Void) -> (Void))?

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

    좋은 예:

    { operation, responseObject in
      // doSomething()
    }

    나쁜 예:

    { (operation, responseObject) in
      // doSomething()
    }
  • Closure 정의시 가능한 경우 타입 정의를 생략합니다.

    좋은 예:

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

    나쁜 예:

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

    좋은 예:

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

    나쁜 예:

    UIView.animate(withDuration: 0.5, animations: { () -> Void in
      // doSomething()
    })

➡️ 주석

코드는 가능하면 자체적으로 문서가 되어야 하므로, 코드와 함께 있는 인라인(inline) 주석은 피한다.

📌 MARK 주석

// MARK: - Properties

// MARK: - @IBOutlet Properties

// MARK: - @IBAction Properties

// MARK: - View Life Cycle

// MARK: - Methods
> setUI, registerXib, assignDelegate 등

// MARK: - @objc Function

// MARK: - Network
> 네트워크 목적을 가진 함수들

// TODO: -
> 해야하는 것

// FIXME: -
> 고칠 예정

📌 퀵헬프 주석

커스텀 베서드, 프로토콜, 클래스의 경우에 퀵헬프 주석 달기

/// (서머리 부분)
/// (디스크립션 부분)
class MyClass {
    let myProperty: Int

    init(myProperty: Int) {
        self.myProperty = myProperty
    }
}

/**summary
- note: 설명

- parameters:
    - property: 프로퍼티

- throws: 오류가 발생하면 customError의 한 케이스를 throw
- returns: "\\(name)는 ~" String
*/func printProperty(property: Int) {
        print(property)
    }
  • 참고 :

[iOS - swift] 주석 처리, 퀵 헬프 주석, //, ///

[Swift] Xcode 퀵헬프 및 마크업 문법

➡️ 프로그래밍 권장사항

Type Annotation 사용

좋은 예:

let name: String = "철수"
let height: Float = "10.0"

나쁜 예:

let name = "철수"
let height = "10.0"

UICollectionViewDelegate, UICollectionViewDatsource 등 시스템 프로토콜

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

좋은 예:

final class MyViewController: UIViewController {
  // ...
}

// MARK: - UITableViewDataSource

extension MyViewController: UITableViewDataSource {
  // ...
}

// MARK: - UITableViewDelegate

extension MyViewController: UITableViewDelegate {
  // ...
}

나쁜 예:

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

➡️ 기타규칙

  • self 는 최대한 사용을 지양
  • viewDidLoad() 에서는 함수호출만
  • delegate 지정, UI관련 설정 등등 모두 함수와 역할에 따라서 extension 으로 빼기
  • 필요없는 주석들 제거