Skip to content

SwiftUI Code Convention

Joonyong Ji edited this page Jan 4, 2024 · 5 revisions

SwiftUI Code Convention

해당 문서는 채널톡 ios-convention-guide를 참고하였습니다.

개행 규칙

  • body 내부의 View를 여러 개 생성할 때에는 개행을 둔다.
    • 단, body의 처음과 끝은 개행을 두지 않는다.
      var body: some View {
        Text("Hello")
          .frame(width: 200, height: 50, alignment: .center)
    
        Spacer()
    
        Text("World")
      }

SubView 선언 규칙

  • @ViewBuilder 함수보다 struct를 활용한 SubView선언을 지향한다.
    • 부모 뷰에 있는 @State를 변경하기 위해 @ViewBuilder를 통해 함수 선언이 가능하지만, 뷰 계층 간 위계를 명확히 하기 위해 @Binding을 사용한다.
        // Preferred
        struct SubView: View {
          @Binding private var isFavorited: Bool
      
          init(isFavorited: Binding<Bool>) {
            self._isFavorited = isFavorited
          }
      
          var body: some View {
            ...
          }
        }
      
        // Not Preferred
        @ViewBuilder 
        public func subView(title: String) -> some View {
          VStack {
            ...
            Button(title) { self.isFavorited.toggle() }
          }
        }

네이밍(Naming)

  • 명확히 View가 아닌 컴포넌트에 대해서는 suffix(~View)를 사용하지 않는다.
      // Preferred
      struct ChannelButton: View {
        ...
      }
    
      struct ChannelSwitch: View {
        ...
      }
    
      // Not Preferred
      struct ChannelButtonView: View {
        ...
      }
    
      struct ChannelSwitchView: View {
        ...
      }

뷰 크기(View frame)

  • 뷰 크기를 확장할 때에는 Spacer()대신 .frame()을 사용한다.

    • 수직 확장은 maxHeight: .infinity, 수평 확장은 maxWidth: .infinity를 사용한다.
    // Preferred
      HStack {
        ...
      } 
      .frame(maxWidth: .infinity, alignment: .leading)
    
     // Not Preferred
      HStack {
        ...
        Spacer()
      }

Custom View Modifier

  • Custom View Modifier 적용 시, 아래와 같이 extension을 활용해 해당 modifier에 접근한다.
      // Preferred
      struct ContentView: View {
        var body: some View {
          Text("Eddy")
            .asPrimaryCaption()
        }
      }
    
      extension View {
        func asPrimaryCaption() -> some View {
            modifier(PrimaryCaptionText())
          }
      }
    
      // Not Preferred
      struct ContentView: View {
        var body: some View {
          Text("Eddy")
            .modifier(asPrimaryCaption())
        }
      }

속성(Property)

  • 필수 Propety는 init시점에 적용한다. 옵셔널한 Property는 ViewModifier함수로 참조한다.
    • ViewModifier함수 생성 시, Property에 대한 불필요한 접근을 최소화하기 위해 접근제어자를 활용한다.
      // Required Action
      struct ChannelButton: View {
        private let title: String
        private let action: () -> ()
    
        init(title: String, action: @escaping () -> ()) {
          self.title = title
          self.action = action
        }
        ...
      }
    
      // Optional Action
      struct ChannelView: View {
        private let title: String
        private let optionalAction: (() -> ())?
    
        init(title: String) {
          self.title = title
        }
        ...
    
        func optionalAction(_ optionalAction: (() -> ())?) -> Self {
          var view = self
          view.optionalAction = optionalAction
          return view
        }
      }