Skip to content

๐Ÿš€ HMH Swift Style Guide

Zoe edited this page Jan 4, 2024 · 4 revisions

๐Ÿ”ฅ ์ฝ”๋“œ๋Š” ํ•œ๋ช…์ด ์ง  ๊ฒƒ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ž…๋‹ˆ๋‹ค. SwiftLint๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋Œ€์‹ , ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์ •์„ฑ์Šค๋Ÿฝ๊ฒŒ ํ•ด์ฃผ์„ธ์š”.

1๏ธโƒฃ Naming

1-1. Class

UpperCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ViewController, TableViewCell, CollectionViewCell ์ค„์ด์ง€ ์•Š๊ณ  ์‚ฌ์šฉ

1-2. ๋ณ€์ˆ˜ ๋ฐ ์ƒ์ˆ˜

  • ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๋„ค์ด๋ฐ์„ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”!

  • lowerCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ex. nameList

1-3. ํ•จ์ˆ˜

  • ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๋„ค์ด๋ฐ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํ•จ์ˆ˜ ์ด๋ฆ„์—๋Š” lowerCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • Action ํ•จ์ˆ˜์˜ ๋„ค์ด๋ฐ์€ '์ฃผ์–ด + ๋™์‚ฌ + ๋ชฉ์ ์–ด' ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    • Tap(๋ˆŒ๋ €๋‹ค ๋—Œ)*์€ย UIControlEvents์˜ย .touchUpInside์— ๋Œ€์‘ํ•˜๊ณ , *Press(๋ˆ„๋ฆ„)*๋Š”ย .touchDown์— ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค.
    • will~์€ ํŠน์ • ํ–‰์œ„๊ฐ€ ์ผ์–ด๋‚˜๊ธฐ ์ง์ „์ด๊ณ ,ย did~๋Š” ํŠน์ • ํ–‰์œ„๊ฐ€ ์ผ์–ด๋‚œ ์งํ›„์ž…๋‹ˆ๋‹ค.
    • should~๋Š” ์ผ๋ฐ˜์ ์œผ๋กœย Bool์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • API ๊ด€๋ จ ํ•จ์ˆ˜๋Š” get, post, put, delete๋“ฑ์˜ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ข‹์€ ์˜ˆ:

    func backButtonDidTap() {
      // ...
    }
  • ๋‚˜์œ ์˜ˆ:

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

1-4. ์—ด๊ฑฐํ˜•

  • enum์˜ ์ด๋ฆ„์—๋Š” UpperCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • enum์˜ ๊ฐ case์—๋Š” lowerCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    ์ข‹์€ ์˜ˆ:

    enum Result {
      case .success
      case .failure
    }
    

    ๋‚˜์œ ์˜ˆ:

    enum Result {
      case .Success
      case .Failure
    }
    
    enum result {
      case .Success
      case .Failure
    }
    

1-5. ํ”„๋กœํ† ์ฝœ

  • ํ”„๋กœํ† ์ฝœ์˜ ์ด๋ฆ„์—๋Š” UpperCamelCase๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Delegate์ผ ๊ฒฝ์šฐ Delegate / ์ด์™ธ์—๋Š” Protocol ์ ‘๋ฏธ์‚ฌ๋ฅผ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”
Protocol PickerDelegate { .. }

1-6. ๊ธฐํƒ€ ๋„ค์ด๋ฐ

  • setUI : UI ์ž‘์„ฑ๊ณผ ๊ด€๋ จ๋œ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • setHierarchy : ๋ทฐ ๊ณ„์ธต ๊ด€๊ณ„๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    • setConstraints : ๋ ˆ์ด์•„์›ƒ ์„ค์ • ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • configure~ : ์ถ”ํ›„์— ์Šคํƒ€์ผ์„ ์ •ํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • addTarget : ๋ฒ„ํŠผ์— Event๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • setDelegate : delegate๋ฅผ ์„ค์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ ์ž‘์„ฑ ์ˆœ์„œ

