EEYatHo 앱 깎는 이야기

Swift ) 코드 컨벤션 - EEYatHo iOS 본문

iOS, Swift

Swift ) 코드 컨벤션 - EEYatHo iOS

EEYatHo 2022. 12. 26. 20:22
반응형

 

노션 링크

 

코드 컨벤션

📌 코드 포맷팅

iris-numeric-9d0.notion.site

 

 

📌 코드 포맷팅

띄어쓰기, 쉼표, 콜론, 줄바꿈 등. 기능 및 성능과는 직접적인 연관이 없는, 코드들의 모양 (겉부분) 을 다룹니다.

통일된 코드 포멧은, 우리 뇌의 청킹을 도와, 타인의 코드를 더 쉽게 볼 수 있게 도와줍니다.

 

1. import

 

  • import 순서

내장 프레임워크를 먼저 import 한 후, 한줄 띄우고 third-party 들을 import 합니다.

이후 알파벳 순으로 정렬합니다.


✅ Preferred

import Foundation
import UIKit

import AdSupport
import AppTrackingTransparency
import RxCocoa
import RxSwift
import SwiftyJSON

⛔ Not Preferred

import UIKit
import AppTrackingTransparency
import Foundation
import RxSwift
import RxCocoa
import SwiftyJSON
import AdSupport

 

 

 

  • 필요한 최소한의 모듈만 import 합니다.

✅ Preferred

import UIKit

var view: UIView

⛔ Not Preferred

import Foundation
import UIKit // not required

var view: UIView

 

 

2. 들여쓰기

 

  • control(^) + i

control(^) + i 를 이용해 들여쓰기 합니다.


✅ Preferred

var vaildProductCount: Int {
    allProducts
        .filter { $0.isVaild }
        .count
}

⛔ Not Preferred

var vaildProductCount: Int {
    allProducts
    .filter { $0.isVaild }
    .count
}

 

 

3. 띄어쓰기

 

  • 콜론(:)

클래스 상속, 클래스 프로토콜 채택, 타입 선언, 호출 파라미터, 선언 파라미터, 제네릭 프로토콜 채택, 딕셔너리에서 콜론(:) 사용 시, 오른쪽에만 띄어쓰기를 하나 둡니다.

삼항연산자를 제외한 모든 곳 입니다.


✅ Preferred

class MyViewController: UIViewController { ... }
extension MyViewController: UITableViewDataSource { ... }
let headerDictionary: [String: String] = [
    "Content-Type": "application/json"
]
someFunction(name: someString)
func myFunction<T, U: SomeProtocol>(firstParam: T, secondParam: U) { ... }

⛔ Not Preferred

class MyViewController:UIViewController { ... }
extension MyViewController : UITableViewDataSource { ... }
let headerDictionary:[String : String] = [
    "Content-Type":"application/json"
]
someFunction(name : someString)
func myFunction<T, U:SomeProtocol>(firstParam : T, secondParam:U) { ... }

 

 

 

    • 삼항연산자에서 콜론(:) 사용 시, 양 옆에 띄어쓰기를 하나 둡니다.

✅ Preferred

var dayString = day < 10 ? "0\\(day)" : "\\(day)"

⛔ Not Preferred

var dayString = day < 10 ? "0\\(day)": "\\(day)"

 

 

 

  • 쉼표(,)

배열, 함수, switch-case 등 모든 곳에서 쉼표(,) 사용 시, 오른쪽에만 띄어쓰기를 하나 둡니다.


✅ Preferred

let array = ["A", "B", "C"]

func testFunc<T, U>(firstParam: T, secondParam: U) { ... } 

someFunction(name: "a", age: 15)

switch number {
case 1, 2, 3:
    // Do something
default:
    break
}

let dict: [String: Int] = [
    "a": 1,
    "b": 2
]

⛔ Not Preferred

let array = ["A","B" , "C"]

func testFunc<T,U>(firstParam: T , secondParam: U) { ... } 

someFunction(name: "a",age: 15)

switch number {
case 1 , 2,3:
    // Do something
default:
    break
}

let dict: [String: Int] = [
    "a": 1 ,
    "b": 2
]

 

 

 

  • 연산자

연산자는 양 옆에 띄워쓰기를 하나 둡니다.


✅ Preferred

let answer = 1 + 2 / 3 % (4 * 5)

⛔ Not Preferred

let answer = 1+2/3%(4*5)

 

 

 

  • 함수 선언 소괄호

일반적으로 함수 선언시, 함수명과 소괄호를 붙혀쓰고, 연산자 오버로딩시 띄어쓰기 하나를 둡니다.

Swift 내장 프레임워크를 참고하였습니다. (ex. Equatable 구현부)


✅ Preferred

func testFunc() {}
func == (lhs: SomeClass, rhs: SomeClass) {}

⛔ Not Preferred

func testFunc () {}
func ==(lhs: SomeClass, rhs: SomeClass) {}

 

 

 

  • 리턴 타입 ( 화살표 )

함수와 클로저 리턴 타입에 사용되는 화살표는 양 옆에 띄워쓰기를 하나 둡니다.


✅ Preferred

func someFunc() -> String { ... }
let completion: () -> Void

⛔ Not Preferred

func someFunc()->String { ... }
let completion: ()->Void

 

 

 

  • 한줄 클로저

한줄 클로저 등, 중괄호 안쪽은 띄워쓰기를 하나 둡니다.


✅ Preferred

numArray
    .filter { $0 % 2 == 0 }
    .map { "\\($0)" }
    .joined(separator: ",")

guard let self = self else { return }

⛔ Not Preferred

numArray
    .filter {$0 % 2 == 0}
    .map {"\\($0)" }
    .joined(separator: ",")

guard let self = self else { return}

 

 

4. 줄 바꿈

 

 

  • Page guide 120 charactor

Xcode 의 Page guide 기준, 120 charactor 을 넘으면 반드시 줄 바꿈 합니다.

SwiftLint 의 기본값을 참고했습니다.

Page guide 켜는 방법


✅ Preferred

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... }

⛔ Not Preferred

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... }

 

 

 

  • guard let self = self else { return }

guard let self = self else { return } 은 한 줄로 씁니다.


✅ Preferred

guard let self = self else { return }

⛔ Not Preferred

guard let self = self else { 
    return 
}

 

 

 

  • // MARK: -

// MARK: - 주석은 위로 2개 아래로 1개 빈 줄을 둡니다.


✅ Preferred

final class HomeViewController: UIViewController { ... }


// MARK: - UITableViewDataSource

extension HomeViewController: UITableViewDataSource { ... }


// MARK: - UITableViewDelegate

extension HomeViewController: UITableViewDelegate { ... }

⛔ Not Preferred

final class HomeViewController: UIViewController { ... }

// MARK: - UITableViewDataSource

extension HomeViewController: UITableViewDataSource { ... }

// MARK: - UITableViewDelegate

extension HomeViewController: UITableViewDelegate { ... }

 

 

 

  • 여는 중괄호 ( 1TBS 스타일 )

