Skip to content

Instantly share code, notes, and snippets.

@itolosa
Last active May 21, 2023 01:56
Show Gist options
  • Select an option

  • Save itolosa/75a790e63102ceb04104ac80d1e572c7 to your computer and use it in GitHub Desktop.

Select an option

Save itolosa/75a790e63102ceb04104ac80d1e572c7 to your computer and use it in GitHub Desktop.
Clean Architecture Notes

How to pass the ViewModel from the Presenter to the View?

Solution 1: Inject a shared reference

You could use a shared reference between the view and the presenter to store the viewModel (as a data structure).

For example, using dependency injection:

Response perform(Request request) {
    ViewModel viewModel;   // <-- just a space in memory
    Controller(&UseCase(&Presenter(&viewModel))).execute(request);
    return View(&viewModel).execute();
}

Solution 2: Use type erasure for return types

Another possible solution is to make use of type erasure to hide the return types of the Presenter, UseCase and Controller, then you can return an instance of the ViewModel from the Presenter to the UseCase to the Controller respectively.

For example:

class Presenter: ... {
    ...
    Object execute(OutputData) {
        ...
        return aViewModelInstance
    }
}

class UseCase: ... {
    ...
    Object execute(InputData) {
        ...
        return aPresenter.execute(aOutputDataInstance)
    }
}

class Controller: ... {
    ...
    Object execute(Request) {
        ...
        return aUseCase.execute(aInputDataInstance)
    }
}

Then in the outermost layer, cast to the ViewModel type:

Response perform(Request request) {
    viewModel = (ViewModel) Controller(&UseCase(&Presenter())).execute(request);
    return View().execute(viewModel);
}

Solution 3: Using generic types

class Presenter<T>: ... {
    ...
    T execute(OutputData) {
        ...
        return aViewModelInstance
    }
}

class UseCase<T>: ... {
    ...
    T execute(InputData) {
        ...
        return aPresenter.execute(aOutputDataInstance)
    }
}

class Controller<T>: ... {
    ...
    T execute(Request) {
        ...
        return aUseCase.execute(aInputDataInstance)
    }
}

Then in the outermost layer:

Response perform(Request request) {
    viewModel = Controller<ViewModel>(&UseCase<ViewModel>(&Presenter<ViewModel>())).execute(request);
    return View().execute(viewModel);
}

Solution 4: Use function composition

Response perform(Request request) {
    return View.run(Presenter.run(UseCase.run(Controller.run(request))))
}
import Foundation
typealias DTO = Codable & Equatable & Hashable
class Container<T: DTO> {
var value: T? = nil
}
struct ViewModel: DTO {
let todayDate: String
}
protocol Presenter {
func execute(input: Date)
}
class PresenterImpl: Presenter {
var container: Container<ViewModel>
init(container: Container<ViewModel>) {
self.container = container
}
func execute(input: Date) {
container.value = ViewModel(todayDate: input.formatted())
}
}
protocol UseCase {
func execute(input: String)
}
class UseCaseImpl: UseCase {
let presenter: Presenter
init(_ presenter: Presenter) {
self.presenter = presenter
}
func execute(input: String) {
self.presenter.execute(input: Date.now)
}
}
protocol Controller {
func execute(_: String)
}
class ControllerImpl: Controller {
let useCase: UseCase
init(_ useCase: UseCase) {
self.useCase = useCase
}
func execute(_ request: String) {
self.useCase.execute(input: request)
}
}
class View {
func execute(_ container: Container<ViewModel>) -> String {
let viewModel = container.value!
return "<html>\(viewModel.todayDate)</html>"
}
}
func perform(request: String) -> String {
let container = Container<ViewModel>()
ControllerImpl(UseCaseImpl(PresenterImpl(container: container))).execute(request)
return View().execute(container)
}
print(perform(request: "give me the date"))
import Foundation
typealias DTO = Codable & Equatable & Hashable
struct ViewModel: DTO {
let todayDate: String
}
protocol Presenter {
func execute(input: Date) -> any DTO
}
class PresenterImpl: Presenter {
init() {}
func execute(input: Date) -> any DTO {
return ViewModel(todayDate: input.formatted())
}
}
protocol UseCase {
func execute(input: String) -> any DTO
}
class UseCaseImpl: UseCase {
let presenter: Presenter
init(_ presenter: Presenter) {
self.presenter = presenter
}
func execute(input: String) -> any DTO {
return self.presenter.execute(input: Date.now)
}
}
protocol Controller {
func execute(_: String) -> any DTO
}
class ControllerImpl: Controller {
let useCase: UseCase
init(_ useCase: UseCase) {
self.useCase = useCase
}
func execute(_ request: String) -> any DTO {
return self.useCase.execute(input: request)
}
}
class View {
func execute(_ viewModel: ViewModel) -> String {
return "<html>\(viewModel.todayDate)</html>"
}
}
func perform(request: String) -> String {
return View().execute(ControllerImpl(UseCaseImpl(PresenterImpl())).execute(request) as! ViewModel);
}
print(perform(request: "give me the date"))
import Foundation
typealias DTO = Codable & Equatable & Hashable
struct ViewModel: DTO {
let todayDate: String
}
protocol Presenter {
associatedtype ViewModelType: DTO
func execute(input: Date) -> ViewModelType
}
class PresenterImpl: Presenter {
init() {}
func execute(input: Date) -> ViewModel {
return ViewModel(todayDate: input.formatted())
}
}
protocol UseCase {
associatedtype ViewModelType: DTO
func execute(input: String) -> ViewModelType
}
class UseCaseImpl<PresenterType: Presenter>: UseCase {
let presenter: PresenterType
init(_ presenter: PresenterType) {
self.presenter = presenter
}
func execute(input: String) -> PresenterType.ViewModelType {
return self.presenter.execute(input: Date.now)
}
}
protocol Controller {
associatedtype ViewModelType
func execute(_: String) -> ViewModelType
}
class ControllerImpl<UseCaseType: UseCase>: Controller {
let useCase: UseCaseType
init(_ useCase: UseCaseType) {
self.useCase = useCase
}
func execute(_ request: String) -> UseCaseType.ViewModelType {
return self.useCase.execute(input: request)
}
}
class View {
func execute(_ viewModel: ViewModel) -> String {
return "<html>\(viewModel.todayDate)</html>"
}
}
func perform(request: String) -> String {
return View().execute(ControllerImpl(UseCaseImpl(PresenterImpl())).execute(request));
}
print(perform(request: "give me the date"))
import Foundation
typealias DTO = Codable & Equatable & Hashable
struct ViewModel: DTO {
let todayDate: String
}
class PresenterImpl {
func execute(_ input: Date) -> ViewModel {
return ViewModel(todayDate: input.formatted())
}
}
class UseCaseImpl {
func execute(_ input: String) -> Date {
return Date.now
}
}
class ControllerImpl {
func execute(_ request: String) -> String {
return request
}
}
class View {
func execute(_ viewModel: ViewModel) -> String {
return "<html>\(viewModel.todayDate)</html>"
}
}
func perform(request: String) -> String {
return View().execute(PresenterImpl().execute(UseCaseImpl().execute(ControllerImpl().execute(request))))
}
print(perform(request: "give me the date"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment