Skip to content

Swift Code Convention

Mintaek Kim (Meenu) edited this page Jan 13, 2023 · 3 revisions

Code Layout

들여쓰기 및 띄어쓰기

  • 들여쓰기에는 tab 혹은 4칸의 space를 사용한다

  • 삼항연산자를 제외하고 콜론(:)을 사용할 때에는 오른쪽에만 한 칸의 공백을 준다.

    let userAgeDictionary: [String: Int]

줄바꿈

  • 한 줄에 길이가 99자를 초과하는 경우, 줄바꿈한다.

    • Xcode에서 Preferences - Text Editing - DisplayPage guide at column 옵션을 체크하고 99자로 설정하면 편리하게 관리할 수 있다.
  • 함수 정의가 최대 길이(99자)를 초과하는 경우, 아래와 같이 줄바꿈한다.

func collectionView (
    _ collectionView: UICollectionView,
    cellForItemAt indexPath: IndexPath
) -> UICollectionViewCell { // 119자
    // doSomething()
}

func animationController(
    forPresented presented: UIViewController,
    presenting: UIViewController,
    source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
    // doSomething()
}
  • 함수를 호출하는 코드가 최대 길이(99자)를 초과하는 경우, 파라미터 이름을 기준으로 줄바꿈한다.
let actionSheet = UIActionSheet(
    title: "정말 계정을 삭제하실 건가요?",
    delegate: self,
    cancelButtonTitle: "취소",
    destructiveButtonTitle: "삭제해주세요"
)
  • 파라미터에 클로저가 2개 이상 존재하는 경우, 무조건 내려쓰기한다.
UIView.animate(
    withDuration: 0.25,
    animations: {
        // doSomething()
    }
    completion: { finished in
        // doSomething()
    }
)

빈 줄

  • 빈 줄에는 공백이 포함되지 않도록 한다.
  • 모든 파일은 빈 줄로 끝나도록 한다.
  • // MARK: - 구문 위와 아래에는 공백이 필요하다.
// MARK: - Layout

override func layoutSubview() {
    // doSomething
}

// MARK: - Actions

override func menuButtonDidTap() {
    // doSomething
}

import

  • 모듈 import는 알파벳 순으로 정렬한다. 내장 프레임워크를 먼저 import하고, 빈 줄로 구분하여 서드파티 프레임워크를 import한다.
import Combine
import Foundation
import SwiftUI
import UIKit

import SwiftyColor
import SwiftyImage
import Then
import URLNavigator

네이밍

클래스 및 구조체

  • 클래스 및 구조체의 이름에는 PascalCase(UpperCamelCase)를 사용한다.
  • 클래스 및 구조체의 이름에는 접두사(prefix)를 붙이지 않는다.

함수

  • 함수 이름에는 camelCase(lowerCamelCase)를 사용한다.

  • 함수 이름 앞에는 get, set을 사용하지 않는다.

  • Action 함수의 네이밍은 '동사 + 목적어'의 형태를 사용한다.

    • request는 에러가 발생하거나, 실패할 수 있는 비동기 작업에 사용한다.
    • fetch는 요청이 실패하지 않고 결과를 바로 반환할 때 사용합니다.
  • Event-Handling 함수의 경우 (조동사 + 동사원형)으로 시작한다. 주어는 유추 가능하다면 생략 가능하다.

    • will은 특정 행위가 일어나기 직전을 의미한다.
    • did는 특정 행위가 일어난 직후를 의미한다.

변수 및 상수

  • 변수 및 상수의 이름에는 camelCase(lowerCamelCase)를 사용한다.

  • 변수를 정의할 때, 변수의 타입이 명확하다면 생략한다.

✅ Preferred

var number = 0

❌ Not Preferred

var number: Int = 0
  • Sequential Type에는 각 Sequential Type에 맞는 접미어를 사용한다.

✅ Preferred

var categoryArray: [String]
var userDictionary: [String: Int]
var person: Person
var isShowing: Bool

❌ Not Preferred

var category: [String]
var show: Bool

열거형

  • enum의 이름에는 PascalCase(UpperCamelCase)를 사용한다.
  • enum의 각 case에는 camelCase(lowerCamelCase)를 사용한다.
enum Color {
    case red
    case green
    case blue
}

약어

  • 약어는 사용하지 않는다.

✅ Preferred

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

❌ Not Preferred

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

클로저

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

✅ Preferred

let completionBlock: (() -> Void)?

❌ Not Preferred

let completionBlock: (() -> ())?
let completionBlock: ((Void) -> (Void))?
  • Closure 정의시 파라미터에는 괄호를 사용하지 않는다.

✅ Preferred

{ operation, responseObject in
    // doSomething()
}

❌ Not Preferred

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

✅ Preferred

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

❌ Not Preferred

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

✅ Preferred

Button("save") {
    saveData()
}

❌ Not Preferred

Button(action: {
    saveData()
}, label: {
    Text("save")
})

클래스와 구조체

  • 클래스와 구조체 내부에서는 self를 명시적으로 사용한다.
  • 구조체를 생성할 때에는 Swift 구조체 생성자를 사용한다.

✅ Preferred

let frame = CGRect(x: 0, y: 0, width: 100, height: 100)

❌ Not Preferred

let frame = CGRectMake(0, 0, 100, 100)

타입

  • Array<T>Dictionary<T: U>보다는 [T], [T: U]를 사용한다.

✅ Preferred

var messages: [String]?
var names: [Int: String]?

❌ Not Preferred

var messages: Array<String>?
var names: Dictionary<Int: String>?

주석

  • 주석 내용을 입력할 때에는 // 뒤에 공백을 한 칸 입력한다.
  • MARK: -를 사용해서 연관된 코드를 구분짓고, MARK: - 구문 위와 아래에 공백을 넣는다.
// MARK: - Init

override init(frame: CGRect) {
    // doSomething()
}

deinit {
    // doSomething()
}

// MARK: - Layout

override func layoutSubviews() {
    // doSomething()
}

// MARK: - Actions

override func menuButtonDidTap() {
    // doSomething()
}

문서화 주석

  • 변수, 상수, 클래스, 메서드, 함수, 열거형 등을 설명할 경우 마크업 문법에 따라 주석을 작성하면 다른 프로그래머가 '퀵헬프'를 통해 해당 내용을 확인할 수 있다. (command + option + /)
  • 한 줄 문서화 주석은 ///를 사용한다.
/// 사용자 프로필을 그려주는 뷰
class ProfileView: UIView {
  
    /// 사용자 닉네임을 그려주는 라벨
    var nameLabel: UILabel!
}
  • 여러 줄 문서화 주석은 아래와 같이 사용한다.
import SwiftUI

extension Image: ViewModifier {
    /**
     이미지를 배경에 맞게 만들어 주는 Modifier
     
     이미지에 사용하면 이미지를 SafeArea를 무시하고 배경에 꽉 차게 만들어 줍니다
    
     **Example**
    ```

    Image("imageName").backgroundImage()
    ```
    */
    func backgroundStyle() -> some View {
    	self
    		.resizable()
    		.ignoresSafeArea()
    }

}
  • 여러 줄 문서화 주석 사용시 아래와 같이 퀵헬프를 사용할 수 있다.
스크린샷 2022-08-04 21 30 15

프로그래밍 권장사항

  • 가능하다면 변수를 정의할 때 초기화하도록 한다.

✅ Preferred

var number = 0

❌ Not Preferred

var number: Int
number = 0
  • 상수를 정의할 때에는 enum을 만들어 비슷한 상수끼리 모아둔다
    • 재사용성 및 유지보수 측면에서 큰 향상
    • struct 대신 enum을 사용하는 이유는, 생성자가 제공되지 않는 자료형을 사용하기 위함
final class ProfileViewController: UIViewController {
    private enum Font {
        static let nameLabel = UIFont.boldSystemFont(ofSize: 14)
    		static let bioLabel = UIFont.boldSystemFont(ofSize: 12)
    }
}
  • 위와 같이 선언된 상수들은 아래와 같이 사용할 수 있다.
self.nameLabel.font = Font.nameLabel
  • 더이상 상속이 발생하지 않는 클래스는 항상 final 키워드로 선언한다.
  • 프로토콜을 적용할 때에는 extension을 만들어서 관련된 메서드를 모아둔다.

✅ Preferred

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

// MARK: - UITableViewDataSource

extension MyViewController: UITableViewDataSource {
    // ...
}

// MARK: - UITableViewDelegate

extension MyViewController: UITableViewDelegate {
    // ...
}

❌ Not Preferred

final class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // ...
}
  • 하나의 함수는 한 가지 기능만 수행하도록 한다.