-
Notifications
You must be signed in to change notification settings - Fork 1
Swift Style Guide
-
최대한 명시적인 이름을 사용한다.
ViewModel의 경우 이름을 다 작성해준다. (MC 등으로 줄여서 사용하지 않음)
// 좋은 예 class HomeViewController: UIViewController { // } class MovieDetailCollectionCell: UICollectionViewCell { } struct HomeView { } // 나쁜 예 class HomeVM: ObservableObject { // }
Delegate의 경우 객체이름 + 기능 + Delegate를 적는다.
// HomeViewController의 화면 플로우 Delegate protocol HomeViewControllerFlowDelegate { }
-
Extension : 하나의 Extenstion에 대해 하나의 파일에 적는 것을 우선
파일 크기가 너무 커진다 싶으면 분리 후 PR ㄱㄱ
기본 -> Extension+String // String과련 Extension 다 적기
-
Unit Test 파일의 경우 테스트 기능 + Tests를 적어준다.
// 회원가입 정규식 로직 확인 유닛 테스트 SignUpRegexTests
-
UI Test 파일의 경우 화면 이름 + UITests 을 적어준다.
// 로그인 화면 UI 테스트 LoginUITests
-
ReducerProtocol : 기능명 + Feature
struct LoginFeature: ReducerProtocol { }
- 최대 700줄이다. (Lint 기본값 경고:400, 에러:1000)
```
#예시
📁 dnd-9th-2-ios
- App
- info.plist
- Resources
- Assets
- Colors, Fonts, Icons, Images
- Sources
- AppView
- AppView (ContentView)
- Lifecycle
- AppDelegate
- SceneDelegate
- Core
- Common
- enum형 파일
- Extensions (Array+, Date+)
- Models
Scene별로 폴더 분리, Model
- Dummy
- DummyModel
- Services
Scene별로 폴더 분리, API, Service
- Dummy
- DummyAPI
- DummyService
- Features
Scene별로 폴더 분리 - Feature(action, state), View
- DummyScene
- DummyFeature
- DummyView
- TabBarScene
- TabBarFeature
- TabBarItem
- TabBarView 등등
- DesignSystem
공통 컴포넌트
- Views (컴포넌트)
- Background, BottomSheet, Button 등
- Extension
- 컬러, 폰트 등 extension
```
-
tab으로 사용한다. (tab은 xcode에서 4칸)
extension String { func isValidRegex(_ regex: String) -> Bool { // } }
-
공백문자 기본값은 유니코드 가로 공간 문자(U+0020) (= space 바 한 칸) 이다.
:
,,
의 경우 띄어쓰기는 뒤에만 사용한다.let example: Int = 1 func addTwoInts(a: Int, b: Int) { }
-
그 외
→
나{
,}
는 앞 뒤로 띄어쓰기를 한다.// 함수 리턴값 // return 앞 뒤로 꼭 띄어쓰기 guard let data = data else { return }
-
블럭
{}
시작 과 끝에는 공백이 없다.class CartViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } }
-
함수 정의가 최대 길이를 초과하는 경우에는 아래와 같이 줄바꿈합니다. (초과 안하면 1줄에 적기)
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
구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다. -
guard let
구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다.else
는guard
와 같은 들여쓰기를 적용합니다.if let user = self.veryLongFunctionNameWhichReturnsOptionalUser(), let name = user.veryLongFunctionNameWhichReturnsOptionalName(), user.gender == .female { // ... } guard let user = self.veryLongFunctionNameWhichReturnsOptionalUser(), let name = user.veryLongFunctionNameWhichReturnsOptionalName(), user.gender == .female // 띄어쓰기 else { return }
-
Switch 문 작성시 한 줄에 작성할 수 있으면, 한 줄에 다 작성합니다. 한 줄 최대 길이를 넘어가면 줄바꿈을 합니다.
// 좋은 예 switch self { case .home: return "home" case .mypage: return "mypage" } // 좋은 예 : 한 줄 최대길이를 넘어 줄바꿈을 했다 switch self { case .home: return "Lorem Ipsum is simply dummy text of the printing and typesetting industry." case .mypage: return "Lorem Ipsum is simply dummy text of the printing and typesetting industry." } // 나쁜 예 switch self { case .home: return "home" case .mypage: return "mypage" }
- 한 줄은 최대 100자를 넘지 않아야 합니다.
- Xcode의 Preferences → Text Editing → Display의 'Page guide at column' 옵션을 활성화하고 100자로 설정
- 빈 줄은 공백이 없습니다.
-
SwiftLint의 file_types_order를 따른다.
/// 좋은 예 // Supporting Types = 추상화, Delegate protocol // Main Types class TestViewController: UIViewController { // Type Aliases typealias CompletionHandler = ((TestEnum) -> Void) // 서브 타입 class TestClass { // 10 lines } // Stored Type Properties = static 프로퍼티 static let cellIdentifier: String = "AmazingCell" // Stored Instance Properties = 저장 프로퍼티 var shouldLayoutView1: Bool! weak var delegate: TestViewControllerDelegate? // Computed Instance Properties = 연산 프로퍼티 private var hasAnyLayoutedView: Bool { return hasLayoutedView1 || hasLayoutedView2 } // IBOutlets -> 프로젝트에서 쓰진 않지만 우선은 Lint에 포함되어 있음 @IBOutlet private var view1: UIView! // Initializers = 초기화 override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // Type Methods = static 메소드 static func makeViewController() -> TestViewController { // some code } // Life-Cycle Methods = 라이플 사이클 함수 override func viewDidLoad() { super.viewDidLoad() } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() } // IBActions -> IBOutlet과 마찬가지로 프로젝트에서 쓰진 않지만 우선은 Lint에 포함되어 있음 @IBAction func goNextButtonPressed() { goToNextVc() delegate?.didPressTrackedButton() } @objc func goToRandomVcButtonPressed() { goToRandomVc() } // MARK: 메소드들 func goToNextVc() { /* TODO */ } private func getRandomVc() -> UIViewController { return UIViewController() } // Subscripts = 서브 스크립트 subscript(_ someIndexThatIsNotEvenUsed: Int) -> String { get { return "This is just a test" } set { log.warning("Just a test", newValue) } } } // Extensions = 프로토콜 채택 (추상화 제외 -> Main Type 선언시 채택) extension TestViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } }
- 타입(클래스, 구조체, 프로토콜 등)은 최대 250자이다.
- 타입(클래스, 구조체, 프로토콜 등)의 이름(name)은 3~40자이다.
- 프로퍼티(identifier) 의 이름(name)은 3~40자이다.
- 예외 : id, URL, url
-
클래스와 구조체 내부에서는 self를 사용할 필요가 없는 경우 사용하지 않습니다.
// 좋은 예 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationController?.pushViewController( someViewController, animated: true ) } } // 나쁜 예 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.navigationController?.pushViewController( someViewController, animated: true ) } }
-
구조체를 생성할 때에는 Swift 구조체 생성자를 사용합니다.
// 좋은 예 let frame = CGRect(x: 0, y: 0, width: 100, height: 100) // 나쁜 예 let frame = CGRectMake(0, 0, 100, 100)
-
함수 길이는 최대 50줄 입니다.
-
함수 이름에는 lowerCamelCase를 사용합니다.
func deleteUser() { // }
-
함수 이름 앞에는 되도록이면
get
,set
을 붙이지 않습니다. 바로 명사로 시작하거나 다른 동사를 사용합니다.// 좋은 예 func date(from string: String) -> Date? func distance(from location: CLLocation) -> CLLocationDistance // 나쁜 예 func getDate(from string: String) -> Date? func getDistance(from location: CLLocation) -> CLLocationDistance
-
함수 이름에 인자가 명시적으로 나타나는 경우 인자를 생략가능 합니다.
// 생략 func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b } // 생략 안 한 경우 func addTwoInts(a: Int, b: Int) -> Int { return a + b }
Action 함수의 네이밍은 '주어 + 동사 + 목적어' 형태를 사용합니다.
-
Tap(눌렀다 뗌)*은
UIControlEvents
의.touchUpInside
에 대응하고, *Press(누름)*는.touchDown
에 대응합니다. -
동사는 원형으로 쓴다.
// 좋은 예 func backButtonDidTap() { // ... } // 나쁜 예 func back() { // ... } func touchBack() { // ... }
-
will~은 특정 행위가 일어나기 직전이고, did~는 특정 행위가 일어난 직후입니다.
func viewDidLoad() { // } // before, after 아님
-
Bool
타입 함수의 경우 is로 시작합니다.func isValid() -> Bool { // }
-
클래스 내부 띄어쓰기
- 첫 줄은 띄운다. 막 줄은 붙인다. → 알아서
-
함수 내부 띄어쓰기
- 시작, 막줄 다 붙인다.
- 위쪽에 변수 선언부가 몰려있으면 로직 시작 전 한 칸
- 두 개 이상의 로직이 들어있다면 로직 사이에 한 칸
- return 띄어쓰기는 줄 수에 따라 → 짧으면 붙이고, 길면 띄우기 → 코드리뷰시 코멘트 남겨주기
func hello() -> String { let a: Int = 1 var b: String = "world" var c: String = "안녕하세요" b += "\(a)" c += "\(a)" c += b return c }
-
변수 이름에는 lowerCamelCase를 사용합니다.
-
상수 이름에는 lowerCamelCase를 사용합니다.
-
프로퍼티 초기화시 타입 적기
// 좋은 예 let loginViewModel: LoginViewModel = LoginViewModel() // 나쁜 예 let loginViewModel = LoginViewModel()
-
“프로토콜을 채택하는 객체”를 초기화
protocol Coordinator { } class AuthCooordinator: Coordinator { } // 좋은 예 let childCoordinator: [Coordinator]
-
enum의 각 case에는 lowerCamelCase를 사용합니다.
// 좋은 예 enum Result { case .success case .failure } // 나쁜 예 enum Result { case .Success case .Failure }
-
나열을 하지 않고 무조건 띄어쓰기 한다.
// 좋은 예 enum Alphabet { case a case b case c case d } // 나쁜 예 enum Alphabet { case a, b, c, d }
-
약어로 시작하는 경우 소문자로 표기하고, 그 외의 경우에는 항상 대문자로 표기합니다.
// 좋은 예 let userID: String? let id: String? let html: String? let websiteURL: URL? let urlString: String? // 나쁜 예 let userId: Int? let HTML: String? let websiteUrl: NSURL? let URLString: String?
추상화를 위한 프로토콜은 객체를 선언할 때 채택한다. (extension에는 추가적인 프로토콜만 채택한다)
protocol NetworkManager {
//
}
class MyNetworkManager: NetworkManager {
//
}
-
파일 하나에 프로토콜 추가 선언 시 프로토콜 파일을 분리해서 작성한다.
protocol HomeViewControllerFlowDelegate { } class HomeViewController: HomeViewControllerFlowDelegate { }
-
추가적인 프로토콜을 채택할 때 사용한다.
extension HomeViewController: UICollectionViewDelegateFlowLayout { }
-
기존 Swift 타입에 기능을 추가할 때 사용한다. 주석을 작성할 것
extension String { // }
-
extension 이후 한 줄 띄우고 선언을 한다. (class와 동일)
주석을 작성할 수 있으면 한다.
ViewController 에 여러 개의 기능이 있는 경우 연관된 기능끼리 // MARK:
로 분리해준다.
// MARK:
위, 아래 줄은 공백 줄이다.
// MARK: - Static Methods
는 static method가 없을 경우 작성하지 않는다.
class HomeViewController: UIViewController {
typealias a = String
// MARK: - Properties
var a = 1
// MARK: - Static Methods (없을 경우 작성하지 않는다.)
// MARK: - Life Cycle
func viewDidLoad() { }
// MARK: - Methods
}
// MARK: - Properties
// MARK: -
// MARK: - Life Cycle
// MARL: - UI & Layout
// MARK: - Methods
// > setUI, registerXib, assignDelegate 등
// MARK: - Network
> 네트워크 목적을 가진 함수들
// TODO: -
> 해야하는 것
// FIXME: -
> 고칠 예정
버전 | 날짜 | 업데이트 내용 |
---|---|---|
1.0 | 07/ 10 | 초기 컨벤션 |
-
스타일쉐어 스타일 가이드
[swift-style-guide/README.md at master · StyleShare/swift-style-guide](https://github.com/StyleShare/swift-style-guide/blob/master/README.md)
-
구글 Swift Guide
-
노수진님 글
-
Swift Lint