Many people are familiar with (Model-View-Controller) and its challenges, yet only a few discuss how to address them effectively. In this article, we will delve into the vital components of : the and the . MVC MVC View Controller Typically, a is employed as both a and a , resulting in increased code within the . So, let's explore the possibility of separating the into distinct and entities. UIViewController View Controller UIViewController UIViewController View Controller Let's begin by examining the lifecycle of a . UIViewController One of the methods in this lifecycle is . According to Apple's documentation: loadView “You should never call this method directly. The view controller calls this method when its viewproperty is requested but is currently nil. This method loads or creates a view and assigns it to the view property.” This insight allows us to segregate the and by utilizing the method. First, we will define a protocol that encapsulates the functionality of our future . View Controller loadView View protocol AbstractTestView: UIView { func bind(_ model: String) } Now, let's proceed to implement the protocol for our . View final class TestView: UIView, AbstractTestView { private let content: UILabel = { $0.numberOfLines = 0 $0.textColor = .red return $0 }(UILabel()) init() { super.init(frame: .zero) layout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func bind(_ model: String) { content.text = model } private func layout() { NSLayoutConstraint.activate([ content.centerXAnchor.constraint(equalTo: centerXAnchor), content.centerYAnchor.constraint(equalTo: centerYAnchor) ]) } } Finally, we can implement our , which will communicate with the through the protocol. Controller View final class TestVC: UIViewController { private let mainView: AbstractTestView init(view: AbstractTestView) { mainView = view super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { view = mainView } override func viewDidLoad() { super.viewDidLoad() mainView.bind("Hello from MVC!") } } For more intricate elements like tables, the business logic will remain within our . Here's an example: Controller extension TestVC: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { } } Now, let's connect our with the . Controller View let vc = TestVC(view: TestView()) It is crucial to maintain a clear separation between business logic and the view. Continuously ask yourself: "Does this piece of code handle the display of elements or work with data?" Initially, this may pose a challenge, but with time, it will become second nature.