Skip to content

Capstone-Design-Project-2-I-Catch/I-Catch-iOS-Style-Guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

I-Catch-iOS-Style-Guide

다른 사람의 코드를 읽을 때 가독성을 높여주며, 내가 코드를 작성할 때 애매한 부분을 제거해줘 생산성 향상에 도움을 줍니다.
협업시 일관성있는 코드를 작성할 수 있어서 코드의 일관성 유지에 좋습니다.
또 다른 사람이 작성한 코드를 읽을 때 코드의 문법보다 로직에 집중할 수 있는 장점이 있습니다.

목차

  1. 정확성
  2. 네이밍
    1. 변수와 상수
    2. 약어
    3. 함수
    4. 열거형
    5. 구조체와 클래스
    6. 프로토콜
    7. 델리게이트
  3. 주석
  4. 띄어쓰기
  5. 코드 구성
    1. 미사용 코드
  6. 접근제어자
  7. 클래스와 스트럭트
  8. 함수호출
  9. 클로져
    1. 후행 클로저 축약
    2. 다중 후행 클로져
  10. 타입
    1. 타입 추론
    2. 타입 어노테이션
  11. 메모리 관리
  12. 파일관리
  13. 뷰 생성 방법
  14. Extension
    1. Color
    2. Font
  15. Component

정확성

경고(warnings)없이 컴파일되도록 노력해야 합니다. 이 규칙은 문자열 원문(literals) 대신 #selector타입을 사용하는 것과 같이 많은 스타일을 결정하는 것을 제공합니다.

네이밍

변수와 상수

  • 일반변수 / 상수인 경우 따로 접두사를 붙이지 않습니다.

  • 변수 이름은 lowerCamelCase를 사용해주세요.

  • 배열과 같이 복수의 의미를 담고있는 변수라면 끝에 s를 붙여서 사용해주세요.

    예제코드
    • Good ✅
      var categories: [String]
      var person: Person
      var isShowing: Bool
    • Bad ❌
      var category: [String]
      var show: Bool
  • static 상수인 경우 앞에 k를 붙여줍니다

    예제코드
    • Good ✅
      static let kMaximumNumberOfLines = 3
    • Bad ❌
      static let maximumNumberOfLines = 3

약어

  • 약어로 시작하는 경우 소문자로 표기하고, 그 외 경우에는 항상 대문자로 표기합니다.

    예제코드
    • Good ✅
      let userID: Int?
      let html: String?
      let websiteURL: URL?
      let urlString: String?
    • Bad ❌
      let userId: Int?
      let HTML: String?
      let websiteUrl: NSURL?
      let URLString: String?

함수

  • 함수 이름에는 lowerCamelCase를 사용해주세요.

  • 함수는 일반적으로 동사원형으로 시작해주세요.

  • Event-Handling 함수의 경우 (조동사 + 동사원형)으로 시작해주세요. 주어는 유추 가능하다면, 생략 가능합니다.

    • will은 특정 행위가 일어나기 직전을 의미합니다.
    • did는 특정 행위가 일어난 직후를 의미합니다.
    예제코드
    • Good ✅
      class AcademyViewController {
      
          private func didFinishSession() {
              // ...
          }
      
          private func willFinishSession() {
              // ...
          }
      
          private func scheduleDidChange() {
              // ...
          }
      }
    • Bad ❌
      class AcademyViewController {
      
          private func handleSessionEnd() {
              // ...
          }
      
          private func finishSession() {
              // ...
          }
      
          private func scheduleChanged() {
              // ...
          }
      }
  • 데이터를 가져오는 함수의 경우, get 사용을 지양하고 request, fetch을 적절하게 사용해주세요.

    • request : 에러가 발생하거나, 실패할 수 있는 비동기 작업에 사용합니다. 예를 들어, http 통신을 통해 값을 요청하는 경우가 이에 해당합니다.
    • fetch : 요청이 실패하지 않고 결과를 바로 반환할 때 사용합니다. 예를 들어, data를 찾고자 하는 모든 행위를 할 때가 이에 해당합니다.
    예제코드
    • Good ✅
      func reqeustData(for user: User) -> Data?
      func fetchData(for user: User) -> Data
    • Bad ❌
      func getData(for user: User) -> Data?

열거형

  • 열거형의 이름은 UpperCamelCase를 사용해주세요.

  • 열거형의 각 case에는 lowerCamelCase를 사용해주세요.

    예제코드
    • Good ✅
      enum Result {
        case .success
        case .failure
      }
    • Bad ❌
      enum result {
        case .Success
        case .Failure
      }

