-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Basic Food Table View #66
Conversation
@@ -11,6 +11,10 @@ class StubDetailPresenter: StubSelectionPresenter { | |||
|
|||
extension StubDetailPresenter: DetailPresenter { | |||
|
|||
func foodTablePresentation(of viewModel: FoodTableViewModel) -> DismissablePresentation { | |||
return DismissablePresentation.stub() | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to include this because StubDetailPresenter
can't also inherit from StubFoodTablePresenter
in addition to StubSelectionPresenter
. They are both classes and not protocols. Any thoughts or ideas on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Maybe fine because it's just for testing?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah... This should probably either be done with protocol extensions instead (or just implemented uniquely in every stub) rather than using class inheritance. What you have here is fine for this PR if you'd prefer to punt on that.
|
||
} | ||
|
||
protocol DetailNavigationControllerFactoryProtocol: DetailViewControllerFactoryProtocol, FoodTableViewControllerFactoryProtocol, SingleViewNavigationControllerFactoryProtocol { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should DetailViewControllerFactoryProtocol
conform to FoodTableViewControllerFactoryProtocol
(and therefore remove the need to specify the conformance here)?
@available(*, unavailable) | ||
required init?(coder aDecoder: NSCoder) { fatalError("\(#function) not implemented.") } | ||
|
||
private let cellIdentifier = "FoodCell" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Should this just be defined in viewDidLoad
since it's not referenced anywhere else?
Alternatively, should this be static
?
import UIKit | ||
import Core | ||
|
||
struct FoodTableViewControllerStyle: Style { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't thought about how theming with cells using the rx
style UITableView
would work.
I believe we'd have to iterate over all visible cells when it's set and update the style using a ...CellStyle
, and also have the current style available to the tableView.rx.items(cellIdentifier: cellIdentifier)
binding in the UIViewController
's viewDidLoad
function.
Is this something you'd like to try implementing yourself? We could pair program it?
class FoodTableViewModel: ViewModel { | ||
|
||
let isActive = BehaviorRelay(value: false) | ||
let foods: BehaviorRelay<[Food]> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be exposed as Property<[Food]>
so that it's not mutable by consumers of this ViewModel
?
withFactory factory: FoodTableViewModelFactoryProtocol, | ||
foods: BehaviorRelay<[Food]>, | ||
setupViewModel: ((FoodTableViewModel) -> Void)? = nil | ||
) -> Action<Bool, FoodTableViewModel> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This should be un-indented one line.
/// is executed. Consumers can use this to observe changes to the presented view model if necessary. | ||
func makePresentFoodTable( | ||
withFactory factory: FoodTableViewModelFactoryProtocol, | ||
foods: BehaviorRelay<[Food]>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foods should be owned by the FoodTableViewModelFactoryProtocol
and should not need to be passed in here. The factory is responsible for owning and injecting all dependencies.
import Presentations | ||
import Action | ||
|
||
class FoodTableViewModel: ViewModel { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I'd prefer not to have Table
in the name of the ViewModel
(or ViewController
unless there is also a Collection
version as well).
Should this instead be called this IngredientsViewModel
? FoodListViewModel
? FoodViewModel
?
} | ||
|
||
protocol FoodTableViewModelFactoryProtocol { | ||
func makeFoodTableViewModel(with foods: BehaviorRelay<[Food]>) -> FoodTableViewModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foods
should be a property of FoodTableViewModelFactoryProtocol
—not passed in—since it is a dependency of FoodTableViewModel
.
We should also make foods
a property of the DetailViewModelFactoryProtocol
, and then both would inject foods
into their corresponding view models when they are created.
@@ -11,6 +11,10 @@ class StubDetailPresenter: StubSelectionPresenter { | |||
|
|||
extension StubDetailPresenter: DetailPresenter { | |||
|
|||
func foodTablePresentation(of viewModel: FoodTableViewModel) -> DismissablePresentation { | |||
return DismissablePresentation.stub() | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah... This should probably either be done with protocol extensions instead (or just implemented uniquely in every stub) rather than using class inheritance. What you have here is fine for this PR if you'd prefer to punt on that.
@@ -50,24 +48,26 @@ class DetailViewModel: ViewModel, SelectionPresentingViewModel { | |||
.disposed(by: self.disposeBag) | |||
}) | |||
|
|||
init(selectionFactory: SelectionViewModelFactoryProtocol) { | |||
init(selectionFactory: SelectionViewModelFactoryProtocol, foodTableFactory: FoodTableViewModelFactoryProtocol) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this should be a single factory: SelectionViewModelFactoryProtocol & FoodTableViewModelFactoryProtocol
(perhaps typealias'd as Factory
in the scope of this ViewModel
) parameter instead? I can't imagine a case where you would be passing two separate instances here...
@n8chur PR updated. Let's chat IRL if there are any other suggested changes, as I won't spend much more time on this going forward. Travel prep and my other side project beckons! |
@@ -81,4 +81,8 @@ extension DetailViewControllerFactoryProtocol { | |||
return DetailViewController(viewModel: viewModel, themeProvider: themeProvider) | |||
} | |||
|
|||
func makeFoodInfoViewController(viewModel: FoodInfoViewModel) -> FoodInfoViewController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe this is needed. This function should be implemented by the protocol extension.
func makeDetailViewModel() -> DetailViewModel | ||
} | ||
|
||
extension DetailViewModelFactoryProtocol { | ||
|
||
// This can be backed by a datastore. For now, use a computed property to represent persistent data | ||
var foods: Property<[Food]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this should just be defined on the class that implements DetailViewModelFactoryProtocol
(DetailNavigationModelFactory
). Even though this is temporary, it's odd that it would instantiate the data model in a protocol extension. It could look like this (in DetailNavigationModel.swift
):
class DetailNavigationModelFactory: DetailNavigationModelFactoryProtocol {
let foods = Property([.beans, .greens, .potatoes, .tomatoes])
}
I believe one of the Stubs would also need to be updated.
* Add content title and button to Details view (#60) * Added strings for 'Contents' on details view * Change simulated delay to 1.0 to be more noticable * Clarify presenting VM vs presented VM in comments * Added content list title and button to detail view * Add some breathing room to stackViews * Fix unit test timeout * Add Content model and list display (#63) * Abstract centered label, remove unnecessary button boilerplate, and add spacing view * No longer excluding colors.txt from project in Xcode * Add alternate body text color * Adding content / food enum for testing * Display contents in detail view * Further abstract contents list creation and add unit test * Refactor contents list to be more reactive * Changes per PR review * Remove centeredLabel extension in favor of UILabelStyle additions * Use container stackView instead of spacingView * Add Basic Food Table View (#66) * Add FoodTable class cluster * Populate basic tableView * Add tests for FoodTable * Per Swift4 convention, use AnyObject instead of class for protocol constraints * Per PR review, fix FoodVCFactoryProtocol conformance * Per PR review, rename FoodTable to FoodInfo * Per PR review, streamline DetailVM factory protocol parameter * Per PR review, make cell identifier local var * Per PR review, update foods dependency structure * PR cleanup * Per PR review, cleanup where Food is located
Add Food Table View class cluster and unit tests.
For now, DetailViewModel passes in the array of Food items. In an upcoming PR, this will be moved to a mutable datastore.
This PR also updates protocols that constrain to
class
to useAnyObject
instead, per Swift 4 convention.class
will likely be deprecated in a future Swift version. See https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md#class-and-anyobject