관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 2. @resultBuilder 이해하기 - 응용하여 깔끔한 코드 만들기 본문

iOS 응용 (swift)

[iOS - swift] 2. @resultBuilder 이해하기 - 응용하여 깔끔한 코드 만들기

jake-kim 2023. 8. 18. 23:28

1. @resultBuilder 이해하기 - 만들어진 이유

2. @resultBuilder 이해하기 - 응용하여 깔끔한 코드 만들기

3. @resultBuilder 이해하기 - 선언형 UI 만들기 (Declarative UI)

 

@resultBuiler 개념 복습

  • 이름이 result builder인 이유?
    • 결과를 만들어주는 역할을 담당
    • 결과를 만들어준다는 의미는 lazy var로 선언것처럼 코드 블록 내에서 여러 개의 값을 취합하여 return 키워드 없이 하나의 결과로 반환하는 것을 도와주는 기능
  • 목적: 빌딩을 간소화하는 것

SwiftUI에서 @resultBuilder 사용

  • result buildr의 기능: 여러 개의 값을 취합하여 return 키워드 없이 하나의 결과로 반환해주는 것
    • 따로 function을 만들어서 분리할 필요 없이 SwiftUI에서 뷰를 선언형으로 만들기가 가능

ex) SwiftUI에서 VStack안에 여러개의 Text를 넣을 때 ForEach를 사용하는데 이를 따로 함수로 빼지 않고 바로 Text()를 ForEach안에 선언하여 사용이 가능

  • 만약 @resultBuilder 속성을 사용할 수 없다면 ForEach문 자체를 하나의 함수로 빼고 이를 사용하는 쪽에서 호출하여 반환된 Text()뷰들을 사용
struct ContentView: View {
    var body: some View {
        ScrollView {
            if #available(macOS 11.0, iOS 14.0, *) {
                LazyVStack {
                    ForEach(1...1000, id: \.self) { value in
                        Text("Row \(value)")
                    }
                }
            } else {
                VStack {
                    ForEach(1...1000, id: \.self) { value in
                        Text("Row \(value)")
                    }
                }
            }
        }
    }
}

@resultBuilder 응용

  • @resultBuilder 키워드를 사용하고 Builder enum와 buildBlock(_) 함수를 정의
@resultBuilder
enum Builder<T> {
    static func buildBlock(_ component: T) -> T { component }
}
  • buildBlock에서 if-else도 사용할 수 있어야하므로, buildEither(first:)buildEither(second:)를 정의
@resultBuilder
enum Builder<T> {
    static func buildBlock(_ component: T) -> T { component }
    static func buildEither(first component: T) -> T { component }
    static func buildEither(second component: T) -> T { component }
}
  • 사용하는 쪽에서는 @Builder를 파라미터로 받아서 사용
// UIViewController.swift

let label = buildLabel {
    if datas.count == 0 {
        UILabel()
    } else {
        UILabel()
    }
}

func buildLabel(@Builder<UILabel> _ closure: () -> UILabel) -> UILabel {
    closure()
}
  • buildLabel 함수를 더욱 추상화하여 일일이 이런 함수를 정의하지 않고 바로 쓸 수 있도록 전역으로 선언
func builder<T>(@Builder<T> _ closure: () -> T) -> T { closure() }

(사용하는쪽)

  • return 키워드 없이 간소하게 사용 가능
let value2 = builder {
    if datas.count == 0 {
        1
    } else {
        2
    }
}

let value3 = builder {
    if datas.count == 0 {
        UILabel()
    } else {
        UILabel()
    }
}

 

* 전체 코드: https://github.com/JK0369/ExResultBuilder2

 

* 참고

https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#the-result-builder-transform

Comments