여는 중괄호({) 를 새 줄에 배치하지 않습니다.

1TBS 스타일을 사용합니다.


✅ Preferred

func someMethod() {
    // Do something
}

⛔ Not Preferred

func someMethod() 
{
    // Do something
}

 

 

5. 소괄호

 

  • 불필요한 소괄호 생략

if, switch-case, 파라미터, 후행 클로저 등 불필요한 소괄호는 생략합니다.


✅ Preferred

if count > 0 { ... }
switch type { ... }
_ = arr.filter { num in num % 2 == 0 }

⛔ Not Preferred

if (count > 0) { ... }
switch (type) { ... }
_ = arr.filter() { (num) in num % 2 == 0 }

 

 

📌 네이밍

네이밍은 코드의 첫인상 입니다.

클래스와 함수의 표기법이 같은 경우, 생성자인지 함수인지 헷갈리게하고,

동의어를 여러개 쓴 경우, 같은 의미임에도 다른 의미가 있는지 불필요한 생각을 하게도 합니다.

 

 

1. 표기법

 

  • UpperCamelCase

클래스, 구조체, 열거형, 프로토콜, Xcode 안에서 만들수 있는 파일 이름(.swift, .storyboard, .xcassets, .plist 등) 에는 UpperCamelCase 를 사용합니다.

단어의 첫글자를 대문자로 표기합니다.


✅ Preferred

class MyViewController { ... }
struct MyModel { ... }
enum SomeEnum { ... }
protocol RootPresentable { ... }

// swift 파일 이름 : MyViewController.swift
// storyboard 파일 이름 : LaunchScreen.storyboad
// xcassets 파일 이름 : AppIcon.xcassets

⛔ Not Preferred

class myViewController { ... }
struct myModel { ... }
enum someenum { ... }
protocol rootpresentable { ... }

// swift 파일 이름 : Myviewcontroller.swift
// storyboard 파일 이름 : launchScreen.storyboad
// xcassets 파일 이름 : app_icon.xcassets

 

 

 

  • lowerCamelCase

변수, 상수, 함수, case, Xcode 외부에서 가져온 파일 이름(.png, .json, .wav )등 에는 lowerCamelCase 를 사용합니다.

단어의 첫글자를 대문자로 표기하되, 첫글자를 소문자로 표기합니다.


✅ Preferred

var myVariable = 0
let leftPadding = 16.0

func someFunc() { ... }

enum {
    case firstCase
    case secondCase
}

// 이미지 파일 이름 : logout24Gray800
// Lottie 파일 이름 : live.json
// 사운드 파일 이름 : foundItem.wav

⛔ Not Preferred

var myvariable = 0
let LEFT_PADDING = 16.0

func SomeFunc() { ... }

enum {
    case FirstCase
    case secondcase
}

// 이미지 파일 이름 : logout_24_gray_800
// Lottie 파일 이름 : Live.json
// 사운드 파일 이름 : FOUND_ITEM.wav

 

 

2. 명명법

 

  • 동의어

동의어들 중에 어떤 것을 사용할지 정리합니다.

✅ 사용할 이름 ⛔ 사용하지 않을 이름
상품 product item
편성표 timeline schedule, pairing, timeLine
상품 ID productId pid, id, pdid, crawlId, shopVenderProductId
엔티티 ID entitiyId eid, id, itemId
유저 ID xid serverId, userId, userToken
쇼핑사명 shop genre2, logo
타입 type case, kind
유저 터치 tap touch, click, press, tab
유저 텍스트 입력 write input, text, change
유저 스크롤 scroll drag, pullUp, pullDown
요청 get request, fetch
초기화 create init
여백 padding margin, space, edge

 

 

 

  • 액션 함수

액션함수(유저 인터렉션을 처음 처리하는 함수) 와 ReactorKit.Action 는, 주어 + 동사 + 목적어 순으로 사용합니다.

액션이 일어나기 전을 will, 일어난 후를 did 로 표현합니다.

ex) tap, write, scroll 등


✅ Preferred

@objc func backButtonDidTap() { ... }

⛔ Not Preferred

@objc func pop() { ... }
@objc func clickBack() { ... }

 

 

 

  • API 함수

API 를 호출하는 함수는 앞에 HTTP 함수를 붙힙니다.

ex) get, post, put, delete, patch 등


✅ Preferred

static func getAccessToken() -> Observable<Data> { ... }

static func patchShippingAddress() -> Observable<Data> { ... }

⛔ Not Preferred

static func requestAccessToken() -> Observable<Data> { ... }
static func takeAccessToken() -> Observable<Data> { ... }
static func fetchAccessToken() -> Observable<Data> { ... }

static func updateShippingAddress() -> Observable<Data> { ... }
static func modifyShippingAddress() -> Observable<Data> { ... }

 

 

 

  • 약어

약어는 모두 대문자로 표기하고, 약어로 시작하는 경우에만 소문자로 표기합니다.


✅ Preferred

var productID: Int?
var urlString: String?
var homeVC: ViewController?

⛔ Not Preferred

var productId: Int?
var URLString: String?
var homevc: ViewController?

 

 

 

  • 명확한 이름

클래스, 함수, 변수 어디든. 가능한 명확하고 모든 의미를 포함하는 이름을 사용합니다.

이름이 길어져도 의미가 있는 이름이, 타인의 코드를 볼 때 훨씬 빠르고 편합니다.


✅ Preferred

class RoundAnimationButton { ... }
func startAnimating() { ... }
let animationDuration: CGFloat
let personImageView: UIImageView
let titleLabel: UILabel

⛔ Not Preferred

class CustomButton { ... }
func srtAnimating() { ... }
let aniDur: CGFloat
let personImage: UIImageView  // image? imageView?
let title: UILabel            // String? label?

 

 

 

  • Protocol

프로토콜의 이름은, 애플의 API 디자인 가이드라인을 참고합니다.

  • 무엇인가 설명하는 프로토콜은 명사로 읽어야 합니다. (e.g. Collection).
  • 기능을 설명하는 프로토콜은 able, ible, ing 접미사를 사용합니다.(e.g. Equatable, ProgressReporting)
  • 어떤 것도 적합하지 않은 경우, Protocol 을 접미사로 사용할 수 있습니다.

✅ Preferred

protocol ImagePresentable { ... }
protocol NotificationUIProtocol { ... }

⛔ Not Preferred

protocol ImagePresent { ... }
protocol NotificationUIType { ... }

 

 

📌 Detail. 코드 스타일

 

1. 타입추론 최대한 사용

가능하면 타입추론을 사용합니다.


✅ Preferred

let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)

⛔ Not Preferred

let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)

 

 

2. self 생략

필수가 아닌 이상, self 를 사용하지 않습니다.


✅ Preferred

private let name: String
private let isAdult: Bool
  
init(name: Int, age: Bool) {
    self.name = name
    idAdult = age > 18
}

⛔ Not Preferred

private let name: String
private let isAdult: Bool
  
init(name: Int, age: Bool) {
    self.name = name
    self.idAdult = age > 18
}

 

 

3. tuple 값 네이밍

tuple 이 가진 값에는 명확성을 위해 이름을 붙혀줍니다.


✅ Preferred

func someFunc() -> (x: Int, y: Int) {
    return (x: 4, y: 4)
}

enum SomeEnum {
    case someCase(x: Int, y: Int)
}

⛔ Not Preferred

func someFunc() -> (Int, Int) {
    return (4, 7)
}

enum SomeEnum {
    case someCase(Int, Int)
}

 

 

4. 배열과 딕셔너리 생성

배열과 딕셔너리를 생성할 때 [T], [T: U] 를 사용합니다.


✅ Preferred

var stringArray: [String]?
var stringDictionary: [Int: String]?

⛔ Not Preferred

var stringArray: Array<String>?
var stringDictionary: Dictionary<Int, String>?

 

 

5. 내장 프로퍼티 선언과 초기화

내장 프로퍼티는 가능하면 unwrapped Optional 을 사용하고, 생성자에서 초기화합니다.


✅ Preferred

class SomeClass: NSObject {

    init() {
        someInt = 0
        someString = "created"
        super.init()
    }

    var someInt: Int
    var someString: String
}

⛔ Not Preferred

class SomeClass: NSObject {

    init() {
        super.init()
    }

    var someInt: Int = 0
    var someString: String?
}

 

 

6. 타입 메소드는 static

타입 메소드는 static 을 사용합니다.


✅ Preferred

class 

⛔ Not Preferred

class SomeClass: NSObject {

    init() {
        super.init()
    }

    var someInt: Int = 0
    var someString: String?
}

 

 

7. final 사용

상속이 필요없는 class, 함수, 변수는 final 키워드를 사용합니다.

final 을 사용하면, 동적 디스패치를 거치지 않고 직접 호출하기 때문에, 컴파일 성능 이점이 있습니다.

Swift 깃허브 OptimizationTips 링크


✅ Preferred

final class SomeClass: NSObject {
    final var arr: [Int]
    final func someFunc() {} 
}

⛔ Not Preferred

class SomeClass: NSObject {
    var arr: [Int]
    func someFunc() {} 
}

 

 

8. 프로토콜 채택시, 코드 나누기

프로토콜 채택시, extension과 // MARK: - 주석으로 나눕니다.


✅ Preferred

final class HomeViewController: UIViewController { ... }

// MARK: - UITableViewDataSource

extension HomeViewController: UITableViewDataSource { ... }

// MARK: - UITableViewDelegate

extension HomeViewController: UITableViewDelegate { ... }

⛔ Not Preferred

final class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { ... }

 

 

9. switch-case default 미사용

switch-case 문 사용시, 가능하면 default 를 사용하지 않습니다.

새로운 case 생성시 놓칠 수 있기 때문입니다.


✅ Preferred

switch someEnum {
case someCase1:
    print("someCase1")
case someCase2, soemCase3:
    break
}

⛔ Not Preferred

switch someEnum {
case someCase1:
    print("someCase1")
default:
    break
}

 

 

10. 사용하지 않는 코드

Xcode가 자동으로 생성했지만, 사용하지 않는 코드는 모두 제거합니다.


✅ Preferred

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .white
}

⛔ Not Preferred

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    view.backgroundColor = .white
}

 

 

 

 

  • 참고 링크

Swift.org

팀에서 사용 중인 Swift Style Guide (코드 컨벤션)

https://github.com/linkedin/swift-style-guide

Comments