구조체와 클래스

  • 구조체와 클래스의 이름은 UpperCamelCase를 사용해주세요.

  • 구조체와 함수의 이름 앞에 prefix를 붙이지 말아주세요.

  • 구조체와 클래스의 프로퍼티 및 메소드는 lowerCamelCase를 사용해주세요.

    예제코드
    • Good ✅
      struct LeftRectangle {
          var width: Int
          var height: Int
      
          func drawRectangle() {
              // ...
          }
      }
      class Mentee {
          let id: String
          let session: String
          var group: Int
          var team: Int
      
          func callOutMentor() {
              // ...
          }
      }
    • Bad ❌
      struct rwRightRectangle {
          var Width: Int
          var Height: Int
      
          func DrawRectangle() {
              // ...
          }
      }
      class rwMentor {
          let Id: String
          var Group: Int
      
          func GiveAdvice() {
              // ...
          }
      }

프로토콜

  • 구조를 나타내는 프로토콜은 명사로 작성해야합니다.

  • 무언가를 할 수 있음(능력)을 설명하는 프로토콜은 형용사로 작성해야합니다.

    예제코드
    • Good ✅

      protocol Car {
          var speed: Int { get set }
          var name: String { get }
      
          func speedUp(speed: Int) -> Bool
      }
      protocol Drivable {
          func accelerate(speed: Int) -> ()
          func slowDown(speed: Int) -> ()
      }
    • Bad ❌

      protocol Drivable {
          var speed: Int { get set }
          var name: String { get }
      
          func speedUp(speed: Int) -> Bool
          func accelerate(speed: Int) -> ()
          func slowDown(speed: Int) -> ()
      }
  • 프로토콜을 적용할 때는 extension을 만들어서 관련된 매소드를 모아둡니다.

    예제코드
    • Good ✅
      final class MyViewController: UIViewController {
      // ...
      }
      
      // MARK: - UITableViewDataSource
      extension MyViewController: UITableViewDataSource {
      // ...
      }
      
      // MARK: - UITableViewDelegate
      extension MyViewController: UITableViewDelegate {
      // ...
      }
    • Bad ❌
     final class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // ...
    }

델리게이트

  • protocol을 이용해 delegate 패턴을 구현합니다.

  • 함수의 첫번째 인자는 생략가능한 델리게이트의 소스 객체를 사용합니다.

    예제코드
    • Good ✅
          // 델리게이트의 소스 객체만을 메서드의 인자로 받는 경우
          protocol UserScrollViewDelegate {
              func scrollViewDidScroll(_ scrollView: UIScrollView)
              func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool
          }
      
          // 델리게이트의 소스 객체 다음에 추가적인 인자를 받는 경우
          protocol UserTableViewDelegate {
              func tableView(
                  _ tableView: UITableView,
                  willDisplayCell cell: Cell,
                  cellForRowAt indexPath: IndexPath)
              )
              func tableView(
                  _ tableView: UITableView,
                  numberOfRowsInSection section: Int) -> Int
              )
          }
    • Bad ❌
          protocol UserViewDelegate {
              // 인자를 생략한 경우
              func didScroll()
              // 델리게이트의 소스 객체를 인수로 사용하지 않은 경우
              func willDisplay(cell: Cell)
              // 함수명을 UpperCamelCase로 작성한 경우, 다른 클래스가 존재하면 컴파일 오류 발생
              func UserScrollView(_ scrollView: UIScrollView)
          }  
  • Delegate 메서드는 프로토콜명으로 네임스페이스를 구분합니다.

    예제코드
    • Good ✅
      protocol UserCellDelegate {
          func userCellDidSetProfileImage(_ cell: UserCell)
          func userCell(_ cell: UserCell, didTapFollowButtonWith user: User)
      }
    • Bad ❌
      protocol UserCellDelegate {
          func didSetProfileImage()
          func followPressed(user: User)
      
          // `UserCell`이라는 클래스가 존재할 경우 컴파일 에러 발생
          func UserCell(_ cell: UserCell, didTapFollowButtonWith user: User)
      }

주석

