diff --git a/HomeCafeRecipes/HomeCafeRecipes/Presentation/Feed/View/RecipeDetailViewController.swift b/HomeCafeRecipes/HomeCafeRecipes/Presentation/Feed/View/RecipeDetailViewController.swift index b6d7256..1ae1610 100644 --- a/HomeCafeRecipes/HomeCafeRecipes/Presentation/Feed/View/RecipeDetailViewController.swift +++ b/HomeCafeRecipes/HomeCafeRecipes/Presentation/Feed/View/RecipeDetailViewController.swift @@ -66,3 +66,10 @@ extension RecipeDetailViewController: RecipeDetailInteractorDelegate { } } + +extension RecipeDetailViewController: Drawable { + var viewController: UIViewController? { + return self + } +} + diff --git a/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListView.swift b/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListView.swift index 28ab024..e3bac63 100644 --- a/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListView.swift +++ b/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListView.swift @@ -23,7 +23,6 @@ final class RecipeListView: UIView { private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) private var recipes: [RecipeListItemViewModel] = [] - weak var coordinator: RecipeDetailCoordinatorProtocol? weak var delegate: RecipeListViewDelegate? override init(frame: CGRect) { diff --git a/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListViewController.swift b/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListViewController.swift index ac37e81..367a188 100644 --- a/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListViewController.swift +++ b/HomeCafeRecipes/HomeCafeRecipes/Presentation/FeedList/View/RecipeListViewController.swift @@ -14,13 +14,13 @@ final class RecipeListViewController: UIViewController { private let searchBar = SearchBar() private let recipeListView = RecipeListView() private let recipeListMapper = RecipeListMapper() - private let router: RecipeListRouterProtocol + private let router: RecipeListRouter - init(interactor: RecipeListInteractor, router: RecipeListRouterProtocol) { + init(interactor: RecipeListInteractor, router: RecipeListRouter) { + self.interactor.setDelegate(self) self.interactor = interactor self.router = router super.init(nibName: nil, bundle: nil) - recipeListView.delegate = self } required init?(coder: NSCoder) { diff --git a/HomeCafeRecipes/HomeCafeRecipes/Presentation/Tabbar/MainTabBarController.swift b/HomeCafeRecipes/HomeCafeRecipes/Presentation/Tabbar/MainTabBarController.swift index e592ca4..a57e50c 100644 --- a/HomeCafeRecipes/HomeCafeRecipes/Presentation/Tabbar/MainTabBarController.swift +++ b/HomeCafeRecipes/HomeCafeRecipes/Presentation/Tabbar/MainTabBarController.swift @@ -11,6 +11,16 @@ class MainTabBarController: UITabBarController, UITabBarControllerDelegate { private let addButton = UIButton(type: .custom) private let buttonSize = CGSize(all: 64.0) + private let router = Router() + + init(router: Router) { + self.router = router + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func viewDidLoad() { super.viewDidLoad() @@ -33,23 +43,26 @@ class MainTabBarController: UITabBarController, UITabBarControllerDelegate { } private func setupTabBar() { - let baseneworkServie = BaseNetworkService() - let networkService = RecipeFetchServiceImpl(networkService: baseneworkServie) - let repository = FeedListRepositoryImpl(networkService: networkService) - let searchrepository = SearchFeedRepositoryImpl(networkService: networkService) - let fetchFeedListUseCase = FetchFeedListUseCaseImpl(repository: repository) - let searchFeedListUsecase = SearchFeedListUseCaseImpl(repository: searchrepository) - - let recipeListViewModel = RecipeListInteractor(fetchFeedListUseCase: fetchFeedListUseCase, searchFeedListUseCase: searchFeedListUsecase) - - let recipeListVC = RecipeListViewController(interactor: recipeListViewModel) - recipeListVC.tabBarItem = UITabBarItem(title: "Recipes", image: UIImage(systemName: "list.bullet"), tag: 0) + let recipeListVC = router.createRecipeListDependencies() + let favoritesVC = createFavoritesViewController() + recipeListVC.tabBarItem = UITabBarItem( + title: "Recipes", + image: UIImage(systemName: "list.bullet"), + tag: 0 + ) + favoritesVC.tabBarItem = UITabBarItem( + title: "Favorites", + image: UIImage(systemName: "bookmark"), + tag: 1 + ) + viewControllers = [recipeListVC, favoritesVC] + } + + private func createFavoritesViewController() -> UIViewController { let favoritesVC = UIViewController() favoritesVC.view.backgroundColor = .white - favoritesVC.tabBarItem = UITabBarItem(title: "Favorites", image: UIImage(systemName: "bookmark"), tag: 1) - - viewControllers = [recipeListVC, favoritesVC] + return favoritesVC } private func setupActionButton() { @@ -68,12 +81,12 @@ class MainTabBarController: UITabBarController, UITabBarControllerDelegate { let alert = UIAlertController(title: "게시물 작성", message: "어떤 게시물을 작성하실 건가요?", preferredStyle: .actionSheet) alert.addAction(UIAlertAction(title: "Coffee", style: .default, handler: { [weak self] _ in guard let self else { return } - let addRecipeVC = AddRecipeViewController(recipeType: .coffee) + let addRecipeVC = router.makeAddRecipeViewController(recipeType: .coffee) self.navigationController?.pushViewController(addRecipeVC, animated: true) })) alert.addAction(UIAlertAction(title: "Dessert", style: .default, handler: { [weak self] _ in guard let self else { return } - let addRecipeVC = AddRecipeViewController(recipeType: .dessert) + let addRecipeVC = router.makeAddRecipeViewController(recipeType: .dessert) self.navigationController?.pushViewController(addRecipeVC, animated: true) })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) diff --git a/HomeCafeRecipes/HomeCafeRecipes/Router/RecipeListRouter.swift b/HomeCafeRecipes/HomeCafeRecipes/Router/RecipeListRouter.swift new file mode 100644 index 0000000..bf15166 --- /dev/null +++ b/HomeCafeRecipes/HomeCafeRecipes/Router/RecipeListRouter.swift @@ -0,0 +1,25 @@ +// +// RecipeListRouter.swift +// HomeCafeRecipes +// +// Created by 김건호 on 7/9/24. +// + +import UIKit + +protocol RecipeListRouter { + func navigateToRecipeDetail(from viewController: UIViewController, recipeID: Int) +} + +class RecipeListRouterImpl: RecipeListRouter { + private let router: Router + + init(router: Router) { + self.router = router + } + + func navigateToRecipeDetail(from viewController: UIViewController, recipeID: Int) { + let detailVC = router.makeRecipeDetailViewController(recipeID: recipeID) + router.push(detailVC, from: viewController, isAnimated: true, onNavigateBack: nil) + } +} diff --git a/HomeCafeRecipes/HomeCafeRecipes/Router/Router.swift b/HomeCafeRecipes/HomeCafeRecipes/Router/Router.swift new file mode 100644 index 0000000..570dbf0 --- /dev/null +++ b/HomeCafeRecipes/HomeCafeRecipes/Router/Router.swift @@ -0,0 +1,108 @@ +// +// Router.swift +// HomeCafeRecipes +// +// Created by 김건호 on 7/9/24. +// + +import UIKit + +public typealias NavigationBackClosure = () -> Void + +public protocol Drawable { + var viewController: UIViewController? { get } +} + +public protocol RouterProtocol { + func push ( + _ drawable: Drawable, + from viewController: UIViewController, + isAnimated: Bool, + onNavigateBack closure: NavigationBackClosure? + ) +} + +class Router: NSObject, RouterProtocol { + private var closures: [String: NavigationBackClosure] = [:] + + func push( + _ drawable: Drawable, + from viewController: UIViewController, + isAnimated: Bool, + onNavigateBack closure: NavigationBackClosure? + ) { + guard let targetViewController = drawable.viewController else { + return + } + + if let closure = closure { + closures.updateValue(closure, forKey: targetViewController.description) + } + viewController.navigationController?.pushViewController(targetViewController, animated: isAnimated) + } + + private func executeClosure(_ viewController: UIViewController) { + guard let closure = closures.removeValue(forKey: viewController.description) else { return } + closure() + } +} + +extension Router { + func makeRecipeListViewController() -> RecipeListViewController { + let recipeListInteractor = RecipeListInteractorImpl( + fetchFeedListUseCase: FetchFeedListUseCaseImpl( + repository: FeedListRepositoryImpl( + networkService: RecipeFetchServiceImpl( + networkService: BaseNetworkService() + ) + ) + ), + searchFeedListUseCase: SearchFeedListUseCaseImpl( + repository: SearchFeedRepositoryImpl( + networkService: RecipeFetchServiceImpl( + networkService: BaseNetworkService() + ) + ) + ) + ) + let recipeListRouter = RecipeListRouterImpl(router: self) + let recipeListVC = RecipeListViewController( + interactor: recipeListInteractor, + router: recipeListRouter + ) + recipeListInteractor.delegate = recipeListVC + return recipeListVC + } + + func makeAddRecipeViewController(recipeType: RecipeType) -> AddRecipeViewController { + let addRecipeInteractor = AddRecipeInteractorImpl( + saveRecipeUseCase: AddRecipeUseCaseImpl( + repository: AddRecipeRepositoryImpl( + recipePostService: RecipePostServiceImpl( + networkService: BaseNetworkService() + ) + ) + ) + ) + let addRecipeVC = AddRecipeViewController( + recipeType: recipeType, + addRecipeInteractor: addRecipeInteractor + ) + addRecipeInteractor.delegate = addRecipeVC + return addRecipeVC + } + + func makeRecipeDetailViewController(recipeID: Int) -> RecipeDetailViewController { + let detailInteractor = RecipeDetailInteractorImpl( + fetchRecipeDetailUseCase: FetchRecipeDetailUseCaseImpl( + repository: RecipeDetailRepositoryImpl( + networkService: BaseNetworkService() + ) + ), + recipeID: recipeID + ) + let detailVC = RecipeDetailViewController(interactor: detailInteractor) + detailInteractor.delegate = detailVC + return detailVC + } +} diff --git a/HomeCafeRecipes/HomeCafeRecipes/SceneDelegate.swift b/HomeCafeRecipes/HomeCafeRecipes/SceneDelegate.swift index aa44d9a..1e8615b 100644 --- a/HomeCafeRecipes/HomeCafeRecipes/SceneDelegate.swift +++ b/HomeCafeRecipes/HomeCafeRecipes/SceneDelegate.swift @@ -10,7 +10,7 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - + var router: Router? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. @@ -18,8 +18,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - let mainViewController = ViewController() - let navigationController = UINavigationController(rootViewController : mainViewController) + router = Router() + guard let router else { return } + + let tabBarController = MainTabBarController(router: router) + let navigationController = UINavigationController(rootViewController: tabBarController) + navigationController.isNavigationBarHidden = true window?.rootViewController = navigationController window?.makeKeyAndVisible() }