이 둘의 관계는 너무 강하게 연결되면 리팩토링이 어렵고 재사용이 보통 어려워지는 문제가 생깁니다. View에 해당하는 코드에서 인자로 Model을 받는것은 좋지 않습니다.
만약 정적인 View가 많은 경우, Configurator(시스템의 특정 부분 구성을 지원하기 위한 작은 소프트웨어 응용 프로그램)는 한 번만 구성되면 충분하다. 렌더링 하는 모델은 수명동안 변경되지 않기 때문이다.
예를 들어, Message를 렌더링 하는 경우에, 여러 사용사례가 존재할 수 있다. 이 때 코드를 복제할 필요가 없게 하려면 다음 코드와 같다.
class MessageViewFactory {
func makeView(for message: Message) -> UIView {
let view = TextView()
view.title.text = message.title
view.textLabel.text = message.text
view.imageView.image = message.icon
return view
}
}
위 코드는, 특수화된 클래스 대신, 일반화된 TextView 클래스를 사용하고, 외부에서 사용하는 정확한 View Class를 숨겼다. (캡슐화)
SwiftUI에서 Factory 패턴의 사용 예시는 다음과 같다.
struct HomeViewFactory{
var database: Database
var networkController: NetworkController
...
func makeCatalogView() -> some View {
let viewModel = catalogViewModel(db: database, ...)
return catalogView(viewModel: viewModel)
}
func makeProfileView() -> some View {
...
}
}
스위프트 5.1에서 나온, 불투명한 반환 유형인 some
에 대해서 알아보겠습니다. 지정된 프로토콜을 준수하는 값은 자유롭게 반환이 가능합니다. 이는 주로 SwiftUI에서 뷰를 반환하는 경우 사용합니다
struct ContentView: View {
var body: some View {
...
}
}
이는 함수, 서브스크립트, 계산 프로퍼티를 불투명한 봔환 유형으로 선언을 가능하게 해줍니다. 이것의 의미는 연결된 유형 or 참조가 있는 프로토콜 같은 비제네릭 유형이나, 구체적인 유형인것 처럼 반환할 수 있음을 의미합니다.
구현을 호출하는 기타 코드는 반환값으로 작업시 해당 프로토콜의 모든 속성, 메서드를 계속 사용이 가능합니다. 이는 코드에 API의 구체적인 요소를 노출하지 않아 캡슐화를 깨지 않습니다.
그리고, 유연성이라는 좋은 부작용을 제공합니다. 내부에서 사용되는 정확한 반환 유형을 변경하기 위해, 더 이상 공개 API를 수정할 필요가 없기 때문입니다.
이 의미는 특히 SwiftUI에서 중요합니다. 유지 가능한 View 코드를 작성하는 핵심 부분은 UI의 다양한부분을 별도의 더 작은 부분으로 나누어 리팩토링 하는것이기 때문입니다.
VStack, HStack, Group등을 사용하면 단순히 클로저 내에서 새 인스턴스를 생성하여 여러 View를 그룹화 할 수 있다.(return 생략 가능) 함수 빌더 사용시 클로저를 사용하여 빌더 패턴
을 사용이 가능하다. 만약의 이 기능이 없다면
anyView {
let image: UIImage
let title: String
let subtitle: String
var body: some View {
var builder = VStackBuilder()
builder.add(Image(uiImage: image))
builder.add...
builder.add...
return builder.build()
}
}
위 코드 처럼 사용을 해야만 한다.
이것의 핵심 아이디어는 객체 설정 프로세스가 객체 자체가 아닌 전용 빌더 유형에 의해 수행이 된다는 것이다. 예를 들어, title, text, image가 있는 ArticleView()를 설정한다고 할 때,
let view = ArticleView()
view.titleLabel.text = article.title
view.subTitle.text = article.subtitle
view.ImageView.image = article.image
위 코드로 렌더링을 하고, 속성을 설정한 하위 보기를 노출하는게 일반적인 방법이다.
만약 빌더 패턴을 사용한다면,
let view = ArticleViewBuilder)(
.withTitle(article.title)
.withsubTitle(article.subtitle)
.withImage(article.image)
.build()
)
위와 같이, ArticleView의 하위 뷰에 접근하는 대신, Builder를 사용하여 일련의 연결된 메서드를 통해 모든 속성을 설정하고, build()로 마지막에 호출하여 인스턴스 생성을 완료합니다.
연결된 메서드는 각각 자기 자신을 반환하기 때문에, 이러한 과정이 가능하다
처음과 비교시, 여러 공개된 코드를 private로 만드는 장점이 존재한다.
참고
https://www.swiftbysundell.com/articles/using-the-builder-pattern-in-swift/