주석은 협업에 있어 가독성을 높이고 다른 사람의 코드를 이해하는 중요한 도구입니다.

  • ///를 사용하여 문서화에 사용되는 주석을 남깁니다.

  • 설명은 최대한 간결하고 핵심 요약에 집중해서 작성해주세요.

  • 함수와 메소드는 기본적으로 무엇을 하는지 무엇을 반환하는지 설명해주시고,
    널효과나 void 반환은 생략합니다.

  • 작성한 주석은 퀵헬프 메뉴에서 언제든지 조회가 가능합니다.

    예제코드
    • Good ✅

      /// 사용자 데이터를 추가합니다.
      /// - Parameter name: user fullname
      /// - Parameter age: user age
      func addData(name: String, age: Int) {
          // code to add data...
      }
      /// DB내 사용자 이름과 ID로 나이를 조회합니다.
      /// - Parameter ID: user ID
      /// - Parameter name: user fullname
      /// - Returns: user age
      func readData(ID: Int, name: String) {
          var age: Int
          // code to read data...
          return age
      }
    • Bad ❌

      // 사용자 데이터 추가
      func addData(name: String, age: Int) {
          // return void
      }
  • 연관된 코드가 있다면 // MARK:를 사용하여 코드영역을 구분지는것을 권장합니다.

  • Objective-C에서 제공하는 #pragma mark와 같은 기능으로, 연관된 코드와 그렇지 않은 코드를 구분할 때 사용합니다.

    예제코드
    • Example 💡
      // MARK: - Gryffindor
      let password = "Fotuna Major"
      struct Gryffindor {
          let harry: String
          let ron: String
          let hermione: String
      }
      
      // MARK: - Slytherin  
      class Slytherin {
          let voldemort: String
          let malfoy: String
          func deadlyCurse() {
              print("Avada Kedavra!")
          }
      }
  • 아직 개발이 완료되지 않은 코드가 있다면 TODO나 FIXME를 사용하여 체크하는 것도 좋습니다.

    예제코드
    • Example 💡
      // FIXME: - 버그 수정 필요
      public func buggyFunc() {
          // buggy code..
      }
      
      // TODO: - 문자열 인코딩 함수 작업 계획 
      private func todoFunc() {
          // tbd..
      }

들여쓰기

  • 인덴테이션은 스페이스바 4개를 기본으로 하되, 스페이스바 4개는 탭 1개의 역할을 합니다.

    예제코드
    • Good ✅
      func sayHiLeeo(isHappy: Bool) {
          if isHappy {
              print("Hi Leeo!")
          }
      }
    • Bad ❌
      func sayHiLeeo(isHappy: Bool) {
        if isHappy {
          print("Hi Leeo!")
        }
      }

띄어쓰기

  • 콜론(:)을 사용할 땐 콜론의 오른쪽으로 한 칸의 여백을 생성합니다. 콜론의 왼쪽은 공백없이 코드를 작성합니다.
    • Example 💡
      let leeo: HappyLeeo

클로져

후행 클로저 축약

  • 단일 후행 클로저의 경우에는 타입유추, 함수 라벨 생략, 소괄호 생략을 사용합니다.

    예제코드
    • Function

      func someFunctionThatTakesAClosure(closure: (Int) -> Void) {
            // function body goes here
      }
      
    • Good ✅

      someFunctionThatTakesAClosure { int in
          // trailing closure's body goes here
      }
    • Bad ❌

      someFunctionThatTakesAClosure(closure: { (arguInt: Int) -> Void)
          // function body goes here
      })

다중 후행 클로져

  • 함수 또는 메서드의 형식 매개변수에서 클로져들만을 실 매개변수로 받는 경우 함수 또는 메서드 호출 시 함수 또는 메서드의 소괄호, 첫 번째 실 매개변수의 라벨, 실 매개변수 사이의 콤마를 생략합니다.

    예제코드
    • Good ✅

      func doSomething(do: (String) -> Void, onSuccess: (Any) -> Void, onFailure: (Error) -> Void) {
          // function body
      }
      
      doSomething { something in
          // do closure
      } onSuccess: { result in
          // success closure
      } onFailure: { error in
          // failure closure
      }
    • Bad ❌

      func doSomething(do: (String) -> Void, onSuccess: (Any) -> Void, onFailure: (Error) -> Void) {
          // function body
      }
      
      doSomething (do: { something in
          // do closure
      }, onSuccess: { result in
          // success closure
      }, onFailure: { error in
          // failure closure
      })

타입

타입 추론

  • 컴팩트 코드를 선호하고 컴파일러가 단일 인스턴스의 상수나 변수의 타입을 추론하도록 합니다.

  • 필요한 경우 CGFloatInt64와 같은 경우는 특정 타입을 지정해줍니다.

    예제코드
    • Good ✅

      let apple = "Developer"
      let book1 = Book()
      let age = 25
      let frameWidth: CGFloat = 120
    • Bad ❌

      let apple: String = "Developer"
      let book1: Book = Book()
      let age: Int = 25

