Swift 개발에 있어 범용적으로 사용할 수 있는 코드 컨벤션 가이드입니다
이 가이드는 코드의 일관성을 유지하고, 팀원 간의 협업을 원활하게 하기 위해 만들어졌습니다
-
기본 4 whiteSpace 들여쓰기 사용
if exampleCode { // ... }
-
타입과 프로토콜의 이름에는 PascalCase를 사용하고, 그 외에는 lowerCamelCase를 사용하기
protocol SpaceThing { // ... } class SpaceFleet: SpaceThing { enum Formation { // ... } class Spaceship { // ... } var ships: [Spaceship] = [] static let worldName: String = "Earth" func addShip(_ ship: Spaceship) { // ... } } let myFleet = SpaceFleet()
-
@ViewBuilder
메서드 경우 PascalCase 사용@ViewBuilder private func ExampleComponent() -> some View { ... }
-
Bool 타입 변수 선언 시,
is
||has
로 시작 할 수 있도록 선언var isShowing: Bool = false
-
동작을 의미하는 변수 및 메서드 선언 시, 동사가 맨 앞으로 오도록 선언
enum Action { case showDialog ... }
-
API -
fetch, add, update, delete
명칭 사용 ( not used - remove, get, create … )func fetchList() { ... }
-
extension 사용 시, 파일명
xxxxx +.swift
-
식별자 바로 뒤에 콜론(:)을 놓고 그 뒤에 공백을 두기
var something: String = "코드 컨벤션 만들기" func addWork(_ work: String) { // ... }
-
쉽게 추론할 수 있는 유형은 타입 표기 생략하기 ( 객체 생성 )
// WRONG let host: Host = Host() // RIGHT let host = Host()
enum Direction { case left case right } func someDirection() -> Direction { // WRONG return Direction.left // RIGHT return .left }
-
self.
- 네이밍이 겹치지 않는 이상 사용하지 않기final class Listing { init(capacity: Int, allowsPets: Bool) { // WRONG self.capacity = capacity self.isFamilyFriendly = !allowsPets // `self.` not required here // RIGHT self.capacity = capacity isFamilyFriendly = !allowsPets } private let isFamilyFriendly: Bool private var capacity: Int private func increaseCapacity(by amount: Int) { // WRONG self.capacity += amount // RIGHT capacity += amount // WRONG self.save() // RIGHT save() } }
-
여러 줄이 있는 배열은 각 괄호가 별도의 줄에 있도록 하기 ( 배열 속성 2개 이상의 경우 )
// WRONG let rowContent = [listingUrgencyDatesRowContent(), listingUrgencyBookedRowContent(), listingUrgencyBookedShortRowContent()] // RIGHT let rowContent = [ listingUrgencyDatesRowContent(), listingUrgencyBookedRowContent(), listingUrgencyBookedShortRowContent() ]
-
guard let 구문 - else 문 클로저가 return 이 아닐 경우, 클로저 줄 내려서 사용 ( 조건 여러개는 당연히 조건에만 여러 줄 사용 )
guard let xxxx = xxxx else { return } guard let xxxx = xxxx, xxxx.sdsda.isEmpty, xxxx.ssss.isEmpty else { return } guard let xxxx = xxxx else { throw abcdError.aaaaa }
-
switch case 문은 각 case 별로 한줄씩 띄어서 사용
func handle(_ action: SpaceshipAction) { switch action { case .engageWarpDrive: warpDrive.engage() case .enableArtificialGravity: artificialGravityEngine.enable(strength: .oneG) case .scanPlanet(let planet): scanner.scan(planet) case .handleIncomingEnergyBlast: energyShields.engage() } }
-
한줄 주석과 여러줄 주석을 구분하기
한줄 주석 // 여러줄 주석 ///
-
함수 정의에서 Void 리턴 타입을 생략하기
// WRONG func doSomething() -> Void { ... } // RIGHT func doSomething() { ... }
-
긴 함수의 호출 ( 파라미터 2개 이상 )
// WRONG universe.generateStars(at: location, count: 5, color: starColor, withAverageDistance: 4) // WRONG universe.generateStars(at: location, count: 5, color: starColor, withAverageDistance: 4) // RIGHT universe.generateStars( at: location, count: 5, color: starColor, withAverageDistance: 4 )
// RIGHT func generateStars( at location: Point, count: Int, color: StarColor, withAverageDistance averageDistance: Float ) async throws -> String { populateUniverse() } // RIGHT func generateStars( at location: Point, count: Int, color: StarColor, withAverageDistance averageDistance: Float ) { populateUniverse() }
-
사용하지 않는 클로저 파라미터의 이름은 밑줄(
_
)로 표시하기// WRONG someAsyncThing() { argument1, argument2, argument3 in print(argument3) } // RIGHT someAsyncThing() { _, _, argument3 in print(argument3) }
-
중위 연산자는 양쪽에 공백이 하나씩 두기 및 다양한 공백 너비보다는 괄호를 사용하여 연산자가 많은 문을 시각적으로 그룹화하기
// WRONG let capacity = 1+2 let capacity = currentCapacity ?? 0 let mask = (UIAccessibilityTraitButton|UIAccessibilityTraitSelected) let capacity=newCapacity let latitude = region.center.latitude - region.span.latitudeDelta/2.0 // RIGHT let capacity = 1 + 2 let capacity = currentCapacity ?? 0 let mask = (UIAccessibilityTraitButton | UIAccessibilityTraitSelected) let capacity = newCapacity let latitude = region.center.latitude - (region.span.latitudeDelta / 2.0)
-
삼항 연산자의 표현이 너무 길어지면, 줄 내려서 사용하기
let xx = ab ? a : b let destinationPlanet = solarSystem.hasPlanetsInHabitableZone ? solarSystem.planetsInHabitableZone.first : solarSystem.uninhabitablePlanets.first
-
조건이 여러 개의 경우, 각 조건이 별도의 줄에 있도록 하기 ( 조건 2개 이상의 경우 )
// WRONG if selectImage != userSaveImage && userNickname != nickname && userManager.uid != "" { // ... } // RIGHT if selectImage != userSaveImage && userNickname != nickname && userManager.uid != "" { // ... }
-
열거형에서
switch
를 사용할 때default
case를 사용하지 말기// WRONG switch trafficLight { case .greenLight: // Move your vehicle default: // Stop your vehicle } // RIGHT switch trafficLight { case .greenLight: // Move your vehicle case .yellowLight, .redLight: // Stop your vehicle }
-
기본 클래스는
final
// WRONG class SettingsRepository { // ... } // RIGHT final class SettingsRepository { // ... }
-
한줄일 경우엔
return
생략// WRONG ["1", "2", "3"].compactMap { return Int($0) } var size: CGSize { return CGSize( width: 100.0, height: 100.0) } func makeInfoAlert(message: String) -> UIAlertController { return UIAlertController( title: "ℹ️ Info", message: message, preferredStyle: .alert) } var alertTitle: String { if issue.severity == .critical { return "💥 Critical Error" } else { return "ℹ️ Info" } func type(of planet: Planet) -> PlanetType { switch planet { case .mercury, .venus, .earth, .mars: return .terrestrial case .jupiter, .saturn, .uranus, .neptune: return .gasGiant } } // RIGHT ["1", "2", "3"].compactMap { Int($0) } var size: CGSize { CGSize( width: 100.0, height: 100.0) } func makeInfoAlert(message: String) -> UIAlertController { UIAlertController( title: "ℹ️ Info", message: message, preferredStyle: .alert) } var alertTitle: String { if issue.severity == .critical { "💥 Critical Error" } else { "ℹ️ Info" } func type(of planet: Planet) -> PlanetType { switch planet { case .mercury, .venus, .earth, .mars: .terrestrial case .jupiter, .saturn, .uranus, .neptune: .gasGiant } }
-
서로 다른 종류의 프로퍼티 선언 사이에 빈 줄을 추가하기
// WRONG static let gravityEarth: CGFloat = 9.8 static let gravityMoon: CGFloat = 1.6 var gravity: CGFloat // RIGHT static let gravityEarth: CGFloat = 9.8 static let gravityMoon: CGFloat = 1.6 var gravity: CGFloat
-
import 문 관리
내부 / 외부 한 줄 띄고 관리 ( 외부를 상단에, 내부를 하단에, test 최하단 )
a-z 알파벳 순으로
import Constellation import DLSPrimitives import Epoxy import Foundation @testable import Epoxy
-
프로퍼티 순서
- 한줄 띄어서 구분
propertyWrapper
가 붙는 프로퍼티를 최상단에 선언- EnvironmentObject, StateObject, ObservedObject, State, Binding, private 순으로 선언
- var / let 의 경우, var 를 상단에 let 을 하단에 선언
struct xxx { @EnvironmentObject var ... @StateObject var ... @ObservedObject var ... @State var ... @Binding var ... @Presents var ppp: ... private var ... var aaa var bbb var ccc let ddd let eee }
- 연산 프로퍼티는 프로퍼티 중 맨 밑에 선언
// WRONG var atmosphere: Atmosphere { didSet { print("oh my god, the atmosphere changed") } } var gravity: CGFloat // RIGHT var gravity: CGFloat var atmosphere: Atmosphere { didSet { print("oh my god, the atmosphere changed") } }
-
In TCA
- @Dependency 위치는 Action 과 body 사이에 사용
- @Dependency 도 알파벳 순으로 선언
enum Action { ... } @Dependency(\.dismiss) var dismiss @Dependency(\.swiftDataService) var swiftData var body: some ReducerOf<Self> {
- 프로퍼티 && 액션
- 관련 기능끼리 모으는걸 우선시하고, 내부에서는 알파벳 순으로 정렬하기