From 2cb6e95d17b59cf778b8ce63228e90023383edef Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:04:58 +0100 Subject: [PATCH 01/10] Moved the FeedListConfiguration struct out of the FeedListViewController view controller in the Feed framework into its own file. --- .../Logic/Structs/FeedListConfiguration.swift | 29 +++++++++++++++++++ .../FeedListViewController.swift | 23 --------------- Reviews.xcodeproj/project.pbxproj | 12 ++++++++ 3 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 Frameworks/Feed/Bundle/Sources/Logic/Structs/FeedListConfiguration.swift diff --git a/Frameworks/Feed/Bundle/Sources/Logic/Structs/FeedListConfiguration.swift b/Frameworks/Feed/Bundle/Sources/Logic/Structs/FeedListConfiguration.swift new file mode 100644 index 0000000..a38d262 --- /dev/null +++ b/Frameworks/Feed/Bundle/Sources/Logic/Structs/FeedListConfiguration.swift @@ -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 + } + +} diff --git a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift index 78cfe49..6af6d97 100644 --- a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift +++ b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift @@ -292,29 +292,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 { diff --git a/Reviews.xcodeproj/project.pbxproj b/Reviews.xcodeproj/project.pbxproj index 3e3f750..8465ebc 100644 --- a/Reviews.xcodeproj/project.pbxproj +++ b/Reviews.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0220ADA32BA90646001E6A9F /* FeedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0220ADA22BA90646001E6A9F /* FeedItemView.swift */; }; 023AC7FC2BAA3EC10027D064 /* Int+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023AC7FB2BAA3EC10027D064 /* Int+Constants.swift */; }; 02620B8C2BA89C9A00DE7137 /* FeedListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02620B8B2BA89C9A00DE7137 /* FeedListViewModel.swift */; }; + 028134712BACC8CC0074AB4B /* FeedListConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028134702BACC8CC0074AB4B /* FeedListConfiguration.swift */; }; 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E782BAB6B0200710E14 /* FilterOption.swift */; }; 02909E7B2BAB6D2E00710E14 /* Bundle+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */; }; 02909E7D2BAB7FFE00710E14 /* Review+DTOs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */; }; @@ -62,6 +63,7 @@ 0220ADA22BA90646001E6A9F /* FeedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedItemView.swift; sourceTree = ""; }; 023AC7FB2BAA3EC10027D064 /* Int+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Constants.swift"; sourceTree = ""; }; 02620B8B2BA89C9A00DE7137 /* FeedListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListViewModel.swift; sourceTree = ""; }; + 028134702BACC8CC0074AB4B /* FeedListConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListConfiguration.swift; sourceTree = ""; }; 02909E782BAB6B0200710E14 /* FilterOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterOption.swift; sourceTree = ""; }; 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Constants.swift"; sourceTree = ""; }; 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Review+DTOs.swift"; sourceTree = ""; }; @@ -134,6 +136,7 @@ 02909E772BAB6AD500710E14 /* Enumerations */, 023AC7FA2BAA3EB60027D064 /* Extensions */, 02620B8A2BA89C3300DE7137 /* Models */, + 0281346F2BACC8B00074AB4B /* Structs */, 02620B872BA89C0700DE7137 /* View Models */, ); path = Logic; @@ -174,6 +177,14 @@ path = Models; sourceTree = ""; }; + 0281346F2BACC8B00074AB4B /* Structs */ = { + isa = PBXGroup; + children = ( + 028134702BACC8CC0074AB4B /* FeedListConfiguration.swift */, + ); + path = Structs; + sourceTree = ""; + }; 02909E772BAB6AD500710E14 /* Enumerations */ = { isa = PBXGroup; children = ( @@ -480,6 +491,7 @@ 02C1B1972BAC9BFE001781DE /* FeedListCoordinator.swift in Sources */, 02DC7FAE2BA51B4C000EEEBE /* FeedListViewController.swift in Sources */, 02EACF322BABB23A00FF8ECD /* TopWordsView.swift in Sources */, + 028134712BACC8CC0074AB4B /* FeedListConfiguration.swift in Sources */, 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; -- 2.47.1 From af6d4db807085cd9fb9bdab3ac3b0533818700a0 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:09:08 +0100 Subject: [PATCH 02/10] Added the "configuration" property constant to the FeedListCoordinator coordinator in the Feed framework to initialise the view model for the FeedListViewController view controller. --- App/Sources/App/AppDelegate.swift | 1 + .../Coordinators/FeedListCoordinator.swift | 14 ++++---- .../Logic/View Models/FeedListViewModel.swift | 7 ++-- .../FeedListViewController.swift | 34 +++++++++---------- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/App/Sources/App/AppDelegate.swift b/App/Sources/App/AppDelegate.swift index e0dfd38..83efe14 100644 --- a/App/Sources/App/AppDelegate.swift +++ b/App/Sources/App/AppDelegate.swift @@ -37,6 +37,7 @@ extension AppDelegate: UIApplicationDelegate { coordinator.present(animated: false) coordinator.present( child: FeedListCoordinator( + configuration: .init(session: .ephemeral), router: StackRouter(coordinator.navigationController) ), animated: false diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift index cf494ab..e2ff456 100644 --- a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift @@ -10,22 +10,22 @@ 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] = [] // 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,9 +34,7 @@ public final class FeedListCoordinator: Coordinator { onDismiss: Router.OnDismissClosure? = nil ) { router.present( - FeedListViewController(configuration: .init( - session: sessionConfiguration - )), + FeedListViewController(.init(configuration: configuration)), animated: animated, onDismiss: onDismiss ) diff --git a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift index 5fd5a99..fe33373 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift @@ -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() @@ -40,7 +37,7 @@ extension FeedListViewController { }() // MARK: Initialisers - init(configuration: Configuration = .init()) { + init(configuration: FeedListConfiguration = .init()) { self.configuration = configuration } diff --git a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift index 6af6d97..adbc9dc 100644 --- a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift +++ b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift @@ -13,7 +13,7 @@ import ReviewsUIKit import SwiftUI import UIKit -public class FeedListViewController: UITableViewController { +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,7 +127,7 @@ public class FeedListViewController: UITableViewController { } // MARK: UITableViewDelegate - public override func tableView( + override func tableView( _ tableView: UITableView, didSelectRowAt indexPath: IndexPath ) { @@ -340,9 +340,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, *) @@ -398,9 +398,9 @@ import ReviewsiTunesKit ]) ) - return UINavigationController( - rootViewController: FeedListViewController(configuration: .init(session: .mock)) - ) + return UINavigationController(rootViewController: FeedListViewController(.init( + configuration: .init(session: .mock) + ))) } @available(iOS 17.0, *) @@ -410,13 +410,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 -- 2.47.1 From 1618a8d8410eaccd710c06803ca0aa0372374f23 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:45:51 +0100 Subject: [PATCH 03/10] Defined the FeedListCoordination protocol in the Feed framework. --- .../Coordination/FeedListCoordination.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Frameworks/Feed/Bundle/Sources/Coordinators/Coordination/FeedListCoordination.swift diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/Coordination/FeedListCoordination.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/Coordination/FeedListCoordination.swift new file mode 100644 index 0000000..ffb5cf8 --- /dev/null +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/Coordination/FeedListCoordination.swift @@ -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) + +} -- 2.47.1 From aacc0fa0fecca87132fc12fdffcd2b58e365b984 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:48:10 +0100 Subject: [PATCH 04/10] Added the "coordination" weak property to the FeedListViewModel view model in the Feed framework. --- .../Sources/Coordinators/FeedListCoordinator.swift | 5 ++++- .../Sources/Logic/View Models/FeedListViewModel.swift | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift index e2ff456..2562825 100644 --- a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift @@ -34,7 +34,10 @@ public final class FeedListCoordinator: Coordinator { onDismiss: Router.OnDismissClosure? = nil ) { router.present( - FeedListViewController(.init(configuration: configuration)), + FeedListViewController(.init( + configuration: configuration, + coordination: self + )), animated: animated, onDismiss: onDismiss ) diff --git a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift index fe33373..96ef07a 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift @@ -27,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: FeedListConfiguration = .init()) { + init( + configuration: FeedListConfiguration = .init(), + coordination: FeedListCoordination? = nil + ) { self.configuration = configuration + self.coordination = coordination } // MARK: Computed -- 2.47.1 From 5f2cc4638c8b844118e0da5e81d90afaac5b08a0 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:50:13 +0100 Subject: [PATCH 05/10] Implemented the FeedItemCoordinator coordinator in the Feed framework. --- .../Coordinators/FeedItemCoordinator.swift | 42 +++++++++++++++++++ .../Bundle/Sources/Logic/Models/Review.swift | 2 +- Reviews.xcodeproj/project.pbxproj | 16 +++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 Frameworks/Feed/Bundle/Sources/Coordinators/FeedItemCoordinator.swift diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedItemCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedItemCoordinator.swift new file mode 100644 index 0000000..765c916 --- /dev/null +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedItemCoordinator.swift @@ -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 + ) + } + +} diff --git a/Frameworks/Feed/Bundle/Sources/Logic/Models/Review.swift b/Frameworks/Feed/Bundle/Sources/Logic/Models/Review.swift index a315a88..4da44cf 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/Models/Review.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/Models/Review.swift @@ -8,7 +8,7 @@ import Foundation -struct Review { +public struct Review { // MARK: Constants let author: String diff --git a/Reviews.xcodeproj/project.pbxproj b/Reviews.xcodeproj/project.pbxproj index 8465ebc..d05c3f5 100644 --- a/Reviews.xcodeproj/project.pbxproj +++ b/Reviews.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 023AC7FC2BAA3EC10027D064 /* Int+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023AC7FB2BAA3EC10027D064 /* Int+Constants.swift */; }; 02620B8C2BA89C9A00DE7137 /* FeedListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02620B8B2BA89C9A00DE7137 /* FeedListViewModel.swift */; }; 028134712BACC8CC0074AB4B /* FeedListConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028134702BACC8CC0074AB4B /* FeedListConfiguration.swift */; }; + 028134822BACCC780074AB4B /* FeedListCoordination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028134812BACCC770074AB4B /* FeedListCoordination.swift */; }; + 028134842BACD0B20074AB4B /* FeedItemCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028134832BACD0B20074AB4B /* FeedItemCoordinator.swift */; }; 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E782BAB6B0200710E14 /* FilterOption.swift */; }; 02909E7B2BAB6D2E00710E14 /* Bundle+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */; }; 02909E7D2BAB7FFE00710E14 /* Review+DTOs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */; }; @@ -64,6 +66,8 @@ 023AC7FB2BAA3EC10027D064 /* Int+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Constants.swift"; sourceTree = ""; }; 02620B8B2BA89C9A00DE7137 /* FeedListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListViewModel.swift; sourceTree = ""; }; 028134702BACC8CC0074AB4B /* FeedListConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListConfiguration.swift; sourceTree = ""; }; + 028134812BACCC770074AB4B /* FeedListCoordination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListCoordination.swift; sourceTree = ""; }; + 028134832BACD0B20074AB4B /* FeedItemCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedItemCoordinator.swift; sourceTree = ""; }; 02909E782BAB6B0200710E14 /* FilterOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterOption.swift; sourceTree = ""; }; 02909E7A2BAB6D2E00710E14 /* Bundle+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Constants.swift"; sourceTree = ""; }; 02909E7C2BAB7FFE00710E14 /* Review+DTOs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Review+DTOs.swift"; sourceTree = ""; }; @@ -185,6 +189,14 @@ path = Structs; sourceTree = ""; }; + 028134802BACCC630074AB4B /* Coordination */ = { + isa = PBXGroup; + children = ( + 028134812BACCC770074AB4B /* FeedListCoordination.swift */, + ); + path = Coordination; + sourceTree = ""; + }; 02909E772BAB6AD500710E14 /* Enumerations */ = { isa = PBXGroup; children = ( @@ -213,6 +225,8 @@ 02C1B1952BAC9BE7001781DE /* Coordinators */ = { isa = PBXGroup; children = ( + 028134802BACCC630074AB4B /* Coordination */, + 028134832BACD0B20074AB4B /* FeedItemCoordinator.swift */, 02C1B1962BAC9BFE001781DE /* FeedListCoordinator.swift */, ); path = Coordinators; @@ -484,6 +498,7 @@ 02DC7FAC2BA51B4C000EEEBE /* FeedItemViewController.swift in Sources */, 02909E7B2BAB6D2E00710E14 /* Bundle+Constants.swift in Sources */, 02EACF2E2BABA34600FF8ECD /* FeedItemCell.swift in Sources */, + 028134842BACD0B20074AB4B /* FeedItemCoordinator.swift in Sources */, 02909E7D2BAB7FFE00710E14 /* Review+DTOs.swift in Sources */, 0220ADA32BA90646001E6A9F /* FeedItemView.swift in Sources */, 02EACF362BABB2F200FF8ECD /* TopWord+DTOs.swift in Sources */, @@ -492,6 +507,7 @@ 02DC7FAE2BA51B4C000EEEBE /* FeedListViewController.swift in Sources */, 02EACF322BABB23A00FF8ECD /* TopWordsView.swift in Sources */, 028134712BACC8CC0074AB4B /* FeedListConfiguration.swift in Sources */, + 028134822BACCC780074AB4B /* FeedListCoordination.swift in Sources */, 02909E792BAB6B0200710E14 /* FilterOption.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; -- 2.47.1 From 391433210b2b9ee221143623c6684b41e07934c2 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 21:51:16 +0100 Subject: [PATCH 06/10] Conformed the FeedListCoordinator coordinator in the Feed framework to the FeedListCoordination protocol. --- .../Coordinators/FeedListCoordinator.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift index 2562825..6c6a162 100644 --- a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift @@ -44,3 +44,19 @@ public final class FeedListCoordinator: Coordinator { } } + +// MARK: - FeedListCoordination +extension FeedListCoordinator: FeedListCoordination { + + // MARK: Functions + func open(_ item: Review) { + present( + child: FeedItemCoordinator( + item: item, + router: router + ), + animated: true + ) + } + +} -- 2.47.1 From 0fb3fcfee0f430d4982c3e51724042b9c34389ca Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 23:50:01 +0100 Subject: [PATCH 07/10] Added the "close" static constant for String+Icons and UIImage+Icons extensions in the UI library. --- Libraries/UI/Kit/Sources/Extensions/String+Icons.swift | 1 + .../Extensions/{UIImage+ICons.swift => UIImage+Icons.swift} | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) rename Libraries/UI/Kit/Sources/Extensions/{UIImage+ICons.swift => UIImage+Icons.swift} (80%) diff --git a/Libraries/UI/Kit/Sources/Extensions/String+Icons.swift b/Libraries/UI/Kit/Sources/Extensions/String+Icons.swift index 5ecef45..161b879 100644 --- a/Libraries/UI/Kit/Sources/Extensions/String+Icons.swift +++ b/Libraries/UI/Kit/Sources/Extensions/String+Icons.swift @@ -19,6 +19,7 @@ public extension String { public static let star4 = "4.circle" public static let star5 = "5.circle" + static let close = "xmark.circle.fill" static let filter = "camera.filters" static let questionMark = "questionmark.circle.fill" static let star = "star" diff --git a/Libraries/UI/Kit/Sources/Extensions/UIImage+ICons.swift b/Libraries/UI/Kit/Sources/Extensions/UIImage+Icons.swift similarity index 80% rename from Libraries/UI/Kit/Sources/Extensions/UIImage+ICons.swift rename to Libraries/UI/Kit/Sources/Extensions/UIImage+Icons.swift index ee2789d..049dfbf 100644 --- a/Libraries/UI/Kit/Sources/Extensions/UIImage+ICons.swift +++ b/Libraries/UI/Kit/Sources/Extensions/UIImage+Icons.swift @@ -1,5 +1,5 @@ // -// UIImage+ICons.swift +// UIImage+Icons.swift // ReviewsUIKit // // Created by Javier Cicchelli on 20/03/2024. @@ -12,6 +12,7 @@ public extension UIImage { enum Icon { // MARK: Constants + public static let close = UIImage(systemName: .Icon.close) public static let filter = UIImage(systemName: .Icon.filter) public static let star = UIImage(systemName: .Icon.star) -- 2.47.1 From 558bedb57ed5c22b07c6798a8f3b98cc5e82324a Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 23:51:02 +0100 Subject: [PATCH 08/10] Implemented the SheetRouter router in the Coordination library. --- .../Kit/Sources/Routers/SheetRouter.swift | 103 ++++++++++++++++++ Libraries/Package.swift | 1 + 2 files changed, 104 insertions(+) create mode 100644 Libraries/Coordination/Kit/Sources/Routers/SheetRouter.swift diff --git a/Libraries/Coordination/Kit/Sources/Routers/SheetRouter.swift b/Libraries/Coordination/Kit/Sources/Routers/SheetRouter.swift new file mode 100644 index 0000000..a748efb --- /dev/null +++ b/Libraries/Coordination/Kit/Sources/Routers/SheetRouter.swift @@ -0,0 +1,103 @@ +// +// SheetRouter.swift +// ReviewsCoordinationKit +// +// Created by Javier Cicchelli on 21/03/2024. +// Copyright © 2024 Röck+Cöde. All rights reserved. +// + +import ReviewsUIKit +import UIKit + +public class SheetRouter: BaseNavigationRouter { + + // MARK: Properties + public unowned let parentViewController: UIViewController + + // MARK: Initialisers + public init(parentViewController: UIViewController) { + self.parentViewController = parentViewController + + super.init(navigationController: .init()) + } + +} + +// MARK: - Router +extension SheetRouter: Router { + + // MARK: Functions + public func present( + _ viewController: UIViewController, + animated: Bool, + onDismiss: Router.OnDismissClosure? + ) { + onDismissForViewController[viewController] = onDismiss + + if navigationController.viewControllers.isEmpty { + presentModally( + viewController, + animated: animated + ) + } else { + navigationController.pushViewController( + viewController, + animated: animated + ) + } + } + + public func dismiss(animated: Bool) { + guard let firstViewController = navigationController.viewControllers.first else { + return + } + + performOnDismiss(for: firstViewController) + + parentViewController.dismiss(animated: animated) + } + +} + +// MARK: - Helpers +private extension SheetRouter { + + // MARK: Actions + @objc func onCancelPressed() { + guard let firstViewController = navigationController.viewControllers.first else { + return + } + + dismiss(animated: true) + + performOnDismiss(for: firstViewController) + } + + // MARK: Functions + func presentModally( + _ viewController: UIViewController, + animated: Bool + ) { + viewController.navigationItem.rightBarButtonItem = UIBarButtonItem( + image: .Icon.close, + style: .plain, + target: self, + action: #selector(onCancelPressed) + ) + + if #available(iOS 15.0, *) { + navigationController.sheetPresentationController?.detents = [.medium(), .large()] + } + + navigationController.setViewControllers( + [viewController], + animated: false + ) + + parentViewController.present( + navigationController, + animated: animated + ) + } + +} diff --git a/Libraries/Package.swift b/Libraries/Package.swift index e0327a1..09d51be 100644 --- a/Libraries/Package.swift +++ b/Libraries/Package.swift @@ -25,6 +25,7 @@ let package = Package( name: .Target.coordination.kit, dependencies: [ .byName(name: .Target.foundation.kit), + .byName(name: .Target.ui.kit), ], path: "Coordination/Kit" ), -- 2.47.1 From 63739cb8cdcf00d9fab4203e08d4e520cddb5bd3 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 23:52:53 +0100 Subject: [PATCH 09/10] Integrated the SheetRouter router inside the "open(_: )" function for the FeedListCoordinator coordinator in the Feed framework. --- .../Sources/Coordinators/FeedListCoordinator.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift index 6c6a162..73a5fca 100644 --- a/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift +++ b/Frameworks/Feed/Bundle/Sources/Coordinators/FeedListCoordinator.swift @@ -19,6 +19,13 @@ public final class FeedListCoordinator: Coordinator { // MARK: Properties public var children: [any Coordinator] = [] + lazy var viewController = { + FeedListViewController(.init( + configuration: configuration, + coordination: self + )) + }() + // MARK: Initialisers public init( configuration: FeedListConfiguration, @@ -34,10 +41,7 @@ public final class FeedListCoordinator: Coordinator { onDismiss: Router.OnDismissClosure? = nil ) { router.present( - FeedListViewController(.init( - configuration: configuration, - coordination: self - )), + viewController, animated: animated, onDismiss: onDismiss ) @@ -53,7 +57,7 @@ extension FeedListCoordinator: FeedListCoordination { present( child: FeedItemCoordinator( item: item, - router: router + router: SheetRouter(parentViewController: viewController) ), animated: true ) -- 2.47.1 From 80b760350436ee61824ad27b21bb3b303ee10483 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 23:54:46 +0100 Subject: [PATCH 10/10] Implemented the "openItem(at: )" function for the FeedListViewModel view model in the Feed framework, and integrated it to the FeedListViewController view controller. --- .../Sources/Logic/View Models/FeedListViewModel.swift | 6 ++++++ .../UI/View Controllers/FeedListViewController.swift | 11 +++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift index 96ef07a..08671b7 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift @@ -117,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) + } } } diff --git a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift index adbc9dc..4fee1d3 100644 --- a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift +++ b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift @@ -13,7 +13,7 @@ import ReviewsUIKit import SwiftUI import UIKit -class FeedListViewController: UITableViewController { +final class FeedListViewController: UITableViewController { // MARK: Constants private let viewModel: ViewModel @@ -131,17 +131,12 @@ class FeedListViewController: UITableViewController { _ 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 - ) } } -- 2.47.1