Skip to content

Commit 41edc9e

Browse files
committed
Improved naming and split to multiple files
1 parent 5a992df commit 41edc9e

16 files changed

+217
-181
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import UIKit
2+
import Yosemite
3+
4+
/// Abstraction for building the concrete destination VC for a product detail flow.
5+
protocol ProductDetailCoordinator {
6+
func viewController(
7+
product: Product,
8+
presentationStyle: ProductDetailNavigator.Presentation,
9+
isReadOnly: Bool,
10+
onDelete: (() -> Void)?) -> UIViewController
11+
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import Yosemite
22
import UIKit
33

4-
class NativeProductDetailCoordinator: ProductDetailCoordinator {
4+
/// Coordinator for the **native** product detail/editor flow.
5+
/// Delegates VC construction to `ProductDetailsFactory` and applies the requested presentation style.
6+
class ProductDetailNativeCoordinator: ProductDetailCoordinator {
7+
58
func viewController(
69
product: Product,
7-
presentationStyle: ProductDetailPresenter.PresentationStyle,
8-
forceReadOnly: Bool,
9-
onDeleteCompletion: (() -> Void)? = nil) -> UIViewController {
10+
presentationStyle: ProductDetailNavigator.Presentation,
11+
isReadOnly: Bool,
12+
onDelete: (() -> Void)? = nil) -> UIViewController {
1013
return ProductDetailsFactory.productDetails(product: product,
1114
presentationStyle: presentationStyle.asProductFormPresentationStyle,
1215
forceReadOnly: false,
13-
onDeleteCompletion: onDeleteCompletion ?? {})
16+
onDeleteCompletion: onDelete ?? {})
1417
}
1518
}

WooCommerce/Classes/Routing/ProductDetail/WebViewProductDetailCoordinator.swift renamed to WooCommerce/Classes/Routing/ProductDetail/Coordinator/ProductDetailWebCoordinator.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import UIKit
22
import Yosemite
33

4-
final class WebViewProductDetailCoordinator: NSObject, ProductDetailCoordinator {
4+
/// Coordinator for the **admin web** product detail/editor flow.
5+
final class ProductDetailWebCoordinator: NSObject, ProductDetailCoordinator {
56
private var onDismiss: (() -> Void)?
67

78
private let site: Site
@@ -11,10 +12,10 @@ final class WebViewProductDetailCoordinator: NSObject, ProductDetailCoordinator
1112
}
1213

1314
func viewController(product: Product,
14-
presentationStyle: ProductDetailPresenter.PresentationStyle,
15-
forceReadOnly: Bool,
16-
onDeleteCompletion: (() -> Void)? = nil) -> UIViewController {
17-
guard let url = ProductURLProvider.editAdminURL(for: product, site: site) else {
15+
presentationStyle: ProductDetailNavigator.Presentation,
16+
isReadOnly: Bool,
17+
onDelete: (() -> Void)? = nil) -> UIViewController {
18+
guard let url = ProductAdminURLProvider.editURL(for: product, site: site) else {
1819
return UIViewController()
1920
}
2021

WooCommerce/Classes/Routing/ProductDetail/ProductURLProvider.swift renamed to WooCommerce/Classes/Routing/ProductDetail/ProductAdminURLProvider.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import Yosemite
22
import Foundation
33

4-
enum ProductURLProvider {
5-
static func editAdminURL(for product: Product, site: Site) -> URL? {
4+
/// Builds canonical admin edit URLs for products. Treat returned URLs as opaque.
5+
enum ProductAdminURLProvider {
6+
7+
static func editURL(for product: Product, site: Site) -> URL? {
68
guard let base = site.adminURLWithFallback() else { return nil }
79

810
var components = URLComponents(url: base, resolvingAgainstBaseURL: false)!
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Yosemite
2+
3+
/// Factory for producing coordinators used by the navigator.
4+
protocol ProductDetailCoordinatorFactoryProtocol {
5+
func webCoordinator() -> ProductDetailCoordinator
6+
func nativeCoordinator() -> ProductDetailCoordinator
7+
}
8+
9+
/// Default coordinator factory that wires production dependencies.
10+
class ProductDetailCoordinatorFactory: ProductDetailCoordinatorFactoryProtocol {
11+
static let `default` = ProductDetailCoordinatorFactory()
12+
13+
private let stores: StoresManager
14+
15+
init(stores: StoresManager = ServiceLocator.stores) {
16+
self.stores = stores
17+
}
18+
19+
func webCoordinator() -> ProductDetailCoordinator {
20+
return ProductDetailWebCoordinator(site: stores.sessionManager.defaultSite!)
21+
}
22+
23+
func nativeCoordinator() -> ProductDetailCoordinator {
24+
return ProductDetailNativeCoordinator()
25+
}
26+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import UIKit
2+
import Yosemite
3+
4+
/// Decides between **native** and **admin web** product detail flows and builds the destination VC.
5+
final class ProductDetailNavigator {
6+
7+
/// Describes how the **native** destination should be presented.
8+
enum Presentation {
9+
case push
10+
case contained(in: () -> UIViewController?)
11+
12+
var asProductFormPresentationStyle: ProductFormPresentationStyle {
13+
switch self {
14+
case .contained(let inVC):
15+
.contained(containerViewController: inVC)
16+
case .push:
17+
.navigationStack
18+
}
19+
}
20+
}
21+
22+
/// Convenience singleton for places that cannot inject dependencies easily.
23+
static var shared = ProductDetailNavigator()
24+
25+
init(ciabChecker: CIABEligibilityCheckerProtocol = CIABEligibilityChecker(),
26+
coordinatorFactory: ProductDetailCoordinatorFactoryProtocol = ProductDetailCoordinatorFactory.default) {
27+
self.ciabChecker = ciabChecker
28+
self.coordinatorFactory = coordinatorFactory
29+
}
30+
31+
/// Builds the destination `UIViewController` for the given product.
32+
/// - Parameters:
33+
/// - product: The product to display.
34+
/// - presentationStyle: How to present **native** detail (ignored for web).
35+
/// - isReadOnly: Whether the native screen should be read-only.
36+
/// - onDelete: Optional callback invoked after a successful product delete.
37+
/// - Returns: A ready-to-present view controller (native or web).
38+
func makeDestination(product: Product,
39+
presentationStyle: Presentation = .push,
40+
isReadOnly: Bool,
41+
onDelete: (() -> Void)? = nil) -> UIViewController {
42+
43+
let coordinator: ProductDetailCoordinator
44+
if shouldOpenInWeb(product: product) {
45+
coordinator = coordinatorFactory.webCoordinator()
46+
} else {
47+
coordinator = coordinatorFactory.nativeCoordinator()
48+
}
49+
50+
let viewController = coordinator.viewController(product: product,
51+
presentationStyle: presentationStyle,
52+
isReadOnly: isReadOnly,
53+
onDelete: onDelete)
54+
55+
return viewController
56+
}
57+
58+
private func shouldOpenInWeb(product: Product) -> Bool {
59+
return ciabChecker.isCurrentSiteCIAB && product.productType == .booking
60+
}
61+
62+
private let ciabChecker: CIABEligibilityCheckerProtocol
63+
private let coordinatorFactory: ProductDetailCoordinatorFactoryProtocol
64+
}

WooCommerce/Classes/Routing/ProductDetail/ProductDetailRouter.swift

Lines changed: 0 additions & 84 deletions
This file was deleted.

WooCommerce/Classes/ViewRelated/Products/Product Loader/ProductLoaderViewController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ private extension ProductLoaderViewController {
227227
/// Presents the ProductFormViewController, as a childViewController, for a given Product.
228228
///
229229
func presentProductDetails(for product: Product) {
230-
let viewController = ProductDetailPresenter.shared.viewController(product: product,
231-
presentationStyle: .contained(containerViewController: { [weak self] in self }),
232-
forceReadOnly: forceReadOnly)
230+
let viewController = ProductDetailNavigator.shared.makeDestination(product: product,
231+
presentationStyle: .contained(in: { [weak self] in self }),
232+
isReadOnly: forceReadOnly)
233233
attachProductDetailsChildViewController(viewController)
234234
}
235235

WooCommerce/Classes/ViewRelated/Products/ProductsSplitViewCoordinator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ private extension ProductsSplitViewCoordinator {
122122
}
123123

124124
func showProductForm(product: Product) {
125-
let viewController = ProductDetailPresenter.shared.viewController(product: product,
126-
forceReadOnly: false,
127-
onDeleteCompletion: { [weak self] in
125+
let viewController = ProductDetailNavigator.shared.makeDestination(product: product,
126+
isReadOnly: false,
127+
onDelete: { [weak self] in
128128
self?.onSecondaryProductFormDeletion()
129129
})
130130

WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,8 +1214,8 @@ extension ProductsViewController: UITableViewDelegate {
12141214
private extension ProductsViewController {
12151215
func didSelectProduct(product: Product) {
12161216
guard isSplitViewEnabled else {
1217-
let viewController = ProductDetailPresenter.shared.viewController(product: product,
1218-
forceReadOnly: false)
1217+
let viewController = ProductDetailNavigator.shared.makeDestination(product: product,
1218+
isReadOnly: false)
12191219
navigationController?.pushViewController(viewController, animated: true)
12201220
return
12211221
}

0 commit comments

Comments
 (0)