MVVM + Combine 간단 예제

MVVM + RxSwift 이해하기 쉬운 예제
MVVM + KVO 이해하기 쉬운 예제

위 예제와 동일한 아주 간단한 mvvm + combine 예제이다.
쉬운 예제부터 이해하면 왜 사용하는지를 알고 왜 사용하는지를 알면
코드를 분리하고 유지보수하기 쉬운 코드를 작성하려고 노력하고 좀 더 발전된 방향으로…

View 에서는 viewModel의 label 을 구독하여 갱신이 필요한 시점에서 뷰의 label을 갱신한다.
ViewModel 에서는 View의 label 갱신을 위해 필요한 작업 이후 변경 사항을 저장하여 알린다.

// ViewController
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        if let v: View = UIView.loadFromNib() {
            v.add(self.view)
        }
    }
}
// View
class View: UIView {
    @IBOutlet var label: UILabel!
    
    private let viewModel = ViewModel()
    private var cancellables = Set<AnyCancellable>()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
                
        // 스케쥴러 main
        viewModel.$label.receive(on: DispatchQueue.main).sink { [weak self] val in
            guard let self = self else { return }
            self.label.text = val
        }.store(in: &cancellables)
        
        viewModel.loadLabel()
    }
    
    func add(_ superView: UIView) {
        superView.addSubview(self)
        self.translatesAutoresizingMaskIntoConstraints = false
        
        var constraints = [NSLayoutConstraint]()
        constraints.append(superView.topAnchor.constraint(equalTo: self.topAnchor))
        constraints.append(superView.leadingAnchor.constraint(equalTo: self.leadingAnchor))
        constraints.append(superView.trailingAnchor.constraint(equalTo: self.trailingAnchor))
        constraints.append(superView.bottomAnchor.constraint(equalTo: self.bottomAnchor))
        NSLayoutConstraint.activate(constraints)
    }
}

extension UIView {
    class func loadFromNib<T>() -> T? {
        let identifier = String(describing: T.self)
        let view = Bundle.main.loadNibNamed(identifier, owner: self, options: nil)?.first
        return view as? T
    }
}
// Model
struct Model {
    var label: String = "model"
}
// ViewModel
class ViewModel {
    private var m = Model()
    @Published var label = "init"

    init() {
    }
    
    func changed() {
        self.label = self.m.label
    }
    
    func loadLabel() {
        DispatchQueue.global().asyncAfter(deadline: .now()+2) {
            self.m.label = "value from server or app db"
            self.changed()
        }
    }
}

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다