// // AppDelegate.swift // ReviewsFeed // // Created by Dmitrii Ivanov on 21/07/2020. // Copyright © 2020 ING. All rights reserved. // import Combine import ReviewsUIKit import SwiftUI import UIKit public class FeedViewController: UITableViewController { // MARK: Constants private let viewModel: ViewModel // MARK: Properties private var cancellables: Set = [] // MARK: Initialisers public init(configuration: Configuration = .init()) { self.viewModel = .init(configuration: configuration) super.init(style: .plain) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: UIViewController public override func viewDidLoad() { super.viewDidLoad() tableView.register( UITableViewCell.self, forCellReuseIdentifier: .Cell.feedItem ) bindViewModel() viewModel.fetch() } // MARK: UITableViewDataSource public override func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int ) -> Int { viewModel.items.count } public override func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: .Cell.feedItem) else { return .init() } cell.contentConfiguration = { if #available(iOS 16.0, *) { UIHostingConfiguration { FeedItemCell(viewModel.items[indexPath.row]) } } else { HostingConfiguration { FeedItemCell(viewModel.items[indexPath.row]) } } }() return cell } // MARK: UITableViewDelegate public override func tableView( _ tableView: UITableView, didSelectRowAt indexPath: IndexPath ) { let details = DetailsViewController(review: viewModel.items[indexPath.row]) tableView.deselectRow( at: indexPath, animated: true ) navigationController?.pushViewController(details, animated: true) } } // MARK: - Helpers private extension FeedViewController { // MARK: Functions func bindViewModel() { viewModel.$loading .sink { loading in print("LOADING: \(loading)") } .store(in: &cancellables) viewModel.$loading .dropFirst() .filter { $0 == false } .receive(on: RunLoop.main) .sink { [weak self] _ in self?.tableView.reloadData() } .store(in: &cancellables) } } // MARK: - Configuration extension FeedViewController { 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 { static let feedItem = "FeedItemCell" } } // MARK: - Previews #if DEBUG import ReviewsFoundationKit import ReviewsiTunesKit @available(iOS 17.0, *) #Preview("Feed View Controller with few reviews") { MockURLProtocol.response = .init( statusCode: 200, object: Feed(entries: [ .init( id: 1, author: "Some author name #1 here", title: "Some review title #1 goes here...", content: "Some long, explanatory review comment #1 goes here...", rating: 3, version: "v1.0.0", updated: .init() ), .init( id: 2, author: "Some author name #2 here", title: "Some review title #2 goes here...", content: "Some long, explanatory review comment #2 goes here...", rating: 5, version: "v1.0.0", updated: .init() ), .init( id: 3, author: "Some author name #3 here", title: "Some review title #3 goes here...", content: "Some long, explanatory review comment #3 goes here...", rating: 1, version: "v1.0.0", updated: .init() ), ]) ) return FeedViewController(configuration: .init(session: .mock)) } @available(iOS 17.0, *) #Preview("Feed View Controller with no reviews") { MockURLProtocol.response = .init( statusCode: 200, object: Feed(entries: []) ) return FeedViewController(configuration: .init(session: .mock)) } #endif