관리 메뉴

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

[iOS - swift] Builder 패턴 사용 방법 (공통 컴포넌트 수정, 추가 쉽게하기) 본문

iOS 응용 (swift)

[iOS - swift] Builder 패턴 사용 방법 (공통 컴포넌트 수정, 추가 쉽게하기)

jake-kim 2023. 4. 12. 01:27

Builder 패턴이란?

  • Builder 패턴은 객체를 생성하는 것을 추상화하여, 이 객체를 사용하는쪽에서 Builder라는 별도의 컴포넌트를 사용하여 단계적으로 필요한 옵션을 set 하면서 인스턴스를 만드는 방식
  • 사용하는쪽에서 필요한 옵션을 set하고난 후 인스턴스를 얻을수 있어서, 여러곳에서 옵션을 유연하게 바꾸며 사용하기 쉽고 사용하는쪽에서 최초에 인스턴스를 만들때만 내부 값들을 변경하므로, 인스턴스가 만들어진 후에는 외부에서 바꾸어주는 상태가 없어서 새로운 기능에 대해서도 수정이나 추가가 용이
  • 팝업과 같은 공통 컴포넌트에 builder 패턴을 적용하면 사용하는쪽에서 필요한 옵션을 set하면서 여러곳에서 사용하기가 용이

팝업에 Builder 패턴 사용해보기

* 예제 starter 코드(아래에서 위로 올라오는 커스텀 팝업): https://ios-development.tistory.com/1295

 

[iOS - swift] 아래에서 위로 올라오는 팝업 구현 방법 (커스텀 팝업, viewController 팝업 형태, present, co

커스텀 팝업 ViewController * UIView 를 이용한 커스텀 팝업은 이전 포스팅 글 참고 커스텀 팝업 사용하는 쪽에서 present로 접근하여 사용할 수 있게끔 UIViewController를 상속받아서 구현되어야 하는 형태

ios-development.tistory.com

  • 팝업 형태
    • 문제점: label의 색상, button의 색상, 문구 등 사용하는쪽에서 쉽게 변경해서 쓸 수 있기가 어려움
    • Builder패턴을 통해 리펙토링 해보기
final class CustomPopupViewController: DimmedViewController {
    private let containerView: UIView = {
        let view = UIView()
        view.backgroundColor = .white
        return view
    }()
    private let stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        view.spacing = 12
        view.backgroundColor = .white
        return view
    }()
    private let label: UILabel = {
        let label = UILabel()
        label.textColor = .black
        label.text = "iOS 앱 개발 알아가기 jake"
        return label
    }()
    private let closeButton: UIButton = {
        let button = UIButton()
        button.setTitle("닫기", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.setTitleColor(.blue, for: .highlighted)
        button.addTarget(self, action: #selector(close), for: .touchUpInside)
        return button
    }()
    
    override init() {
        super.init()
        setUp()
    }

    required init?(coder: NSCoder) {
        fatalError()
    }

    private func setUp() {
        view.addSubview(containerView)
        containerView.addSubview(stackView)
        stackView.addArrangedSubview(label)
        stackView.addArrangedSubview(closeButton)
        
        containerView.snp.makeConstraints {
            $0.leading.trailing.equalToSuperview().inset(20)
            $0.centerY.equalToSuperview()
        }
        stackView.snp.makeConstraints {
            $0.center.equalToSuperview()
            $0.leading.top.greaterThanOrEqualToSuperview()
            $0.bottom.trailing.lessThanOrEqualToSuperview()
        }
    }
    
    @objc private func close() {
        dismiss(animated: true)
    }
}
  • Builder 생성
    • Builder를 만들기 전에 CustomPopupViewController에서 값을 입력받으면 UI를 변경하는 computed property 선언
// CustomPopupViewController.swift

var labelText: String? {
    get { label.text }
    set { label.text = newValue }
}
var labelColor: UIColor {
    get { label.textColor }
    set { label.textColor = newValue }
}

var buttonText: String? {
    get { label.text }
    set { label.text = newValue }
}
var buttonNormalColor: UIColor? {
    get { closeButton.titleColor(for: .normal) }
    set { closeButton.setTitleColor(newValue, for: .normal) }
}
var buttonHighlightedColor: UIColor? {
    get { closeButton.titleColor(for: .highlighted) }
    set { closeButton.setTitleColor(newValue, for: .highlighted) }
}
  • builder 구현
    • set 메소드를 호출하면 자기 자신, Self를 리턴하여 사용하는쪽에서 *선언적으로 프로그래밍할 수 있도록 구현
    • *선언적: 어떻게에 관한 내용을 감추고 무엇을 하는것에 초첨을 두는 것
class CustomPopupBuilder {
    private var labelText = "iOS 앱 개발 알아가기 jake"
    private var labelColor = UIColor.black
    private var closeButtonText = "닫기"
    private var closeButtonNormalColor = UIColor.systemBlue
    private var closeButtonHighlightedColor = UIColor.blue
    
    func set(labelText: String) -> Self {
        self.labelText = labelText
        return self
    }
    
    func set(labelColor: UIColor) -> Self {
        self.labelColor = labelColor
        return self
    }
    
    func set(buttonText: String) -> Self {
        self.closeButtonText = buttonText
        return self
    }
    
    func set(buttonNormalColor: UIColor) -> Self {
        self.closeButtonNormalColor = buttonNormalColor
        return self
    }
    
    func set(buttonHighlightedColor: UIColor) -> Self {
        self.closeButtonHighlightedColor = buttonHighlightedColor
        return self
    }
    
    func build() -> CustomPopupViewController {
        let vc = CustomPopupViewController()
        vc.labelText = labelText
        vc.labelColor = labelColor
        vc.buttonText = closeButtonText
        vc.buttonNormalColor = closeButtonNormalColor
        vc.buttonHighlightedColor = closeButtonHighlightedColor
        return vc
    }
}
  • 사용하는쪽
    • 버튼을 탭 했을때 빌더를 통해서 팝업을 만들고 사용
// ViewController.swift

@objc private func tap() {
    let popupVC = CustomPopupBuilder()
        .set(labelText: "cutom label text")
        .set(buttonNormalColor: .orange)
        .build()
    present(popupVC, animated: true)
}

  • 만약 CustomPopupViewController의 containerView.background 색상을 변경하고 싶은 경우, CustomPopupViewController에 아래 코드를 추가
// CustomPopupViewController.swift

var contentBackgroundColor: UIColor? {
    get { containerView.backgroundColor }
    set { containerView.backgroundColor = newValue }
}
CustomPopupBuilder.swift

class CustomPopupBuilder {
    private var contentBackgroundColor = UIColor.white
    
    ...
    
    
    func set(contentBackgroundColor: UIColor) -> Self {
        self.contentBackgroundColor = contentBackgroundColor
        return self
    }
    
    ...
}
// ViewController.swift

let popupVC = CustomPopupBuilder()
    .set(contentBackgroundColor: .brown) // <-
    .set(labelText: "cutom label text")
    .set(buttonNormalColor: .orange)
    .build()
present(popupVC, animated: true)

(완성)

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

* 참고

https://refactoring.guru/design-patterns/builder

Comments