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

Food info / contents feature #68

Merged
merged 3 commits into from
Jun 13, 2019
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@ extension DetailNavigationController: SelectionPresenter {

}

extension DetailNavigationController: FoodInfoPresenter {

func foodInfoPresentation(of viewModel: FoodInfoViewModel) -> DismissablePresentation {
let viewController = factory.makeFoodInfoViewController(viewModel: viewModel)
return makePushPresentation(of: viewController)
}

}

protocol DetailNavigationControllerFactoryProtocol: DetailViewControllerFactoryProtocol, SingleViewNavigationControllerFactoryProtocol { }
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ extension DetailNavigationModelFactoryProtocol {
}

}

class DetailNavigationModelFactory: DetailNavigationModelFactoryProtocol { }
9 changes: 5 additions & 4 deletions Application/Source/Detail/View/DetailPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import RxSwift
import Action
import Presentations

protocol DetailPresentingViewModel: class, PresentingViewModel {
protocol DetailPresentingViewModel: AnyObject, PresentingViewModel {
var detailPresenter: DetailPresenter? { get set }
var presentDetail: Action<Bool, DetailViewModel> { get }
}
Expand All @@ -14,8 +14,8 @@ extension DetailPresentingViewModel {
/// This action should be executed with a Bool indicating whether the presentation should be animated.
///
/// - Parameter factory: A factory to be used to generate the presented view model.
/// - Parameter setupViewModel: This closure will be called with the presenting view model when a present action
/// is executed. Consumers can use this to observe changes to the presenting view model if necessary.
/// - Parameter setupViewModel: This closure will be called with the presented view model when a present action
/// is executed. Consumers can use this to observe changes to the presented view model if necessary.
func makePresentDetail(
withFactory factory: DetailViewModelFactoryProtocol,
setupViewModel: ((DetailViewModel) -> Void)? = nil
Expand All @@ -30,6 +30,7 @@ extension DetailPresentingViewModel {
let viewModel = factory.makeDetailViewModel()

viewModel.selectionPresenter = presenter
viewModel.foodInfoPresenter = presenter

setupViewModel?(viewModel)

Expand All @@ -41,6 +42,6 @@ extension DetailPresentingViewModel {

}

protocol DetailPresenter: SelectionPresenter {
protocol DetailPresenter: SelectionPresenter, FoodInfoPresenter {
func detailPresentation(of viewModel: DetailViewModel) -> DismissablePresentation
}
51 changes: 30 additions & 21 deletions Application/Source/Detail/View/DetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,40 @@ import SnapKit

class DetailView: UIView {

let title: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
let title = UILabel()
let button = UIButton()
let selectionResult = UILabel()

let foodListTitle = UILabel()
let foodList = UILabel()
let foodInfoButton = UIButton()

let button: UIButton = {
let button = UIButton()
button.setTitleColor(.blue, for: .normal)
return button
private(set) lazy var selectionStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [
title,
button,
selectionResult
])
stackView.axis = .vertical
stackView.alignment = .center
return stackView
}()

let selectionResult: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0
return label
private(set) lazy var foodStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [
foodListTitle,
foodList,
foodInfoButton
])
stackView.axis = .vertical
stackView.alignment = .center
return stackView
}()

private(set) lazy var stackView: UIStackView = {
private(set) lazy var containerStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [
title,
button,
selectionResult,
selectionStackView,
foodStackView
])
stackView.axis = .vertical
stackView.alignment = .center
Expand All @@ -39,8 +48,8 @@ class DetailView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)

addSubview(stackView)
stackView.snp.makeConstraints { make in
addSubview(containerStackView)
containerStackView.snp.makeConstraints { make in
make.center.equalTo(self)
}
}
Expand Down
13 changes: 12 additions & 1 deletion Application/Source/Detail/View/DetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,18 @@ class DetailViewController: UIViewController, ViewController {
.bind(to: detailView.selectionResult.rx.text)
.disposed(by: disposeBag)

viewModel.foodListTitle
.bind(to: detailView.foodListTitle.rx.text)
.disposed(by: disposeBag)
viewModel.foodListText
.bind(to: detailView.foodList.rx.text)
.disposed(by: disposeBag)
viewModel.foodInfoButtonTitle
.bind(to: detailView.foodInfoButton.rx.title())
.disposed(by: disposeBag)

detailView.button.rx.bind(to: viewModel.presentSelection, input: true)
detailView.foodInfoButton.rx.bind(to: viewModel.presentFoodInfo, input: true)

rx.isAppeared
.bind(to: viewModel.isActive)
Expand All @@ -58,7 +69,7 @@ class DetailViewController: UIViewController, ViewController {

}

protocol DetailViewControllerFactoryProtocol: SelectionViewControllerFactoryProtocol {
protocol DetailViewControllerFactoryProtocol: SelectionViewControllerFactoryProtocol, FoodInfoViewControllerFactoryProtocol {
var themeProvider: ThemeProvider { get }

func makeDetailViewController(viewModel: DetailViewModel) -> DetailViewController
Expand Down
25 changes: 17 additions & 8 deletions Application/Source/Detail/View/DetailViewControllerStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,38 @@ import UIKit
import Core

struct DetailViewControllerStyle: Style {

let theme: Theme
let background: BackgroundViewStyle
let title: LabelStyle
let selectionResult: LabelStyle
let label: LabelStyle
let alternateLabel: AlternateLabelStyle
let button: ButtonStyle

init(theme: Theme) {
self.theme = theme
background = BackgroundViewStyle(theme: theme)
title = LabelStyle(theme: theme)
selectionResult = LabelStyle(theme: theme)
label = LabelStyle(theme: theme)
alternateLabel = AlternateLabelStyle(theme: theme)
button = ButtonStyle(theme: theme)
}

func apply(to styleable: DetailViewController) {
let view = styleable.detailView

background.apply(to: view)
title.apply(to: view.title)
selectionResult.apply(to: view.selectionResult)

label.apply(to: view.title)
label.apply(to: view.foodListTitle)

alternateLabel.apply(to: view.selectionResult)
alternateLabel.apply(to: view.foodList)

button.apply(to: view.button)
button.apply(to: view.foodInfoButton)

view.selectionStackView.spacing = theme.layout.interitemSpacing
view.foodStackView.spacing = theme.layout.interitemSpacing

view.stackView.spacing = theme.layout.interitemSpacing
view.containerStackView.spacing = theme.layout.containerSpacing
}

}
37 changes: 30 additions & 7 deletions Application/Source/Detail/View/DetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import RxSwift
import RxCocoa
import RxExtensions
import Presentations
import Action

class DetailViewModel: ViewModel, SelectionPresentingViewModel {
class DetailViewModel: ViewModel, SelectionPresentingViewModel, FoodInfoPresentingViewModel {

let isActive = BehaviorRelay(value: false)

weak var selectionPresenter: SelectionPresenter?
weak var foodInfoPresenter: FoodInfoPresenter?

let title = Property(L10n.Detail.title)

Expand All @@ -16,8 +18,24 @@ class DetailViewModel: ViewModel, SelectionPresentingViewModel {

let presentSelectionTitle = Property(L10n.Detail.Select.title)

let foodListTitle = Property(L10n.Detail.FoodList.title)
let foodInfoButtonTitle = Property(L10n.Detail.FoodButton.title)

private(set) lazy var presentFoodInfo = makePresentFoodInfo(withFactory: factory)

private let foods: Property<[Food]>

private(set) lazy var foodListText: Property<String> = {
let observable = foods.map { foods -> String in
return foods
.map { $0.name }
.joined(separator: ", ")
}
return Property(observable, initial: "")
}()

private(set) lazy var presentSelection = makePresentSelection(
withFactory: selectionFactory,
withFactory: factory,
defaultValue: { [weak self] in
return self?.selectionResult.value
},
Expand All @@ -30,24 +48,29 @@ class DetailViewModel: ViewModel, SelectionPresentingViewModel {
.disposed(by: self.disposeBag)
})

init(selectionFactory: SelectionViewModelFactoryProtocol) {
self.selectionFactory = selectionFactory
typealias Factory = SelectionViewModelFactoryProtocol & FoodInfoViewModelFactoryProtocol

init(foods: Property<[Food]>, factory: Factory) {
self.foods = foods
self.factory = factory
}

private let selectionResultRelay = BehaviorRelay<String?>(value: nil)
private let selectionFactory: SelectionViewModelFactoryProtocol
private let factory: Factory
private let disposeBag = DisposeBag()

}

protocol DetailViewModelFactoryProtocol: SelectionViewModelFactoryProtocol {
protocol DetailViewModelFactoryProtocol: SelectionViewModelFactoryProtocol, FoodInfoViewModelFactoryProtocol {
var foods: Property<[Food]> { get }

func makeDetailViewModel() -> DetailViewModel
}

extension DetailViewModelFactoryProtocol {

func makeDetailViewModel() -> DetailViewModel {
return DetailViewModel(selectionFactory: self)
return DetailViewModel(foods: foods, factory: self)
}

}
61 changes: 61 additions & 0 deletions Application/Source/Food Info/FoodInfoViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import UIKit
import Presentations
import RxSwift
import Action
import Core

class FoodInfoViewController: UITableViewController, ViewController {

let viewModel: FoodInfoViewModel

let themeProvider: ThemeProvider

required init(viewModel: FoodInfoViewModel, themeProvider: ThemeProvider) {
self.viewModel = viewModel
self.themeProvider = themeProvider

super.init(nibName: nil, bundle: nil)
}

override func viewDidLoad() {
super.viewDidLoad()

let cellIdentifier = "FoodCell"
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)

viewModel.foods
.bind(to: tableView.rx.items(cellIdentifier: cellIdentifier)) { _, food, cell in
cell.textLabel?.text = food.name
}
.disposed(by: disposeBag)

rx.isAppeared
.bind(to: viewModel.isActive)
.disposed(by: disposeBag)

themeProvider.bindToStyleable(self) { FoodInfoViewControllerStyle(theme: $0) }
}

@available(*, unavailable)
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { fatalError("\(#function) not implemented.") }

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { fatalError("\(#function) not implemented.") }

private let disposeBag = DisposeBag()

}

protocol FoodInfoViewControllerFactoryProtocol {
var themeProvider: ThemeProvider { get }

func makeFoodInfoViewController(viewModel: FoodInfoViewModel) -> FoodInfoViewController
}

extension FoodInfoViewControllerFactoryProtocol {

func makeFoodInfoViewController(viewModel: FoodInfoViewModel) -> FoodInfoViewController {
return FoodInfoViewController(viewModel: viewModel, themeProvider: themeProvider)
}

}
16 changes: 16 additions & 0 deletions Application/Source/Food Info/FoodInfoViewControllerStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Themer
import UIKit
import Core

struct FoodInfoViewControllerStyle: Style {

let theme: Theme

init(theme: Theme) {
self.theme = theme
}

func apply(to styleable: FoodInfoViewController) {
}

}
37 changes: 37 additions & 0 deletions Application/Source/Food Info/FoodInfoViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import RxSwift
import RxCocoa
import RxExtensions
import Presentations
import Action

class FoodInfoViewModel: ViewModel {

let isActive = BehaviorRelay(value: false)
let foods: Property<[Food]>

init(with foods: Property<[Food]>) {
self.foods = foods
foods
.asObservable()
.logValue(.info, .application) { "FOODS: \($0)" }
.subscribe()
.disposed(by: disposeBag)
}

private let disposeBag = DisposeBag()

}

protocol FoodInfoViewModelFactoryProtocol {
var foods: Property<[Food]> { get }

func makeFoodInfoViewModel() -> FoodInfoViewModel
}

extension FoodInfoViewModelFactoryProtocol {

func makeFoodInfoViewModel() -> FoodInfoViewModel {
return FoodInfoViewModel(with: foods)
}

}
Loading