Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions ExampleApp/ExampleApp.xcodeproj/project.pbxproj
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
F8DF99162271C00700B68009 /* ToDo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF99152271C00700B68009 /* ToDo.swift */; };
F8DF99182271C04C00B68009 /* MVVMÇ.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF99172271C04C00B68009 /* MVVMÇ.swift */; };
F8DF991D2271C09A00B68009 /* ToDoListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF99192271C09A00B68009 /* ToDoListCoordinator.swift */; };
F8DF991E2271C09A00B68009 /* ToDoListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF991A2271C09A00B68009 /* ToDoListViewController.swift */; };
F8DF991E2271C09A00B68009 /* ToDoListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF991A2271C09A00B68009 /* ToDoListView.swift */; };
F8DF991F2271C09A00B68009 /* ToDoListInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF991B2271C09A00B68009 /* ToDoListInteractor.swift */; };
F8DF99202271C09A00B68009 /* ToDoListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF991C2271C09A00B68009 /* ToDoListViewModel.swift */; };
F8DF99222271C0A900B68009 /* LoadableModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF99212271C0A900B68009 /* LoadableModel.swift */; };
Expand Down Expand Up @@ -42,7 +42,7 @@
F8DF99152271C00700B68009 /* ToDo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDo.swift; sourceTree = "<group>"; };
F8DF99172271C04C00B68009 /* MVVMÇ.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "MVVMÇ.swift"; path = "../../MVVMÇ.swift"; sourceTree = "<group>"; };
F8DF99192271C09A00B68009 /* ToDoListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListCoordinator.swift; sourceTree = "<group>"; };
F8DF991A2271C09A00B68009 /* ToDoListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListViewController.swift; sourceTree = "<group>"; };
F8DF991A2271C09A00B68009 /* ToDoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListView.swift; sourceTree = "<group>"; };
F8DF991B2271C09A00B68009 /* ToDoListInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListInteractor.swift; sourceTree = "<group>"; };
F8DF991C2271C09A00B68009 /* ToDoListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDoListViewModel.swift; sourceTree = "<group>"; };
F8DF99212271C0A900B68009 /* LoadableModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -92,7 +92,7 @@
F8DF99212271C0A900B68009 /* LoadableModel.swift */,
F8DF99152271C00700B68009 /* ToDo.swift */,
F8DF99192271C09A00B68009 /* ToDoListCoordinator.swift */,
F8DF991A2271C09A00B68009 /* ToDoListViewController.swift */,
F8DF991A2271C09A00B68009 /* ToDoListView.swift */,
F8DF991B2271C09A00B68009 /* ToDoListInteractor.swift */,
F8DF991C2271C09A00B68009 /* ToDoListViewModel.swift */,
F8DF98F42271BF8D00B68009 /* Assets.xcassets */,
Expand Down Expand Up @@ -216,7 +216,7 @@
F8DF99182271C04C00B68009 /* MVVMÇ.swift in Sources */,
F8DF99222271C0A900B68009 /* LoadableModel.swift in Sources */,
F8DF991D2271C09A00B68009 /* ToDoListCoordinator.swift in Sources */,
F8DF991E2271C09A00B68009 /* ToDoListViewController.swift in Sources */,
F8DF991E2271C09A00B68009 /* ToDoListView.swift in Sources */,
F8DF991F2271C09A00B68009 /* ToDoListInteractor.swift in Sources */,
F8DF98EE2271BF8C00B68009 /* AppDelegate.swift in Sources */,
);
Expand Down Expand Up @@ -303,7 +303,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -358,7 +358,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
Expand Down
Empty file modified ExampleApp/ExampleApp/AppDelegate.swift
100644 → 100755
Empty file.
10 changes: 3 additions & 7 deletions ExampleApp/ExampleApp/ToDoListCoordinator.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@

import Foundation
import UIKit
import SwiftUI

class ToDoListCoordinator: Coordinator {

override func createRootViewController() -> UIViewController {
let interactor = ToDoListInteractor()
let viewModel = ToDoListViewModel(model: interactor.model, interactor: interactor, coordinator: self)
let viewController = ToDoListViewController(viewModel: viewModel)
interactor.bind(with: viewController) { (interactor, viewController) in
let viewModel = ToDoListViewModel(model: interactor.model, interactor: interactor, coordinator: self)
viewController.configure(with: viewModel)
}

let view = ToDoListView(viewModelPublisher: interactor.viewModelPublisher { ToDoListViewModel(model: $1, interactor: $0, coordinator: self)})
let viewController = UIHostingController(rootView: view)
return viewController
}
}
13 changes: 6 additions & 7 deletions ExampleApp/ExampleApp/ToDoListInteractor.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@
//

import Foundation
import Combine
import SwiftUI

class ToDoListInteractor: Interactor {
@Published private(set) var model: LoadableModel<[ToDo]> = .none

private(set) var model: LoadableModel<[ToDo]> = .none {
didSet {
modelDidUpdate?()
}
var modelPublisher: AnyPublisher<LoadableModel<[ToDo]>, Never> {
return $model.eraseToAnyPublisher()
}

var modelDidUpdate: (() -> Void)?


func load() {
model = model.byStartLoading()
// Fake load!
Expand Down
25 changes: 25 additions & 0 deletions ExampleApp/ExampleApp/ToDoListView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// ToDoListView.swift
// ExampleApp
//
// Created by Alex Manzella on 25/04/2019.
// Copyright © 2019 mpow. All rights reserved.
//

import Foundation
import SwiftUI

struct ToDoListView: View, UpdateableView {
@ObservedObject var viewModelPublisher: ViewModelPublisher<ToDoListViewModel>

var body: some View {
List(viewModel.cellViewModels, rowContent: { Text($0) })
.onAppear(perform: viewModel.load)
}
}

extension String: Identifiable {
public var id: String {
return self
}
}
53 changes: 0 additions & 53 deletions ExampleApp/ExampleApp/ToDoListViewController.swift

This file was deleted.

4 changes: 2 additions & 2 deletions ExampleApp/ExampleApp/ToDoListViewModel.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct ToDoListViewModel {
private let model: LoadableModel<[ToDo]>
private let interactor: ToDoListInteractor
private let coordinator: ToDoListCoordinator
let cellViewModels: [String]?
let cellViewModels: [String]

var title: String {
return "ToDo"
Expand All @@ -31,7 +31,7 @@ struct ToDoListViewModel {

cellViewModels = model.value?.flatMap {
return [$0.name] + ($0.subTasks?.map { " - \($0.name)" } ?? [])
}
} ?? []
}

func load() {
Expand Down
39 changes: 26 additions & 13 deletions MVVMÇ.swift
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
import Foundation
import UIKit
import Combine

protocol Interactor: class {
associatedtype ModelType

var model: ModelType { get }
var modelDidUpdate: (() -> Void)? { get set }
var modelPublisher: AnyPublisher<ModelType, Never> { get }
}

protocol UpdateableView: class {
protocol UpdateableView {
associatedtype ViewModelType

init(viewModel: ViewModelType)
var viewModelPublisher: ViewModelPublisher<ViewModelType> { get }
}

func configure(with viewModel: ViewModelType)
extension UpdateableView {
var viewModel: ViewModelType {
return viewModelPublisher.viewModel
}
}

// MARK: Binding
extension Interactor {
func bind<T: UpdateableView>(with viewController: T, factory: @escaping (_ interactor: Self, _ vc: T) -> Void) {
modelDidUpdate = { [weak self, weak viewController] in
guard let interactor = self, let viewController = viewController else {
return
}

factory(interactor, viewController)
}
func viewModelPublisher<T>(_ factory: @escaping (_ interactor: Self, ModelType) -> T) -> ViewModelPublisher<T> {
return ViewModelPublisher(
initialValue: factory(self, model),
publisher: AnyPublisher<T, Never>(modelPublisher.map { factory(self, $0) })
)
}
}

class ViewModelPublisher<T>: ObservableObject {
@Published var viewModel: T
private var cancellable: Any?

init(initialValue: T, publisher: AnyPublisher<T, Never>) {
self.viewModel = initialValue
self.cancellable = publisher.sink(receiveValue: { [unowned self] viewModel in
self.viewModel = viewModel
})
}
}

Expand Down