Implemented the items filtering by star rating for the FeedListViewController view controller and its view model in the Feed framework.

This commit is contained in:
Javier Cicchelli 2024-03-20 22:22:14 +01:00
parent 8d84f15b40
commit e9590990ea
2 changed files with 60 additions and 37 deletions

View File

@ -7,6 +7,7 @@
//
import Foundation
import ReviewsFilterKit
import ReviewsiTunesKit
extension FeedListViewController {
@ -21,10 +22,14 @@ extension FeedListViewController {
// MARK: Properties
@Published var filter: FilterOption = .all
@Published var isFilterEnabled: Bool = false
@Published var isFiltering: Bool = false
@Published var isLoading: Bool = false
var items: [Review] = []
private var reviewsAll: [Review] = []
private var reviewsFiltered: FilteredReviews = [:]
lazy private var iTunesService: iTunesService = {
.init(configuration: .init(session: configuration.session))
}()
@ -46,19 +51,13 @@ extension FeedListViewController {
countryCode: configuration.countryCode
))
items = output.reviews
.map { review -> Review in
.init(
author: review.author,
comment: review.content,
id: review.id,
rating: .init(
stars: review.rating,
appVersion: review.version
),
title: review.title
)
reviewsAll = output.reviews.map(Review.init)
reviewsFiltered = FilterOption.allCases
.reduce(into: FilteredReviews()) { partialResult, option in
partialResult[option] = reviewsAll.filter { $0.rating.stars == option.rawValue }
}
items = reviewsAll
isFilterEnabled = !items.isEmpty
} catch {
// TODO: handle this error gracefully.
@ -68,6 +67,24 @@ extension FeedListViewController {
isLoading = false
}
}
func filter(by option: FilterOption) {
guard option != filter else { return }
items = option == .all
? reviewsAll
: reviewsFiltered[option] ?? []
filter = option
}
}
}
// MARK: - Helpers
private extension FeedListViewController.ViewModel {
// MARK: Type aliases
typealias FilteredReviews = [FilterOption: [Review]]
}

View File

@ -23,14 +23,7 @@ public class FeedListViewController: UITableViewController {
// MARK: Outlets
private lazy var filterButton = {
let allStars = UIAction(
title: FilterOption.all.text,
image: .init(systemName: FilterOption.all.icon)
) { [weak self] _ in
self?.viewModel.filter = .all
}
return UIBarButtonItem(
UIBarButtonItem(
title: NSLocalizedString(
.Key.Navigation.Button.filter,
bundle: .module,
@ -45,26 +38,19 @@ public class FeedListViewController: UITableViewController {
comment: .empty
),
image: UIImage.Icon.star,
children: [allStars, filterStarMenu]
children: {
FilterOption.allCases.map { option -> UIAction in
.init(title: option.text,
image: .init(systemName: option.icon)
) { [weak self] _ in
self?.viewModel.filter(by: option)
}
}
}()
)
)
}()
private lazy var filterStarMenu = {
UIMenu(
options: .displayInline,
children: {
FilterOption.allCases.map { option -> UIAction in
.init(title: option.text,
image: .init(systemName: option.icon)
) { [weak self] _ in
self?.viewModel.filter = option
}
}
}()
)
}()
// MARK: Initialisers
public init(configuration: Configuration = .init()) {
self.viewModel = .init(configuration: configuration)
@ -145,6 +131,14 @@ private extension FeedListViewController {
// MARK: Functions
func bindViewModel() {
viewModel.$filter
.receive(on: RunLoop.main)
.sink { [weak self] option in
self?.updateFilterMenu(option)
self?.tableView.reloadData()
}
.store(in: &cancellables)
viewModel.$isFilterEnabled
.removeDuplicates()
.receive(on: RunLoop.main)
@ -180,6 +174,18 @@ private extension FeedListViewController {
)
}
func updateFilterMenu(_ option: FilterOption) {
filterButton
.menu?
.children
.compactMap { $0 as? UIAction }
.forEach { action in
action.state = action.title == option.text
? .on
: .off
}
}
}
// MARK: - Configuration