Skip to content

Instantly share code, notes, and snippets.

@cojoj
Last active January 10, 2019 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cojoj/56d73a1b57a86b7c45277a3e4152ed0b to your computer and use it in GitHub Desktop.
Save cojoj/56d73a1b57a86b7c45277a3e4152ed0b to your computer and use it in GitHub Desktop.
This is a test MVVHP architecture. It's just a proof of concept.
import UIKit
// We need a definition of a Presenter.
// It'll have associated type for handler, propety of this type and a possibility to
// bind a View Handler.
protocol Presenter {
associatedtype ViewHandler: BaseViewHandler
var viewHandler: ViewHandler? { get set }
mutating func bind(handler: ViewHandler)
}
// Our basic requirements for View Handlers are defined in this protocol.
// What is ViewHandler? It's a way proxy for communicating Presenter changes to the View.
protocol BaseViewHandler {
func setTitle(_ title: String)
func showStatus(_ status: String, animated: Bool)
func hideStatus(animated: Bool)
}
// We're creating a Base View Controller which we'll always subclass, so we can put there some stuff
// like property for a Presenter etc.
class BaseViewController<P: Presenter>: UIViewController {
var presenter: P
init(presenter: P) {
self.presenter = presenter
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not Implemented")
}
override func loadView() {
self.view = UIView()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
// We make our Base View Controller conform to Base View Handler, so it can react to some top-level
// actions common to all views.
extension BaseViewController: BaseViewHandler {
func setTitle(_ title: String) {
}
func showStatus(_ status: String, animated: Bool) {
}
func hideStatus(animated: Bool) {
}
}
// Our classes
// This is a specific View Handler decalration with a methods related to specific View/VC
protocol TestViewHandler: BaseViewHandler {
func testMethod()
}
// Impelmentation of a specialized VC.
// As you can see, it has access to it's dedicated Presenter
final class TestViewController: BaseViewController<TestPresenter> {
override func viewDidLoad() {
super.viewDidLoad()
presenter.calucateSomething()
print(presenter.something)
}
}
// We're making this VC being a ViewHandler. We could pass here everything, but the problem is that
// we don't want to reimplement BaseViewHandler's methods.
extension TestViewController: TestViewHandler {
func testMethod() {
}
}
// I know...
// Tight coupling
// Tight coupling
// Tight coupling
// Tight coupling
// ... But only as a typealias 🤗
// Thanks to this we have only one ViewHandler. Of course, we can have two of them and pass here
struct TestPresenter: Presenter {
typealias ViewHandler = TestViewController
var viewHandler: ViewHandler?
mutating func bind(handler: ViewHandler) {
self.viewHandler = handler
}
let something = "Something"
func calucateSomething() -> Int {
return 2
}
}
// Usage
var preseter = TestPresenter()
let vc = TestViewController(presenter: preseter)
preseter.bind(handler: vc)
// There's also a question, where's View Model in all this.
// View Model is just a way of transforming data into digestable format for other views.
// In the end it's just getting a data from Presenter, modifying it a bit and passing somewhere else.
// Do we need an object (struct) for this? I don't think so. I think we can just create
// a protocol with definitions and make Presenter conform to it.
// We'd skip another initializations and data juggling.
protocol DetailedTestViewModel {
var uppercasedSomething: String { get }
}
extension TestPresenter: DetailedTestViewModel {
var uppercasedSomething: String {
return something.uppercased()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment