Skip to content
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

Merged
merged 11 commits into from
Feb 25, 2019
Merged

Add Basic Food Table View #66

merged 11 commits into from
Feb 25, 2019

Conversation

lokae0
Copy link
Collaborator

@lokae0 lokae0 commented Jan 28, 2019

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 use AnyObject 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

simulator screen shot - iphone xr - 2019-01-26 at 19 22 01

@lokae0 lokae0 requested a review from n8chur January 28, 2019 02:15
@@ -11,6 +11,10 @@ class StubDetailPresenter: StubSelectionPresenter {

extension StubDetailPresenter: DetailPresenter {

func foodTablePresentation(of viewModel: FoodTableViewModel) -> DismissablePresentation {
return DismissablePresentation.stub()
}
Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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?)

Copy link
Owner

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 { }
Copy link
Owner

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"
Copy link
Owner

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 {
Copy link
Owner

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]>
Copy link
Owner

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> {
Copy link
Owner

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]>,
Copy link
Owner

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 {
Copy link
Owner

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
Copy link
Owner

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()
}
Copy link
Owner

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) {
Copy link
Owner

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...

@lokae0
Copy link
Collaborator Author

lokae0 commented Feb 16, 2019

@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 {
Copy link
Owner

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]> {
Copy link
Owner

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.

@lokae0 lokae0 merged commit a0e3b73 into contents_feature Feb 25, 2019
@lokae0 lokae0 deleted the food_table branch February 25, 2019 01:52
lokae0 added a commit that referenced this pull request Jun 13, 2019
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants