[Framework] Feed Item coordinator (#17)
This PR contains the work done to implement the `FeedItemCoordinator` coordinator in the `Feed` framework. Reviewed-on: rock-n-code/app-reviews#17 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #17.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// FeedListCoordination.swift
|
||||
// ReviewsFeed
|
||||
//
|
||||
// Created by Javier Cicchelli on 21/03/2024.
|
||||
// Copyright © 2024 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol FeedListCoordination: AnyObject {
|
||||
|
||||
// MARK: Functions
|
||||
func open(_ item: Review)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// FeedItemCoordinator.swift
|
||||
// ReviewsFeed
|
||||
//
|
||||
// Created by Javier Cicchelli on 21/03/2024.
|
||||
// Copyright © 2024 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ReviewsCoordinationKit
|
||||
|
||||
public final class FeedItemCoordinator: Coordinator {
|
||||
|
||||
// MARK: Constants
|
||||
public let router: any Router
|
||||
|
||||
private let item: Review
|
||||
|
||||
// MARK: Properties
|
||||
public var children: [any Coordinator] = []
|
||||
|
||||
public init(
|
||||
item: Review,
|
||||
router: any Router
|
||||
) {
|
||||
self.item = item
|
||||
self.router = router
|
||||
}
|
||||
|
||||
// MARK: Functions
|
||||
public func present(
|
||||
animated: Bool,
|
||||
onDismiss: Router.OnDismissClosure? = nil
|
||||
) {
|
||||
router.present(
|
||||
FeedItemViewController(item),
|
||||
animated: animated,
|
||||
onDismiss: onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,22 +10,29 @@ import Foundation
|
||||
import ReviewsCoordinationKit
|
||||
|
||||
public final class FeedListCoordinator: Coordinator {
|
||||
|
||||
|
||||
// MARK: Constants
|
||||
public let router: any Router
|
||||
|
||||
private let sessionConfiguration: URLSessionConfiguration
|
||||
private let configuration: FeedListConfiguration
|
||||
|
||||
// MARK: Properties
|
||||
public var children: [any Coordinator] = []
|
||||
|
||||
lazy var viewController = {
|
||||
FeedListViewController(.init(
|
||||
configuration: configuration,
|
||||
coordination: self
|
||||
))
|
||||
}()
|
||||
|
||||
// MARK: Initialisers
|
||||
public init(
|
||||
router: any Router,
|
||||
sessionConfiguration: URLSessionConfiguration = .ephemeral
|
||||
configuration: FeedListConfiguration,
|
||||
router: any Router
|
||||
) {
|
||||
self.configuration = configuration
|
||||
self.router = router
|
||||
self.sessionConfiguration = sessionConfiguration
|
||||
}
|
||||
|
||||
// MARK: Functions
|
||||
@@ -34,12 +41,26 @@ public final class FeedListCoordinator: Coordinator {
|
||||
onDismiss: Router.OnDismissClosure? = nil
|
||||
) {
|
||||
router.present(
|
||||
FeedListViewController(configuration: .init(
|
||||
session: sessionConfiguration
|
||||
)),
|
||||
viewController,
|
||||
animated: animated,
|
||||
onDismiss: onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - FeedListCoordination
|
||||
extension FeedListCoordinator: FeedListCoordination {
|
||||
|
||||
// MARK: Functions
|
||||
func open(_ item: Review) {
|
||||
present(
|
||||
child: FeedItemCoordinator(
|
||||
item: item,
|
||||
router: SheetRouter(parentViewController: viewController)
|
||||
),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Review {
|
||||
public struct Review {
|
||||
|
||||
// MARK: Constants
|
||||
let author: String
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// FeedListConfiguration.swift
|
||||
// ReviewsFeed
|
||||
//
|
||||
// Created by Javier Cicchelli on 21/03/2024.
|
||||
// Copyright © 2024 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct FeedListConfiguration {
|
||||
|
||||
// MARK: Constants
|
||||
let appID: String
|
||||
let countryCode: String
|
||||
let session: URLSessionConfiguration
|
||||
|
||||
// MARK: Initialisers
|
||||
public init(
|
||||
appID: String = "474495017",
|
||||
countryCode: String = "nl",
|
||||
session: URLSessionConfiguration = .ephemeral
|
||||
) {
|
||||
self.appID = appID
|
||||
self.countryCode = countryCode
|
||||
self.session = session
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,11 +13,8 @@ import ReviewsiTunesKit
|
||||
extension FeedListViewController {
|
||||
final class ViewModel: ObservableObject {
|
||||
|
||||
// MARK: Type aliases
|
||||
typealias Configuration = FeedListViewController.Configuration
|
||||
|
||||
// MARK: Constants
|
||||
private let configuration: Configuration
|
||||
private let configuration: FeedListConfiguration
|
||||
|
||||
private let filterWords: FilterWordsUseCase = .init()
|
||||
private let topWords: TopWordsUseCase = .init()
|
||||
@@ -30,18 +27,24 @@ extension FeedListViewController {
|
||||
|
||||
var items: [Review] = []
|
||||
var words: [TopWord] = []
|
||||
|
||||
|
||||
private var reviewsAll: [Review] = []
|
||||
private var reviewsFiltered: FilteredReviews = [:]
|
||||
private var reviewsTopWords: TopWordsReviews = [:]
|
||||
|
||||
private weak var coordination: FeedListCoordination?
|
||||
|
||||
lazy private var iTunesService: iTunesService = {
|
||||
.init(configuration: .init(session: configuration.session))
|
||||
}()
|
||||
|
||||
// MARK: Initialisers
|
||||
init(configuration: Configuration = .init()) {
|
||||
init(
|
||||
configuration: FeedListConfiguration = .init(),
|
||||
coordination: FeedListCoordination? = nil
|
||||
) {
|
||||
self.configuration = configuration
|
||||
self.coordination = coordination
|
||||
}
|
||||
|
||||
// MARK: Computed
|
||||
@@ -114,6 +117,12 @@ extension FeedListViewController {
|
||||
? items[index - 1]
|
||||
: items[index]
|
||||
}
|
||||
|
||||
func openItem(at index: Int) {
|
||||
guard let item = item(for: index) else { return }
|
||||
|
||||
coordination?.open(item)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import ReviewsUIKit
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
public class FeedListViewController: UITableViewController {
|
||||
final class FeedListViewController: UITableViewController {
|
||||
|
||||
// MARK: Constants
|
||||
private let viewModel: ViewModel
|
||||
@@ -79,8 +79,8 @@ public class FeedListViewController: UITableViewController {
|
||||
}()
|
||||
|
||||
// MARK: Initialisers
|
||||
public init(configuration: Configuration = .init()) {
|
||||
self.viewModel = .init(configuration: configuration)
|
||||
init(_ viewModel: ViewModel) {
|
||||
self.viewModel = viewModel
|
||||
|
||||
super.init(style: .plain)
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public class FeedListViewController: UITableViewController {
|
||||
}
|
||||
|
||||
// MARK: UIViewController
|
||||
public override func viewDidLoad() {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setNavigationBar()
|
||||
@@ -108,14 +108,14 @@ public class FeedListViewController: UITableViewController {
|
||||
}
|
||||
|
||||
// MARK: UITableViewDataSource
|
||||
public override func tableView(
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
numberOfRowsInSection section: Int
|
||||
) -> Int {
|
||||
viewModel.itemsCount
|
||||
}
|
||||
|
||||
public override func tableView(
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
cellForRowAt indexPath: IndexPath
|
||||
) -> UITableViewCell {
|
||||
@@ -127,21 +127,16 @@ public class FeedListViewController: UITableViewController {
|
||||
}
|
||||
|
||||
// MARK: UITableViewDelegate
|
||||
public override func tableView(
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
didSelectRowAt indexPath: IndexPath
|
||||
) {
|
||||
guard let item = viewModel.item(for: indexPath.row) else { return }
|
||||
|
||||
viewModel.openItem(at: indexPath.row)
|
||||
|
||||
tableView.deselectRow(
|
||||
at: indexPath,
|
||||
animated: true
|
||||
)
|
||||
|
||||
navigationController?.pushViewController(
|
||||
FeedItemViewController(item),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -292,29 +287,6 @@ private extension FeedListViewController {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
extension FeedListViewController {
|
||||
public struct Configuration {
|
||||
|
||||
// MARK: Constants
|
||||
let appID: String
|
||||
let countryCode: String
|
||||
let session: URLSessionConfiguration
|
||||
|
||||
// MARK: Initialisers
|
||||
public init(
|
||||
appID: String = "474495017",
|
||||
countryCode: String = "nl",
|
||||
session: URLSessionConfiguration = .ephemeral
|
||||
) {
|
||||
self.appID = appID
|
||||
self.countryCode = countryCode
|
||||
self.session = session
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - String+Constants
|
||||
private extension String {
|
||||
enum Cell {
|
||||
@@ -363,9 +335,9 @@ import ReviewsiTunesKit
|
||||
#Preview("Feed List loading reviews") {
|
||||
MockURLProtocol.response = .init(statusCode: 200)
|
||||
|
||||
return UINavigationController(
|
||||
rootViewController: FeedListViewController(configuration: .init(session: .mock))
|
||||
)
|
||||
return UINavigationController(rootViewController: FeedListViewController(.init(
|
||||
configuration: .init(session: .mock)
|
||||
)))
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@@ -421,9 +393,9 @@ import ReviewsiTunesKit
|
||||
])
|
||||
)
|
||||
|
||||
return UINavigationController(
|
||||
rootViewController: FeedListViewController(configuration: .init(session: .mock))
|
||||
)
|
||||
return UINavigationController(rootViewController: FeedListViewController(.init(
|
||||
configuration: .init(session: .mock)
|
||||
)))
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@@ -433,13 +405,13 @@ import ReviewsiTunesKit
|
||||
object: Feed(entries: [])
|
||||
)
|
||||
|
||||
return UINavigationController(
|
||||
rootViewController: FeedListViewController(configuration: .init(session: .mock))
|
||||
)
|
||||
return UINavigationController(rootViewController: FeedListViewController(.init(
|
||||
configuration: .init(session: .mock)
|
||||
)))
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
#Preview("Feed List with live reviews") {
|
||||
UINavigationController(rootViewController: FeedListViewController())
|
||||
return UINavigationController(rootViewController: FeedListViewController(.init()))
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user