public protocol IdentifiableType { | |
associatedtype Identity: Hashable | |
var identity : Identity { get } | |
} |
typealias RecipeListSectionModel = AnimatableSectionModel<String, Recipe> | |
var dataSource: RxTableViewSectionedAnimatedDataSource<RecipeListSectionModel>! |
dataSource = RxTableViewSectionedAnimatedDataSource<RecipeListSectionModel>( | |
animationConfiguration: AnimationConfiguration(insertAnimation: .right, | |
reloadAnimation: .none, | |
deleteAnimation: .left), | |
configureCell: configureCell, | |
canEditRowAtIndexPath: canEditRowAtIndexPath, | |
canMoveRowAtIndexPath: canMoveRowAtIndexPath | |
) | |
private var configureCell: RxTableViewSectionedAnimatedDataSource<RecipeListSectionModel>.ConfigureCell { | |
return { _, tableView, indexPath, recipe in | |
var cell: RecipeListTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath) | |
cell.bind(to: RecipeListCellViewModel(withRecipe: recipe)) | |
return cell | |
} | |
} | |
private var canEditRowAtIndexPath: RxTableViewSectionedAnimatedDataSource<RecipeListSectionModel>.CanEditRowAtIndexPath { | |
return { [unowned self] _, _ in | |
if self.tableView.isEditing { | |
return true | |
} else { | |
return false | |
} | |
} | |
} | |
private var canMoveRowAtIndexPath: RxTableViewSectionedAnimatedDataSource<RecipeListSectionModel>.CanMoveRowAtIndexPath { | |
return { _, _ in | |
return true | |
} | |
} |
viewModel.dataSource.asDriver() | |
.map { [RecipeListSectionModel(model: "", items: $0)] } | |
.drive(tableView.rx.items(dataSource: dataSource)) | |
.disposed(by: disposeBag) |
tableView.rx.itemDeleted.asDriver() | |
.drive(onNext: { [unowned self] indexPath in | |
self.viewModel.dataSource.remove(at: indexPath.row) | |
}) | |
.disposed(by: disposeBag) |
tableView.rx.itemMoved.asDriver() | |
.drive(onNext: { [unowned self] source, destination in | |
guard source != destination else { return } | |
let item = self.viewModel.dataSource.value[source.row] | |
self.viewModel.dataSource.replaceElement(at: source.row, insertTo: destination.row, with: item) | |
}) | |
.disposed(by: disposeBag) |
tableView.rx.modelSelected(Recipe.self) | |
.asDriver() | |
.drive(onNext: { [unowned self] recipe in | |
var vc = RecipeViewController.initFromNib() | |
vc.bind(to: RecipeViewModel(withRecipe: recipe)) | |
self.navigationController?.pushViewController(vc, animated: true) | |
}) | |
.disposed(by: disposeBag) |
enum RecipeSectionModel { | |
case recipe(items: [SectionItem]) | |
} | |
enum SectionItem { | |
case image(imageData: Data) | |
case about(text: String) | |
case ingredients(ingredients: [Ingredient]) | |
} | |
extension RecipeSectionModel: SectionModelType { | |
typealias Item = SectionItem | |
var items: [SectionItem] { | |
switch self { | |
case .recipe(let items): | |
return items.map { $0 } | |
} | |
} | |
init(original: RecipeSectionModel, items: [Item]) { | |
switch original { | |
case .recipe: | |
self = .recipe(items: items) | |
} | |
} | |
} |
extension RecipeViewModel { | |
func initSectionModels(withRecipe recipe: Recipe) { | |
sectionModels = .just(setupSections(recipe: recipe)) | |
} | |
} | |
private extension RecipeViewModel { | |
func setupSections(recipe: Recipe) -> [RecipeSectionModel] { | |
let dataSource: [RecipeSectionModel] = [ | |
.recipe(items: [.image(imageData: recipe.image), | |
.about(text: "Total calories: \(recipe.totalCalories)"), | |
.about(text: isVegetarianCell(recipe.isVegetarian)), | |
.ingredients(ingredients: recipe.ingredients)]) | |
] | |
return dataSource | |
} | |
func isVegetarianCell(_ isVegetarian: Bool) -> String { | |
if isVegetarian { | |
return "Vegetarian recipe" | |
} else { | |
return "Non-Vegetarian recipe" | |
} | |
} | |
} |
private var configureCell: DataSource.ConfigureCell { | |
return { dataSource, tableView, indexPath, _ in | |
switch dataSource[indexPath] { | |
case .image(let imageData): | |
var cell: ImageTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath) | |
cell.bind(to: ImageCellViewModel(withData: imageData)) | |
return cell | |
case .about(let text): | |
var cell: AboutTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath) | |
cell.bind(to: AboutCellViewModel(withText: text)) | |
return cell | |
case .ingredients(let ingredients): | |
var cell: IngredientsTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath) | |
cell.bind(to: IngredientsCellViewModel(withIngredients: ingredients)) | |
return cell | |
} | |
} | |
} |