타입 어노테이션

  • 전체 제네릭 구문 Array<T>Dictionary<T: U> 보다는 단축 구문 [T], [T: U]를 사용합니다.

    예제코드
    • Good ✅

      var student: [String: String]?
      var students: [String]?
    • Bad ❌

      var student: Dictionary<String, String>?
      var students: Array<String>?
  • 빈 배열과 딕셔너리 선언 시, 타입을 명시하는 것을 선호합니다.

    예제코드
    • Good ✅

      var student: [String: String] = [:]
      var students: [String] = []
    • Bad ❌

      var student = [String: String]()
      var students = [String]()

메모리 관리

  • 메모리 누수의 원인이 되는 순환 참조가 일어나지 않도록 주의해주세요.

  • 객체 간의 관계를 분석하면서 weakunowned를 사용하여 순환 참조를 방지할 수 있습니다.

  • weak 참조 변수는 반드시 Optional 타입이어야 합니다.

    예제코드
    • Good ✅
      class ExampleClass {
          weak var example: ExmapleClass? = nil
          
          init(){
              print("init class")
          }
          
          deinit{
              print("deinit class")
          }
      }
      
      // 객체 내의 인스턴스가 서로를 가리키고 있지만, weak 참조를 선언했기에 순환 참조가 일어나지 않습니다.
      var ex1: ExampleClass? = ExampleClass()
      var ex2: ExampleClass? = ExampleClass()
      
      ex1?.example = ex2
      ex2?.example = ex1
      
      ex1 = nil
      ex2 = nil
      
      // 출력결과
      // init class
      // init class
      // deinit class
      // deinit class

파일관리

  • 파일 내에서 모듈 import를 알파벳순으로 지정하고 중복된 것들을 제거해주세요.

    예제코드
    • Good ✅
      import Alamofire
      import Foundation
      import SnapKit
    • Bad ❌
      import Foundation
      
      import SnapKit
      import Alamofire
      import Foundation
  • Computed propertiesproperty observers가 있는 property는 같은 종류의 선언 집합 끝에 나타나야 합니다.

    예제코드
    • Good ✅
      var gravity: CGFloat
      var atmosphere: Atmosphere {
          didSet {
              print("oh my god, the atmosphere changed")
          }
      }
    • Bad ❌
      var atmosphere: Atmosphere {
          didSet {
              print("oh my god, the atmosphere changed")
          }
      }
      var gravity: CGFloat

뷰 생성 방법

뷰를 생성하는 4가지 방법을 적절히 선택하여 코드를 구성하면 코드의 가독성과 재사용성을 높일 수 있습니다.

  • 새로운 파일을 만들어서 뷰 생성 대규모 앱에서 코드를 구성할때 유지보수성을 향상시키기 위해 뷰를 여러 파일로 나눌 수 있습니다. 이 경우, 각 파일은 해당 뷰의 책임과 역할에 따라 구성됩니다.

    예제코드
    // MyCustomView.swift
    import SwiftUI
    struct MyCustomView: View {
    var body: some View {
    // Your custom view's content
        }
    }
  • Private 함수로 만들어서 뷰 생성 뷰 내에서만 사용할 뷰를 생성하려면, 해당 뷰 내에 private 함수를 사용하여 내부적으로 사용되는 뷰를 생성할 수 있습니다.

    예제코드
    struct ParentView: View {
      var body: some View {
          VStack {
              // ... Some content ...
    
              // Private function creating a view
              createSubview()
              
              // ... More content ...
          }
      }
    
      private func createSubview() -> some View {
          // Your subview's content
      }
    }
  • Extension화 해서 뷰를 생성 뷰를 확장(extension)하여 새로운 메서드를 추가하는 방식으로도 뷰를 생성할 수 있습니다.

    예제코드
    extension View {
      func customStyledView() -> some View {
          // Return a custom-styled view
          }
      }
    struct ContentView: View {
      var body: some View {
          Text("Hello, World!")
              .customStyledView()
      }
    }
  • 컴포넌트화해서 뷰를 생성 반복적으로 사용되는 UI 요소를 재사용하기 위해 컴포넌트로 뷰를 생성하는 것이 좋습니다. 이 컴포넌트는 필요한 곳에서 사용되며, 필요에 따라 데이터를 주입하여 동적으로 표시할 수 있습니다.

    예제코드
    struct CustomButton: View {
      let title: String
      var body: some View {
          Button(action: {
              // Button action
          }) {
              Text(title)
                  .padding()
                  .foregroundColor(.white)
                  .background(Color.blue)
                  .cornerRadius(10)
          }
      }
    }
    struct ContentView: View {
      var body: some View {
          VStack {
              Text("Welcome")
              CustomButton(title: "Click Me")
          }
      }
    }
예제코드
- **Good ✅**
  ```swift
  
  ```
  • Bad ❌

Reference

About

I-Catch의 Swift 스타일 가이드

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published