๐Ÿ“Ž ์ƒ๋ช… ์ฃผ๊ธฐ ๊ด€๋ จ ํ•จ์ˆ˜ โ†’ ์ปค์Šคํ…€ ํ•จ์ˆ˜ (๋ ˆ์ด์•„์›ƒ ์šฐ์„ ) โ†’ objc ํ•จ์ˆ˜
  • View ํ…œํ”Œ๋ฆฟ

    import UIKit
    
    import SnapKit
    import Then
    
    final class MainView: UIView {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setUI()
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func setUI() {
            setHierarchy()
            setConstraints()
        }
    
        func setHierarchy() {
            self.addSubview(scrollView)
        }
    
        func setConstraints() {
            view.snp.makeConstraints {
                $0.edges.equalToSuperview()
            }
        }
    		
    		func configureImageView() {
    				view.backgroundColor = .white
    		 }
    		
    		func addTarget() {
    				button.addTarget(self, action: #selector(backArrowButtonTapped), for: .touchUpInside)
    		}
    }

2๏ธโƒฃ ์ฝ”๋“œ ๋ ˆ์ด์•„์›ƒ

2-1. ย Protocol ์‚ฌ์šฉ ์‹œ

extension์œผ๋กœ ๋ชจ๋‘ ๋ถ„๋ฆฌํ•ด์„œ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”

  • ํ•˜๋‚˜์˜ extension์— ํ”„๋กœํ† ์ฝœ ํ•˜๋‚˜์”ฉ๋งŒ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

2-2. ์ž„ํฌํŠธ

๋ชจ๋“ˆ ์ž„ํฌํŠธ๋Š” ์•ŒํŒŒ๋ฒณ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค. ๋‚ด์žฅ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋จผ์ € ์ž„ํฌํŠธํ•˜๊ณ , ๋นˆ ์ค„๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ์„œ๋“œํŒŒํ‹ฐ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.

import UIKit

import SwiftyColor
import SwiftyImage
import Then
import URLNavigator

2-3. ์ƒ๋ช…์ฃผ๊ธฐ(Life Cycle)

์ƒ๋ช…์ฃผ๊ธฐ ์ˆœ์„œ๋Œ€๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”

class MovieRatingViewController: UITableViewController {

  // MARK: - View controller lifecycle methods

  override func viewDidLoad() {
    // ...
  }

  override func viewWillAppear(_ animated: Bool) {
    // ...
  }

  // MARK: - Movie rating manipulation methods

  @objc private func ratingStarWasTapped(_ sender: UIButton?) {
    // ...
  }

  @objc private func criticReviewWasTapped(_ sender: UIButton?) {
    // ...
  }
}

2-4. ์ค„๋ฐ”๊ฟˆ

  • ํ•จ์ˆ˜ ์ •์˜๊ฐ€ ์ตœ๋Œ€ ๊ธธ์ด๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ค„๋ฐ”๊ฟˆํ•ฉ๋‹ˆ๋‹ค.

    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 ๊ตฌ๋ฌธ ์œ„์™€ ์•„๋ž˜์—๋Š” ๊ณต๋ฐฑ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • extension ์ „์— ์ค„ ๋ฐ”๊ฟˆ์€ ํ•˜๋‚˜๋งŒ.

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

2-5. self

์‚ฌ์šฉ์„ ์ง€์–‘ํ•˜๊ณ , ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ / ๊ผญ ํ•„์š”ํ• ๋•Œ๋งŒ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”

2-6. ์ฃผ์„

๊น”๋”ํ•œ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์œ„ํ•ด ์ฃผ์„์€ ์ตœ๋Œ€ํ•œ ์ž‘์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

3๏ธโƒฃ ๊ธฐํƒ€

3-1. ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ถŒ์žฅ์‚ฌํ•ญ

  • ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ ํ•จ๊ป˜ ์ดˆ๊ธฐํ™”ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.ย [Then](https://github.com/devxoul/Then)์„ ์‚ฌ์šฉํ•˜๋ฉด ์ดˆ๊ธฐํ™”์™€ ํ•จ๊ป˜ ์†์„ฑ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    let label = UILabel().then {
      $0.textAlignment = .center
      $0.textColor = .black
      $0.text = "Hello, World!"
    }
  • Literal์— String๊ณผ Image๋ฅผ ํ•œ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    enum ImageLiterals {
        enum NavigationBar {
            static var icArrowLeft: UIImage { .load(named: "ic_back")}
        }
    }
  • ์ƒ์†๋˜์ง€ ์•Š๋Š” class๋Š” final class๋กœ ์„ ์–ธํ•ด์ฃผ์„ธ์š”

  • class ์•ˆ์˜ ๋ณ€์ˆ˜๋“ค์€ private์œผ๋กœ ์„ ์–ธํ•ด์ฃผ์„ธ์š”

  • ๊ฐ•์ œ ์–ธ๋ž˜ํ•‘์€ ์‚ฌ์šฉ์„ ์ง€์–‘ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ  Style Share Swift Style Guide