diff --git a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift index 7e6b00ad..3ac9a5e8 100644 --- a/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift +++ b/MLS/Domain/Domain/UseCaseImpl/AuthAPI/WithdrawUseCaseImpl.swift @@ -14,12 +14,22 @@ public class WithdrawUseCaseImpl: WithdrawUseCase { } public func execute() -> Completable { - switch tokenRepository.deleteToken(type: .accessToken) { - case .success: - return authRepository.withdraw() - case .failure(let error): - return .error(error) - } + return authRepository.withdraw() + .andThen(Completable.deferred { [weak self] in + guard let self = self else { return .empty() } + let results: [Result] = [ + self.tokenRepository.deleteToken(type: .accessToken), + self.tokenRepository.deleteToken(type: .refreshToken), + self.tokenRepository.deleteToken(type: .fcmToken) + ] + + for result in results { + if case .failure(let error) = result { + return .error(error) + } + } + return .empty() + }) } } diff --git a/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved b/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved index f4ae71b6..c7f6a5b9 100644 --- a/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/MLS/MLS.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "f7b0491e2198bb3edde5fd2999d888cbedc1acb8555c5e1c2b384a269b2fb29d", + "originHash" : "ba8fe7771291f9a80fd693d31ec15a696e49e0d6060bae5c6576060534c1d5b4", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -168,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift", "state" : { - "revision" : "5dd1907d64f0d36f158f61a466bab75067224893", - "version" : "6.9.0" + "revision" : "5004a18539bd68905c5939aa893075f578f4f03d", + "version" : "6.9.1" } }, { diff --git a/MLS/MLS/Application/AppCoordinator.swift b/MLS/MLS/Application/AppCoordinator.swift index 1877aed6..947e4dea 100644 --- a/MLS/MLS/Application/AppCoordinator.swift +++ b/MLS/MLS/Application/AppCoordinator.swift @@ -10,9 +10,9 @@ import MyPageFeatureInterface import RxSwift -public final class AppCoordinator { +public final class AppCoordinator: AppCoordinatorProtocol { // MARK: - Properties - private let window: UIWindow + public var window: UIWindow? private let dictionaryMainViewFactory: DictionaryMainViewFactory private let bookmarkMainFactory: BookmarkMainFactory private let myPageMainFactory: MyPageMainFactory @@ -22,7 +22,7 @@ public final class AppCoordinator { // MARK: - Init public init( - window: UIWindow, + window: UIWindow?, dictionaryMainViewFactory: DictionaryMainViewFactory, bookmarkMainFactory: BookmarkMainFactory, myPageMainFactory: MyPageMainFactory, @@ -64,7 +64,7 @@ public final class AppCoordinator { // MARK: - Private Helper private func setRoot(_ viewController: UIViewController) { - window.rootViewController = viewController - window.makeKeyAndVisible() + window?.rootViewController = viewController + window?.makeKeyAndVisible() } } diff --git a/MLS/MLS/Application/AppDelegate.swift b/MLS/MLS/Application/AppDelegate.swift index 3312149e..01d3a82b 100644 --- a/MLS/MLS/Application/AppDelegate.swift +++ b/MLS/MLS/Application/AppDelegate.swift @@ -1,8 +1,5 @@ // swiftlint:disable function_body_length - -import os -import UIKit -import UserNotifications +// swiftlint:disable file_length import AuthFeature import AuthFeatureInterface @@ -17,11 +14,13 @@ import DictionaryFeature import DictionaryFeatureInterface import Domain import DomainInterface -import MyPageFeature -import MyPageFeatureInterface - import Firebase import KakaoSDKCommon +import MyPageFeature +import MyPageFeatureInterface +import os +import UIKit +import UserNotifications @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -62,7 +61,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) -> UISceneConfiguration { return UISceneConfiguration( name: "Default Configuration", - sessionRole: connectingSceneSession.role) + sessionRole: connectingSceneSession.role + ) } func application( @@ -76,16 +76,18 @@ extension AppDelegate: UNUserNotificationCenterDelegate, MessagingDelegate { func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, - withCompletionHandler completionHandler: @escaping ( - UNNotificationPresentationOptions - ) -> Void + withCompletionHandler completionHandler: + @escaping ( + UNNotificationPresentationOptions + ) -> Void ) { completionHandler([.list, .banner]) } // 파이어베이스 MessagingDelegate 설정 func messaging( - _ messaging: Messaging, didReceiveRegistrationToken fcmToken: String? + _ messaging: Messaging, + didReceiveRegistrationToken fcmToken: String? ) { let dataDict: [String: String] = ["token": fcmToken ?? ""] NotificationCenter.default.post( @@ -94,9 +96,12 @@ extension AppDelegate: UNUserNotificationCenterDelegate, MessagingDelegate { userInfo: dataDict ) let tokenUseCase = DIContainer.resolve( - type: SaveTokenToLocalUseCase.self) + type: SaveTokenToLocalUseCase.self + ) let result = tokenUseCase.execute( - type: .fcmToken, value: fcmToken ?? "") + type: .fcmToken, + value: fcmToken ?? "" + ) switch result { case .success: @@ -114,6 +119,22 @@ extension AppDelegate { registerRepository() registerUseCase() registerFactory() + + DIContainer.register(type: AppCoordinatorProtocol.self) { + AppCoordinator( + window: nil, + dictionaryMainViewFactory: DIContainer.resolve( + type: DictionaryMainViewFactory.self + ), + bookmarkMainFactory: DIContainer.resolve( + type: BookmarkMainFactory.self + ), + myPageMainFactory: DIContainer.resolve( + type: MyPageMainFactory.self + ), + loginFactory: DIContainer.resolve(type: LoginFactory.self) + ) + } } fileprivate func registerProvider() { @@ -121,19 +142,23 @@ extension AppDelegate { NetworkProviderImpl() } DIContainer.register( - type: SocialAuthenticatableProvider.self, name: "kakao" + type: SocialAuthenticatableProvider.self, + name: "kakao" ) { KakaoLoginProviderImpl() } DIContainer.register( - type: SocialAuthenticatableProvider.self, name: "apple" + type: SocialAuthenticatableProvider.self, + name: "apple" ) { AppleLoginProviderImpl() } DIContainer.register(type: Interceptor.self) { TokenInterceptor( fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self)) + type: FetchTokenFromLocalUseCase.self + ) + ) } } @@ -150,17 +175,20 @@ extension AppDelegate { DIContainer.register(type: DictionaryDetailAPIRepository.self) { DictionaryDetailAPIRepositoryImpl( provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self)) + tokenInterceptor: DIContainer.resolve(type: Interceptor.self) + ) } DIContainer.register(type: DictionaryListAPIRepository.self) { DictionaryListAPIRepositoryImpl( provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self)) + tokenInterceptor: DIContainer.resolve(type: Interceptor.self) + ) } DIContainer.register(type: BookmarkRepository.self) { BookmarkRepositoryImpl( provider: DIContainer.resolve(type: NetworkProvider.self), - interceptor: DIContainer.resolve(type: Interceptor.self)) + interceptor: DIContainer.resolve(type: Interceptor.self) + ) } DIContainer.register(type: UserDefaultsRepository.self) { UserDefaultsRepositoryImpl() @@ -168,28 +196,36 @@ extension AppDelegate { DIContainer.register(type: AlarmAPIRepository.self) { AlarmAPIRepositoryImpl( provider: DIContainer.resolve(type: NetworkProvider.self), - interceptor: DIContainer.resolve(type: Interceptor.self)) + interceptor: DIContainer.resolve(type: Interceptor.self) + ) } DIContainer.register(type: CollectionAPIRepository.self) { CollectionAPIRepositoryImpl( provider: DIContainer.resolve(type: NetworkProvider.self), - tokenInterceptor: DIContainer.resolve(type: Interceptor.self)) + tokenInterceptor: DIContainer.resolve(type: Interceptor.self) + ) } } fileprivate func registerUseCase() { DIContainer.register( - type: FetchSocialCredentialUseCase.self, name: "kakao" + type: FetchSocialCredentialUseCase.self, + name: "kakao" ) { let provider = DIContainer.resolve( - type: SocialAuthenticatableProvider.self, name: "kakao") + type: SocialAuthenticatableProvider.self, + name: "kakao" + ) return SocialLoginUseCaseImpl(provider: provider) } DIContainer.register( - type: FetchSocialCredentialUseCase.self, name: "apple" + type: FetchSocialCredentialUseCase.self, + name: "apple" ) { let provider = DIContainer.resolve( - type: SocialAuthenticatableProvider.self, name: "apple") + type: SocialAuthenticatableProvider.self, + name: "apple" + ) return SocialLoginUseCaseImpl(provider: provider) } DIContainer.register(type: CheckEmptyLevelAndRoleUseCase.self) { @@ -200,62 +236,80 @@ extension AppDelegate { } DIContainer.register(type: FetchJobListUseCase.self) { FetchJobListUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: LoginWithAppleUseCase.self) { LoginWithAppleUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self), + type: AuthAPIRepository.self + ), tokenRepository: DIContainer.resolve( - type: TokenRepository.self), + type: TokenRepository.self + ), userDefaultsRepository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: LoginWithKakaoUseCase.self) { LoginWithKakaoUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self), + type: AuthAPIRepository.self + ), tokenRepository: DIContainer.resolve( - type: TokenRepository.self), + type: TokenRepository.self + ), userDefaultsRepository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: SignUpWithAppleUseCase.self) { SignUpWithAppleUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: SignUpWithKakaoUseCase.self) { SignUpWithKakaoUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: UpdateUserInfoUseCase.self) { UpdateUserInfoUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: ReissueUseCase.self) { ReissueUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: PutFCMTokenUseCase.self) { PutFCMTokenUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: FetchTokenFromLocalUseCase.self) { FetchTokenFromLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self)) + repository: DIContainer.resolve(type: TokenRepository.self) + ) } DIContainer.register(type: SaveTokenToLocalUseCase.self) { SaveTokenToLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self)) + repository: DIContainer.resolve(type: TokenRepository.self) + ) } DIContainer.register(type: DeleteTokenFromLocalUseCase.self) { DeleteTokenFromLocalUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self)) + repository: DIContainer.resolve(type: TokenRepository.self) + ) } DIContainer.register(type: UpdateMarketingAgreementUseCase.self) { UpdateMarketingAgreementUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self), + type: AuthAPIRepository.self + ), tokenRepository: DIContainer.resolve(type: TokenRepository.self) ) } @@ -268,183 +322,245 @@ extension AppDelegate { DIContainer.register(type: UpdateNotificationAgreementUseCase.self) { UpdateNotificationAgreementUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self)) + type: AuthAPIRepository.self + ) + ) } DIContainer.register(type: CheckLoginUseCase.self) { CheckLoginUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self), + type: AuthAPIRepository.self + ), tokenRepository: DIContainer.resolve(type: TokenRepository.self) ) } DIContainer.register(type: FetchDictionaryAllListUseCase.self) { FetchDictionaryAllListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: SetBookmarkUseCase.self) { SetBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchPlatformUseCase.self) { FetchPlatformUseCaseImpl( repository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryMapListUseCase.self) { FetchDictionaryMapListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryItemListUseCase.self) { FetchDictionaryItemListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryQuestListUseCase.self) { FetchDictionaryQuestListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryNpcListUseCase.self) { FetchDictionaryNpcListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryMonsterListUseCase.self) { FetchDictionaryMonsterListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailMonsterUseCase.self) { FetchDictionaryDetailMonsterUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register( type: FetchDictionaryDetailMonsterItemsUseCase.self ) { FetchDictionaryDetailMonsterDropItemUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailMonsterMapUseCase.self) { FetchDictionaryDetailMonsterMapUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailNpcUseCase.self) { FetchDictionaryDetailNpcUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailNpcQuestUseCase.self) { FetchDictionaryDetailNpcQuestUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailNpcMapUseCase.self) { FetchDictionaryDetailNpcMapUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailItemUseCase.self) { FetchDictionaryDetailItemUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register( type: FetchDictionaryDetailItemDropMonsterUseCase.self ) { FetchDictionaryDetailItemDropMonsterUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailQuestUseCase.self) { FetchDictionaryDetailQuestUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register( type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self ) { FetchDictionaryDetailQuestLinkedQuestsUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailMapUseCase.self) { FetchDictionaryDetailMapUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register( type: FetchDictionaryDetailMapSpawnMonsterUseCase.self ) { FetchDictionaryDetailMapSpawnMonsterUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryDetailMapNpcUseCase.self) { FetchDictionaryDetailMapNpcUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryDetailAPIRepository.self)) + type: DictionaryDetailAPIRepository.self + ) + ) } DIContainer.register(type: RecentSearchRemoveUseCase.self) { RecentSearchRemoveUseCaseImpl( repository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: RecentSearchAddUseCase.self) { RecentSearchAddUseCaseImpl( repository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: FetchDictionaryListCountUseCase.self) { FetchDictionaryListCountUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: FetchDictionarySearchListUseCase.self) { FetchDictionarySearchListUseCaseImpl( repository: DIContainer.resolve( - type: DictionaryListAPIRepository.self)) + type: DictionaryListAPIRepository.self + ) + ) } DIContainer.register(type: RecentSearchFetchUseCase.self) { RecentSearchFetchUseCaseImpl( repository: DIContainer.resolve( - type: UserDefaultsRepository.self)) + type: UserDefaultsRepository.self + ) + ) } DIContainer.register(type: FetchBookmarkUseCase.self) { FetchBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchMonsterBookmarkUseCase.self) { FetchMonsterBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchItemBookmarkUseCase.self) { FetchItemBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchNPCBookmarkUseCase.self) { FetchNPCBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchQuestBookmarkUseCase.self) { FetchQuestBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: FetchMapBookmarkUseCase.self) { FetchMapBookmarkUseCaseImpl( - repository: DIContainer.resolve(type: BookmarkRepository.self)) + repository: DIContainer.resolve(type: BookmarkRepository.self) + ) } DIContainer.register(type: UpdateProfileImageUseCase.self) { UpdateProfileImageUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: FetchJobUseCase.self) { FetchJobUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: FetchProfileUseCase.self) { FetchProfileUseCaseImpl( @@ -457,42 +573,51 @@ extension AppDelegate { } DIContainer.register(type: UpdateNickNameUseCase.self) { UpdateNickNameUseCaseImpl( - repository: DIContainer.resolve(type: AuthAPIRepository.self)) + repository: DIContainer.resolve(type: AuthAPIRepository.self) + ) } DIContainer.register(type: LogoutUseCase.self) { LogoutUseCaseImpl( - repository: DIContainer.resolve(type: TokenRepository.self)) + repository: DIContainer.resolve(type: TokenRepository.self) + ) } DIContainer.register(type: WithdrawUseCase.self) { WithdrawUseCaseImpl( authRepository: DIContainer.resolve( - type: AuthAPIRepository.self), + type: AuthAPIRepository.self + ), tokenRepository: DIContainer.resolve(type: TokenRepository.self) ) } DIContainer.register(type: FetchNoticesUseCase.self) { FetchNoticesUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: FetchOngoingEventsUseCase.self) { FetchOngoingEventsUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: FetchOutdatedEventsUseCase.self) { FetchOutdatedEventsUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: FetchPatchNotesUseCase.self) { FetchPatchNotesUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: SetReadUseCase.self) { SetReadUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: FetchAllAlarmUseCase.self) { FetchAllAlarmUseCaseImpl( - repository: DIContainer.resolve(type: AlarmAPIRepository.self)) + repository: DIContainer.resolve(type: AlarmAPIRepository.self) + ) } DIContainer.register(type: ParseItemFilterResultUseCase.self) { ParseItemFilterResultUseCaseImpl() @@ -500,30 +625,50 @@ extension AppDelegate { DIContainer.register(type: FetchCollectionListUseCase.self) { FetchCollectionListUseCaseImpl( repository: DIContainer.resolve( - type: CollectionAPIRepository.self)) + type: CollectionAPIRepository.self + ) + ) } DIContainer.register(type: CreateCollectionListUseCase.self) { CreateCollectionListUseCaseImpl( repository: DIContainer.resolve( - type: CollectionAPIRepository.self)) + type: CollectionAPIRepository.self + ) + ) } DIContainer.register(type: FetchCollectionUseCase.self) { - FetchCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + FetchCollectionUseCaseImpl( + repository: DIContainer.resolve( + type: CollectionAPIRepository.self + ) + ) } -// DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { -// AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) -// } + // DIContainer.register(type: AddCollectionsToBookmarkUseCase.self) { + // AddCollectionsToBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + // } DIContainer.register(type: SetCollectionUseCase.self) { - SetCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + SetCollectionUseCaseImpl( + repository: DIContainer.resolve( + type: CollectionAPIRepository.self + ) + ) } DIContainer.register(type: DeleteCollectionUseCase.self) { - DeleteCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + DeleteCollectionUseCaseImpl( + repository: DIContainer.resolve( + type: CollectionAPIRepository.self + ) + ) } -// DIContainer.register(type: AddBookmarksToCollectionUseCase.self) { -// AddBookmarksToCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) -// } + // DIContainer.register(type: AddBookmarksToCollectionUseCase.self) { + // AddBookmarksToCollectionUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + // } DIContainer.register(type: AddCollectionAndBookmarkUseCase.self) { - AddCollectionAndBookmarkUseCaseImpl(repository: DIContainer.resolve(type: CollectionAPIRepository.self)) + AddCollectionAndBookmarkUseCaseImpl( + repository: DIContainer.resolve( + type: CollectionAPIRepository.self + ) + ) } } @@ -538,143 +683,216 @@ extension AppDelegate { SortedBottomSheetFactoryImpl() } DIContainer.register(type: AddCollectionFactory.self) { - AddCollectionFactoryImpl(createCollectionListUseCase: DIContainer.resolve(type: CreateCollectionListUseCase.self), setCollectionUseCase: DIContainer.resolve(type: SetCollectionUseCase.self)) + AddCollectionFactoryImpl( + createCollectionListUseCase: DIContainer.resolve( + type: CreateCollectionListUseCase.self + ), + setCollectionUseCase: DIContainer.resolve( + type: SetCollectionUseCase.self + ) + ) } DIContainer.register(type: BookmarkModalFactory.self) { BookmarkModalFactoryImpl( addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self), fetchCollectionListUseCase: DIContainer.resolve(type: FetchCollectionListUseCase.self), - addCollectionAndBookmarkUseCase: DIContainer.resolve(type: AddCollectionAndBookmarkUseCase.self) + type: AddCollectionFactory.self + ), + fetchCollectionListUseCase: DIContainer.resolve( + type: FetchCollectionListUseCase.self + ), + addCollectionAndBookmarkUseCase: DIContainer.resolve( + type: AddCollectionAndBookmarkUseCase.self + ) ) } DIContainer.register(type: LoginFactory.self) { LoginFactoryImpl( termsAgreementsFactory: DIContainer.resolve( - type: TermsAgreementFactory.self), + type: TermsAgreementFactory.self + ), appleLoginUseCase: DIContainer.resolve( - type: FetchSocialCredentialUseCase.self, name: "apple"), + type: FetchSocialCredentialUseCase.self, + name: "apple" + ), kakaoLoginUseCase: DIContainer.resolve( - type: FetchSocialCredentialUseCase.self, name: "kakao"), + type: FetchSocialCredentialUseCase.self, + name: "kakao" + ), loginWithAppleUseCase: DIContainer.resolve( - type: LoginWithAppleUseCase.self), + type: LoginWithAppleUseCase.self + ), loginWithKakaoUseCase: DIContainer.resolve( - type: LoginWithKakaoUseCase.self), + type: LoginWithKakaoUseCase.self + ), fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self), + type: FetchTokenFromLocalUseCase.self + ), putFCMTokenUseCase: DIContainer.resolve( - type: PutFCMTokenUseCase.self), + type: PutFCMTokenUseCase.self + ), fetchPlatformUseCase: DIContainer.resolve( - type: FetchPlatformUseCase.self) + type: FetchPlatformUseCase.self + ) ) } DIContainer.register(type: DictionaryDetailFactory.self) { DictionaryDetailFactoryImpl( loginFactory: { DIContainer.resolve(type: LoginFactory.self) }, bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self), + type: BookmarkModalFactory.self + ), + dictionaryDetailFactory: { + DIContainer + .resolve(type: DictionaryDetailFactory.self) + }, + appCoordinator: { + DIContainer.resolve(type: AppCoordinatorProtocol.self) + }, dictionaryDetailMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapUseCase.self), + type: FetchDictionaryDetailMapUseCase.self + ), dictionaryDetailMapSpawnMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapSpawnMonsterUseCase.self), + type: FetchDictionaryDetailMapSpawnMonsterUseCase.self + ), dictionaryDetailMapNpcUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMapNpcUseCase.self), + type: FetchDictionaryDetailMapNpcUseCase.self + ), dictionaryDetailQuestLinkedQuestsUseCase: DIContainer.resolve( - type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self), + type: FetchDictionaryDetailQuestLinkedQuestsUseCase.self + ), dictionaryDetailQuestUseCase: DIContainer.resolve( - type: FetchDictionaryDetailQuestUseCase.self), + type: FetchDictionaryDetailQuestUseCase.self + ), dictionaryDetailItemDropMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailItemDropMonsterUseCase.self), + type: FetchDictionaryDetailItemDropMonsterUseCase.self + ), dictionaryDetailItemUseCase: DIContainer.resolve( - type: FetchDictionaryDetailItemUseCase.self), + type: FetchDictionaryDetailItemUseCase.self + ), dictionaryDetailNpcUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcUseCase.self), + type: FetchDictionaryDetailNpcUseCase.self + ), dictionaryDetailNpcQuestUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcQuestUseCase.self), + type: FetchDictionaryDetailNpcQuestUseCase.self + ), dictionaryDetailNpcMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailNpcMapUseCase.self), + type: FetchDictionaryDetailNpcMapUseCase.self + ), dictionaryDetailMonsterUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterUseCase.self), + type: FetchDictionaryDetailMonsterUseCase.self + ), dictionaryDetailMonsterDropItemUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterItemsUseCase.self), + type: FetchDictionaryDetailMonsterItemsUseCase.self + ), dictionaryDetailMonsterMapUseCase: DIContainer.resolve( - type: FetchDictionaryDetailMonsterMapUseCase.self), + type: FetchDictionaryDetailMonsterMapUseCase.self + ), checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self), + type: CheckLoginUseCase.self + ), setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self)) + type: SetBookmarkUseCase.self + ) + ) } DIContainer.register(type: DictionaryMainListFactory.self) { DictionaryListFactoryImpl( checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self), + type: CheckLoginUseCase.self + ), dictionaryAllListItemUseCase: DIContainer.resolve( - type: FetchDictionaryAllListUseCase.self), + type: FetchDictionaryAllListUseCase.self + ), dictionaryMapListItemUseCase: DIContainer.resolve( - type: FetchDictionaryMapListUseCase.self), + type: FetchDictionaryMapListUseCase.self + ), dictionaryItemListItemUseCase: DIContainer.resolve( - type: FetchDictionaryItemListUseCase.self), + type: FetchDictionaryItemListUseCase.self + ), dictionaryQuestListItemUseCase: DIContainer.resolve( - type: FetchDictionaryQuestListUseCase.self), + type: FetchDictionaryQuestListUseCase.self + ), dictionaryNpcListItemUseCase: DIContainer .resolve(type: FetchDictionaryNpcListUseCase.self), dictionaryListItemUseCase: DIContainer.resolve( - type: FetchDictionaryMonsterListUseCase.self), + type: FetchDictionaryMonsterListUseCase.self + ), setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self), + type: SetBookmarkUseCase.self + ), parseItemFilterResultUseCase: DIContainer.resolve( - type: ParseItemFilterResultUseCase.self), + type: ParseItemFilterResultUseCase.self + ), itemFilterFactory: DIContainer.resolve( - type: ItemFilterBottomSheetFactory.self), + type: ItemFilterBottomSheetFactory.self + ), monsterFilterFactory: DIContainer.resolve( - type: MonsterFilterBottomSheetFactory.self), + type: MonsterFilterBottomSheetFactory.self + ), sortedFactory: DIContainer.resolve( - type: SortedBottomSheetFactory.self), + type: SortedBottomSheetFactory.self + ), bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self), + type: BookmarkModalFactory.self + ), detailFactory: DIContainer.resolve( - type: DictionaryDetailFactory.self), + type: DictionaryDetailFactory.self + ), loginFactory: { DIContainer.resolve(type: LoginFactory.self) } ) } DIContainer.register(type: DictionarySearchResultFactory.self) { DictionarySearchResultFactoryImpl( dictionaryListCountUseCase: DIContainer.resolve( - type: FetchDictionaryListCountUseCase.self), + type: FetchDictionaryListCountUseCase.self + ), dictionaryMainListFactory: DIContainer .resolve(type: DictionaryMainListFactory.self), dictionarySearchListUseCase: DIContainer.resolve( - type: FetchDictionarySearchListUseCase.self) + type: FetchDictionarySearchListUseCase.self + ) ) } DIContainer.register(type: DictionarySearchFactory.self) { DictionarySearchFactoryImpl( recentSearchRemoveUseCase: DIContainer.resolve( - type: RecentSearchRemoveUseCase.self), + type: RecentSearchRemoveUseCase.self + ), recentSearchAddUseCase: DIContainer.resolve( - type: RecentSearchAddUseCase.self), + type: RecentSearchAddUseCase.self + ), searchResultFactory: DIContainer .resolve(type: DictionarySearchResultFactory.self), recentSearchFetchUseCase: DIContainer.resolve( - type: RecentSearchFetchUseCase.self)) + type: RecentSearchFetchUseCase.self + ) + ) } DIContainer.register(type: NotificationSettingFactory.self) { NotificationSettingFactoryImpl( checkNotificationPermissionUseCase: DIContainer.resolve( - type: CheckNotificationPermissionUseCase.self), + type: CheckNotificationPermissionUseCase.self + ), updateNotificationAgreementUseCase: DIContainer.resolve( - type: UpdateNotificationAgreementUseCase.self)) + type: UpdateNotificationAgreementUseCase.self + ) + ) } DIContainer.register(type: DictionaryNotificationFactory.self) { DictionaryNotificationFactoryImpl( notificationSettingFactory: DIContainer.resolve( - type: NotificationSettingFactory.self), + type: NotificationSettingFactory.self + ), fetchAllAlarmUseCase: DIContainer.resolve( - type: FetchAllAlarmUseCase.self), + type: FetchAllAlarmUseCase.self + ), fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self)) + type: FetchProfileUseCase.self + ) + ) } DIContainer.register(type: DictionaryMainViewFactory.self) { DictionaryMainViewFactoryImpl( @@ -682,12 +900,14 @@ extension AppDelegate { DIContainer .resolve(type: DictionaryMainListFactory.self), searchFactory: DIContainer.resolve( - type: DictionarySearchFactory.self), + type: DictionarySearchFactory.self + ), notificationFactory: DIContainer .resolve(type: DictionaryNotificationFactory.self), checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self) + type: CheckLoginUseCase.self + ) ) } DIContainer.register(type: OnBoardingNotificationSheetFactory.self) { @@ -702,51 +922,77 @@ extension AppDelegate { DIContainer .resolve(type: UpdateNotificationAgreementUseCase.self), updateUserInfoUseCase: DIContainer.resolve( - type: UpdateUserInfoUseCase.self), - dictionaryMainViewFactory: DIContainer.resolve( - type: DictionaryMainViewFactory.self) + type: UpdateUserInfoUseCase.self + ), + appCoordinator: { + DIContainer.resolve( + type: AppCoordinatorProtocol.self + ) + } ) } DIContainer.register(type: OnBoardingInputFactory.self) { OnBoardingInputFactoryImpl( checkEmptyUseCase: DIContainer.resolve( - type: CheckEmptyLevelAndRoleUseCase.self), + type: CheckEmptyLevelAndRoleUseCase.self + ), checkValidLevelUseCase: DIContainer.resolve( - type: CheckValidLevelUseCase.self), + type: CheckValidLevelUseCase.self + ), fetchJobListUseCase: DIContainer.resolve( - type: FetchJobListUseCase.self), + type: FetchJobListUseCase.self + ), updateUserInfoUseCase: DIContainer.resolve( - type: UpdateUserInfoUseCase.self), + type: UpdateUserInfoUseCase.self + ), onBoardingNotificationFactory: DIContainer.resolve( - type: OnBoardingNotificationFactory.self) + type: OnBoardingNotificationFactory.self + ), + appCoordinator: { + DIContainer.resolve(type: AppCoordinatorProtocol.self) + } ) } DIContainer.register(type: OnBoardingQuestionFactory.self) { OnBoardingQuestionFactoryImpl( onBoardingInputFactory: DIContainer.resolve( - type: OnBoardingInputFactory.self) + type: OnBoardingInputFactory.self + ) ) } DIContainer.register(type: TermsAgreementFactory.self) { TermsAgreementFactoryImpl( onBoardingQuestionFactory: DIContainer.resolve( - type: OnBoardingQuestionFactory.self), + type: OnBoardingQuestionFactory.self + ), signUpWithKakaoUseCase: DIContainer.resolve( - type: SignUpWithKakaoUseCase.self), + type: SignUpWithKakaoUseCase.self + ), signUpWithAppleUseCase: DIContainer.resolve( - type: SignUpWithAppleUseCase.self), + type: SignUpWithAppleUseCase.self + ), saveTokenUseCase: DIContainer.resolve( - type: SaveTokenToLocalUseCase.self), + type: SaveTokenToLocalUseCase.self + ), fetchTokenUseCase: DIContainer.resolve( - type: FetchTokenFromLocalUseCase.self), + type: FetchTokenFromLocalUseCase.self + ), updateMarketingAgreementUseCase: DIContainer.resolve( - type: UpdateMarketingAgreementUseCase.self) + type: UpdateMarketingAgreementUseCase.self + ) ) } DIContainer.register(type: OnBoardingNotificationFactory.self) { OnBoardingNotificationFactoryImpl( onBoardingNotificationSheetFactory: DIContainer.resolve( - type: OnBoardingNotificationSheetFactory.self)) + type: OnBoardingNotificationSheetFactory.self + ), + appCoordinator: { + DIContainer.resolve( + type: AppCoordinatorProtocol.self + ) + } + ) } DIContainer.register(type: BookmarkMainFactory.self) { BookmarkMainFactoryImpl( @@ -776,46 +1022,68 @@ extension AppDelegate { DIContainer.register(type: BookmarkListFactory.self) { BookmarkListFactoryImpl( itemFilterFactory: DIContainer.resolve( - type: ItemFilterBottomSheetFactory.self), + type: ItemFilterBottomSheetFactory.self + ), monsterFilterFactory: DIContainer.resolve( - type: MonsterFilterBottomSheetFactory.self), + type: MonsterFilterBottomSheetFactory.self + ), sortedFactory: DIContainer.resolve( - type: SortedBottomSheetFactory.self), + type: SortedBottomSheetFactory.self + ), bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self), + type: BookmarkModalFactory.self + ), loginFactory: DIContainer.resolve(type: LoginFactory.self), dictionaryDetailFactory: DIContainer.resolve( - type: DictionaryDetailFactory.self), + type: DictionaryDetailFactory.self + ), collectionEditFactory: DIContainer.resolve( - type: CollectionEditFactory.self), + type: CollectionEditFactory.self + ), setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self), + type: SetBookmarkUseCase.self + ), checkLoginUseCase: DIContainer.resolve( - type: CheckLoginUseCase.self), + type: CheckLoginUseCase.self + ), fetchBookmarkUseCase: DIContainer.resolve( - type: FetchBookmarkUseCase.self), + type: FetchBookmarkUseCase.self + ), fetchMonsterBookmarkUseCase: DIContainer.resolve( - type: FetchMonsterBookmarkUseCase.self), + type: FetchMonsterBookmarkUseCase.self + ), fetchItemBookmarkUseCase: DIContainer.resolve( - type: FetchItemBookmarkUseCase.self), + type: FetchItemBookmarkUseCase.self + ), fetchNPCBookmarkUseCase: DIContainer.resolve( - type: FetchNPCBookmarkUseCase.self), + type: FetchNPCBookmarkUseCase.self + ), fetchQuestBookmarkUseCase: DIContainer.resolve( - type: FetchQuestBookmarkUseCase.self), + type: FetchQuestBookmarkUseCase.self + ), fetchMapBookmarkUseCase: DIContainer.resolve( - type: FetchMapBookmarkUseCase.self), - parseItemFilterResultUseCase: DIContainer.resolve(type: ParseItemFilterResultUseCase.self)) + type: FetchMapBookmarkUseCase.self + ), + parseItemFilterResultUseCase: DIContainer.resolve( + type: ParseItemFilterResultUseCase.self + ) + ) } DIContainer.register(type: CollectionListFactory.self) { CollectionListFactoryImpl( fetchCollectionListUseCase: DIContainer.resolve( - type: FetchCollectionListUseCase.self), + type: FetchCollectionListUseCase.self + ), addCollectionFactory: DIContainer.resolve( - type: AddCollectionFactory.self), + type: AddCollectionFactory.self + ), bookmarkDetailFactory: DIContainer.resolve( - type: CollectionDetailFactory.self), - sortedBottomSheetFactory: DIContainer - .resolve(type: SortedBottomSheetFactory.self)) + type: CollectionDetailFactory.self + ), + sortedBottomSheetFactory: + DIContainer + .resolve(type: SortedBottomSheetFactory.self) + ) } DIContainer.register(type: CollectionDetailFactory.self) { CollectionDetailFactoryImpl( @@ -838,10 +1106,14 @@ extension AppDelegate { DIContainer .resolve(type: SetBookmarkUseCase.self), fetchCollectionUseCase: DIContainer.resolve( - type: FetchCollectionUseCase.self), - deleteCollectionUseCase: DIContainer + type: FetchCollectionUseCase.self + ), + deleteCollectionUseCase: + DIContainer .resolve(type: DeleteCollectionUseCase.self), - addCollectionAndBookmarkUseCase: DIContainer.resolve(type: AddCollectionAndBookmarkUseCase.self) + addCollectionAndBookmarkUseCase: DIContainer.resolve( + type: AddCollectionAndBookmarkUseCase.self + ) ) } DIContainer.register(type: CollectionSettingFactory.self) { @@ -850,9 +1122,12 @@ extension AppDelegate { DIContainer.register(type: CollectionEditFactory.self) { CollectionEditFactoryImpl( setBookmarkUseCase: DIContainer.resolve( - type: SetBookmarkUseCase.self), + type: SetBookmarkUseCase.self + ), bookmarkModalFactory: DIContainer.resolve( - type: BookmarkModalFactory.self)) + type: BookmarkModalFactory.self + ) + ) } DIContainer.register(type: MyPageMainFactory.self) { MyPageMainFactoryImpl( @@ -870,34 +1145,46 @@ extension AppDelegate { DIContainer .resolve(type: SetCharacterFactory.self), fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self) + type: FetchProfileUseCase.self + ) ) } DIContainer.register(type: CustomerSupportFactory.self) { CustomerSupportBaseViewFactoryImpl( fetchNoticesUseCase: DIContainer.resolve( - type: FetchNoticesUseCase.self), + type: FetchNoticesUseCase.self + ), fetchOngoingEventsUseCase: DIContainer.resolve( - type: FetchOngoingEventsUseCase.self), + type: FetchOngoingEventsUseCase.self + ), fetchOutdatedEventsUseCase: DIContainer.resolve( - type: FetchOutdatedEventsUseCase.self), + type: FetchOutdatedEventsUseCase.self + ), fetchPatchNotesUseCase: DIContainer.resolve( - type: FetchPatchNotesUseCase.self), - setReadUseCase: DIContainer.resolve(type: SetReadUseCase.self)) + type: FetchPatchNotesUseCase.self + ), + setReadUseCase: DIContainer.resolve(type: SetReadUseCase.self) + ) } DIContainer.register(type: SetProfileFactory.self) { SetProfileFactoryImpl( selectImageFactory: DIContainer.resolve( - type: SelectImageFactory.self), + type: SelectImageFactory.self + ), checkNickNameUseCase: DIContainer.resolve( - type: CheckNickNameUseCase.self), + type: CheckNickNameUseCase.self + ), updateNickNameUseCase: DIContainer.resolve( - type: UpdateNickNameUseCase.self), + type: UpdateNickNameUseCase.self + ), logoutUseCase: DIContainer.resolve(type: LogoutUseCase.self), withdrawUseCase: DIContainer.resolve( - type: WithdrawUseCase.self), + type: WithdrawUseCase.self + ), fetchProfileUseCase: DIContainer.resolve( - type: FetchProfileUseCase.self)) + type: FetchProfileUseCase.self + ) + ) } DIContainer.register(type: SetCharacterFactory.self) { SetCharacterFactoryImpl( @@ -918,7 +1205,9 @@ extension AppDelegate { DIContainer.register(type: SelectImageFactory.self) { SelectImageFactoryImpl( updateProfileImageUseCase: DIContainer.resolve( - type: UpdateProfileImageUseCase.self)) + type: UpdateProfileImageUseCase.self + ) + ) } } } diff --git a/MLS/MLS/Application/SceneDelegate.swift b/MLS/MLS/Application/SceneDelegate.swift index f30fc210..929185d5 100644 --- a/MLS/MLS/Application/SceneDelegate.swift +++ b/MLS/MLS/Application/SceneDelegate.swift @@ -1,6 +1,7 @@ import UIKit import AuthFeatureInterface +import BaseFeature import BookmarkFeatureInterface import Core import DictionaryFeatureInterface @@ -12,7 +13,7 @@ import RxSwift final class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - var appCoordinator: AppCoordinator? + var appCoordinator: AppCoordinatorProtocol? var disposeBag = DisposeBag() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -22,18 +23,8 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { window.makeKeyAndVisible() self.window = window - let dictionaryMainViewFactory: DictionaryMainViewFactory = DIContainer.resolve(type: DictionaryMainViewFactory.self) - let bookmarkMainFactory: BookmarkMainFactory = DIContainer.resolve(type: BookmarkMainFactory.self) - let myPageMainFactory: MyPageMainFactory = DIContainer.resolve(type: MyPageMainFactory.self) - let loginFactory: LoginFactory = DIContainer.resolve(type: LoginFactory.self) - - let coordinator = AppCoordinator( - window: window, - dictionaryMainViewFactory: dictionaryMainViewFactory, - bookmarkMainFactory: bookmarkMainFactory, - myPageMainFactory: myPageMainFactory, - loginFactory: loginFactory - ) + let coordinator = DIContainer.resolve(type: AppCoordinatorProtocol.self) + coordinator.window = window self.appCoordinator = coordinator startScene(coordinator: coordinator) @@ -47,7 +38,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - private func startScene(coordinator: AppCoordinator) { + private func startScene(coordinator: AppCoordinatorProtocol) { let fetchTokenUseCase = DIContainer.resolve(type: FetchTokenFromLocalUseCase.self) let reissueUseCase = DIContainer.resolve(type: ReissueUseCase.self) let saveTokenUseCase = DIContainer.resolve(type: SaveTokenToLocalUseCase.self) @@ -70,8 +61,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { coordinator.showLogin(exitRoute: .home) } }, - onError: { error in - print(error) + onError: { _ in coordinator.showLogin(exitRoute: .home) } ) diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift index 8d0c92bf..dcba72ae 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationFactoryImpl.swift @@ -1,15 +1,19 @@ import AuthFeatureInterface import BaseFeature +import DictionaryFeatureInterface public struct OnBoardingNotificationFactoryImpl: OnBoardingNotificationFactory { private let onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory + private let appCoordinator: () -> AppCoordinatorProtocol - public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory) { + public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory, appCoordinator: @escaping () -> AppCoordinatorProtocol) { self.onBoardingNotificationSheetFactory = onBoardingNotificationSheetFactory + self.appCoordinator = appCoordinator } public func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController { - let viewController = OnBoardingNotificationViewController(onBoardingNotificationSheetFactory: onBoardingNotificationSheetFactory) + let viewController = OnBoardingNotificationViewController(onBoardingNotificationSheetFactory: onBoardingNotificationSheetFactory, appCoordinator: appCoordinator()) + viewController.isBottomTabbarHidden = true viewController.reactor = OnBoardingNotificationReactor(selectedLevel: selectedLevel, selectedJobID: selectedJobID) return viewController } diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift index 43ca062c..4d4029b4 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationReactor.swift @@ -6,10 +6,12 @@ public final class OnBoardingNotificationReactor: Reactor { public enum Route { case none case notificationAlert + case home } public enum Action { case nextButtonTapped + case skipButtonTapped } public enum Mutation { @@ -35,7 +37,9 @@ public final class OnBoardingNotificationReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .nextButtonTapped: - return Observable.just(.navigateTo(route: .notificationAlert)) + return .just(.navigateTo(route: .notificationAlert)) + case .skipButtonTapped: + return .just(.navigateTo(route: .home)) } } diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift index 4606d009..62aec0ad 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoadingNotification/OnBoardingNotificationViewController.swift @@ -3,6 +3,7 @@ import UserNotifications import AuthFeatureInterface import BaseFeature +import DictionaryFeatureInterface import ReactorKit import RxCocoa @@ -16,13 +17,15 @@ public class OnBoardingNotificationViewController: BaseViewController, View { public var disposeBag = DisposeBag() private let onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory + private let appCoordinator: AppCoordinatorProtocol // MARK: - Components private var mainView = OnBoardingNotificationView() - public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory) { + public init(onBoardingNotificationSheetFactory: OnBoardingNotificationSheetFactory, appCoordinator: AppCoordinatorProtocol) { self.onBoardingNotificationSheetFactory = onBoardingNotificationSheetFactory + self.appCoordinator = appCoordinator super.init() } @@ -73,6 +76,11 @@ public extension OnBoardingNotificationViewController { .map { Reactor.Action.nextButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + mainView.headerView.underlineTextButton.rx.tap + .map { Reactor.Action.skipButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) } func bindViewState(reactor: Reactor) { @@ -80,11 +88,14 @@ public extension OnBoardingNotificationViewController { .take(1) .flatMapLatest { _ in reactor.pulse(\.$route) } .withUnretained(self) + .observe(on: MainScheduler.instance) .subscribe { owner, route in switch route { case .notificationAlert: let viewController = owner.onBoardingNotificationSheetFactory.make(selectedLevel: reactor.currentState.selectedLevel, selectedJobID: reactor.currentState.selectedJobID) owner.presentModal(viewController) + case .home: + owner.appCoordinator.showMainTab() default: break } diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift index 3267186e..19243aeb 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputFactoryImpl.swift @@ -1,5 +1,6 @@ import AuthFeatureInterface import BaseFeature +import DictionaryFeatureInterface import DomainInterface public struct OnBoardingInputFactoryImpl: OnBoardingInputFactory { @@ -8,23 +9,27 @@ public struct OnBoardingInputFactoryImpl: OnBoardingInputFactory { private let fetchJobListUseCase: FetchJobListUseCase private let updateUserInfoUseCase: UpdateUserInfoUseCase private let onBoardingNotificationFactory: OnBoardingNotificationFactory + private let appCoordinator: () -> AppCoordinatorProtocol public init( checkEmptyUseCase: CheckEmptyLevelAndRoleUseCase, checkValidLevelUseCase: CheckValidLevelUseCase, fetchJobListUseCase: FetchJobListUseCase, updateUserInfoUseCase: UpdateUserInfoUseCase, - onBoardingNotificationFactory: OnBoardingNotificationFactory + onBoardingNotificationFactory: OnBoardingNotificationFactory, + appCoordinator: @escaping () -> AppCoordinatorProtocol ) { self.checkEmptyUseCase = checkEmptyUseCase self.checkValidLevelUseCase = checkValidLevelUseCase self.fetchJobListUseCase = fetchJobListUseCase self.updateUserInfoUseCase = updateUserInfoUseCase self.onBoardingNotificationFactory = onBoardingNotificationFactory + self.appCoordinator = appCoordinator } public func make() -> BaseViewController { - let viewController = OnBoardingInputViewController(onBoardingNotificationFactory: onBoardingNotificationFactory) + let viewController = OnBoardingInputViewController(onBoardingNotificationFactory: onBoardingNotificationFactory, appCoordinator: appCoordinator()) + viewController.isBottomTabbarHidden = true viewController.reactor = OnBoardingInputReactor( checkEmptyUseCase: checkEmptyUseCase, checkValidLevelUseCase: checkValidLevelUseCase, diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift index 93ec8eb6..1da435e4 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingInput/OnBoardingInputViewController.swift @@ -3,6 +3,7 @@ import UIKit import AuthFeatureInterface import BaseFeature import DesignSystem +import DictionaryFeatureInterface import ReactorKit import RxCocoa @@ -17,13 +18,15 @@ public class OnBoardingInputViewController: BaseViewController, View { public var disposeBag = DisposeBag() private let onBoardingNotificationFactory: OnBoardingNotificationFactory + private let appCoordinator: AppCoordinatorProtocol // MARK: - Components private var mainView = OnBoardingInputView() - init(onBoardingNotificationFactory: OnBoardingNotificationFactory) { + init(onBoardingNotificationFactory: OnBoardingNotificationFactory, appCoordinator: AppCoordinatorProtocol) { self.onBoardingNotificationFactory = onBoardingNotificationFactory + self.appCoordinator = appCoordinator super.init() } } @@ -140,9 +143,7 @@ public extension OnBoardingInputViewController { case .dismiss: owner.navigationController?.popViewController(animated: true) case .home: - let controller = UIViewController() - controller.view.backgroundColor = .green - owner.navigationController?.pushViewController(controller, animated: true) + owner.appCoordinator.showMainTab() case .error: let errorViewController = BaseErrorViewController() owner.present(errorViewController, animated: true) diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift index 85de382f..97613d4e 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetFactoryImpl.swift @@ -8,24 +8,25 @@ public struct OnBoardingNotificationSheetFactoryImpl: OnBoardingNotificationShee private let openNotificationSettingUseCase: OpenNotificationSettingUseCase private let updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase private let updateUserInfoUseCase: UpdateUserInfoUseCase - private let dictionaryMainViewFactory: DictionaryMainViewFactory + private let appCoordinator: () -> AppCoordinatorProtocol public init( checkNotificationPermissionUseCase: CheckNotificationPermissionUseCase, openNotificationSettingUseCase: OpenNotificationSettingUseCase, updateNotificationAgreementUseCase: UpdateNotificationAgreementUseCase, updateUserInfoUseCase: UpdateUserInfoUseCase, - dictionaryMainViewFactory: DictionaryMainViewFactory + appCoordinator: @escaping () -> AppCoordinatorProtocol ) { self.checkNotificationPermissionUseCase = checkNotificationPermissionUseCase self.openNotificationSettingUseCase = openNotificationSettingUseCase self.updateNotificationAgreementUseCase = updateNotificationAgreementUseCase self.updateUserInfoUseCase = updateUserInfoUseCase - self.dictionaryMainViewFactory = dictionaryMainViewFactory + self.appCoordinator = appCoordinator } public func make(selectedLevel: Int, selectedJobID: Int) -> BaseViewController & ModalPresentable { - let viewController = OnBoardingNotificationSheetViewController(dictionaryMainViewFactory: dictionaryMainViewFactory) + let viewController = OnBoardingNotificationSheetViewController(appCoordinator: appCoordinator()) + viewController.isBottomTabbarHidden = true viewController.reactor = OnBoardingNotificationSheetReactor( selectedLevel: selectedLevel, selectedJobID: selectedJobID, diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift index 5738ad10..2c2ed762 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingNotificationSheet/OnBoardingNotificationSheetViewController.swift @@ -18,13 +18,13 @@ public final class OnBoardingNotificationSheetViewController: BaseViewController // MARK: - Properties - private let dictionaryMainViewFactory: DictionaryMainViewFactory + private let appCoordinator: AppCoordinatorProtocol // MARK: - Components private var mainView = OnBoardingNotificationSheetView() - init(dictionaryMainViewFactory: DictionaryMainViewFactory) { - self.dictionaryMainViewFactory = dictionaryMainViewFactory + init(appCoordinator: AppCoordinatorProtocol) { + self.appCoordinator = appCoordinator super.init() } } @@ -112,9 +112,7 @@ extension OnBoardingNotificationSheetViewController { case .dismiss: owner.dismissCurrentModal() case .home: - let viewController = owner.dictionaryMainViewFactory.make() - let navigationController = UINavigationController(rootViewController: viewController) - AppRouter.setRoot(navigationController) + owner.appCoordinator.showMainTab() case .setting: guard let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) else { return } diff --git a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift index 133cf277..051cb8ba 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/OnBoardingQuestion/OnBoardingQuestionFactoryImpl.swift @@ -11,6 +11,7 @@ public struct OnBoardingQuestionFactoryImpl: OnBoardingQuestionFactory { public func make() -> BaseViewController { let viewController = OnBoardingQuestionViewController(factory: onBoardingInputFactory) + viewController.isBottomTabbarHidden = true viewController.reactor = OnBoardingQuestionReactor() return viewController } diff --git a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift index bf7a1e72..ef9763b0 100644 --- a/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift +++ b/MLS/Presentation/AuthFeature/AuthFeature/TermsAgreement/TermsAgreementFactoryImpl.swift @@ -29,6 +29,7 @@ public struct TermsAgreementFactoryImpl: TermsAgreementFactory { public func make(credential: Credential, platform: LoginPlatform) -> BaseViewController { let viewController = TermsAgreementViewController(onBoardingQuestionFactory: onBoardingQuestionFactory) + viewController.isBottomTabbarHidden = true viewController.reactor = TermsAgreementReactor( credential: credential, socialPlatform: platform, diff --git a/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift b/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift new file mode 100644 index 00000000..24f9b2ca --- /dev/null +++ b/MLS/Presentation/BaseFeature/BaseFeature/Interface/AppCoordinatorProtocol.swift @@ -0,0 +1,7 @@ +import UIKit + +public protocol AppCoordinatorProtocol: AnyObject { + var window: UIWindow? { get set } + func showMainTab() + func showLogin(exitRoute: LoginExitRoute) +} diff --git a/MLS/Presentation/AuthFeature/AuthFeatureInterface/LoginExitRoute.swift b/MLS/Presentation/BaseFeature/BaseFeature/Interface/LoginExitRoute.swift similarity index 100% rename from MLS/Presentation/AuthFeature/AuthFeatureInterface/LoginExitRoute.swift rename to MLS/Presentation/BaseFeature/BaseFeature/Interface/LoginExitRoute.swift diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj b/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj index 32df0154..cd987481 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 77C755BA2E4B70C30081D80F /* AuthFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */; }; 77C755BB2E4B70C30081D80F /* AuthFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 77C755B72E4B70C30081D80F /* AuthFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 77C755C12E4B917F0081D80F /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 77C755C02E4B917F0081D80F /* RxKeyboard */; }; + 77C7F4E52ED88D14009559E1 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 77C7F4E42ED88D14009559E1 /* RxGesture */; }; 77C974732E376124007198DA /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C974722E376124007198DA /* BookmarkFeatureInterface.framework */; }; 77C97DAB2E37D4AC007198DA /* AuthFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C97DAA2E37D4AC007198DA /* AuthFeature.framework */; }; 77DBD8BF2E1AD0C600529428 /* BookmarkFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77DBD8BE2E1AD0C600529428 /* BookmarkFeatureInterface.framework */; }; @@ -190,6 +191,7 @@ 081D9DC52DF80E15004F850D /* DomainInterface.framework in Frameworks */, 081D9DDA2DF80E7E004F850D /* ReactorKit in Frameworks */, 77C974732E376124007198DA /* BookmarkFeatureInterface.framework in Frameworks */, + 77C7F4E52ED88D14009559E1 /* RxGesture in Frameworks */, 77FEEF1D2EC5B0870023197A /* AuthFeatureInterface.framework in Frameworks */, 081D9DD02DF80E5C004F850D /* RxCocoa in Frameworks */, 77C97DAB2E37D4AC007198DA /* AuthFeature.framework in Frameworks */, @@ -339,6 +341,7 @@ 081D9DD62DF80E69004F850D /* SnapKit */, 081D9DD92DF80E7E004F850D /* ReactorKit */, 772DC9962E55CA93005F92E3 /* RxKeyboard */, + 77C7F4E42ED88D14009559E1 /* RxGesture */, ); productName = DictionaryFeature; productReference = 081D9C192DF80854004F850D /* DictionaryFeature.framework */; @@ -435,6 +438,7 @@ 081D9DD52DF80E69004F850D /* XCRemoteSwiftPackageReference "SnapKit" */, 081D9DD82DF80E7E004F850D /* XCRemoteSwiftPackageReference "ReactorKit" */, 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */, + 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */, ); preferredProjectObjectVersion = 77; productRefGroup = 081D9C1A2DF80854004F850D /* Products */; @@ -962,6 +966,14 @@ minimumVersion = 2.0.1; }; }; + 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.4; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1025,6 +1037,11 @@ package = 77C755BF2E4B917F0081D80F /* XCRemoteSwiftPackageReference "RxKeyboard" */; productName = RxKeyboard; }; + 77C7F4E42ED88D14009559E1 /* RxGesture */ = { + isa = XCSwiftPackageProductDependency; + package = 77C7F4E32ED88D14009559E1 /* XCRemoteSwiftPackageReference "RxGesture" */; + productName = RxGesture; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 081D9C102DF80854004F850D /* Project object */; diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift index 7b65a1e8..3c039007 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailBaseViewController.swift @@ -4,6 +4,7 @@ import AuthFeatureInterface import BaseFeature import BookmarkFeatureInterface import DesignSystem +import DictionaryFeatureInterface import DomainInterface import RxCocoa @@ -30,16 +31,21 @@ class DictionaryDetailBaseViewController: BaseViewController { private let bookmarkModalFactory: BookmarkModalFactory private let loginFactory: LoginFactory + public let dictionaryDetailFactory: DictionaryDetailFactory + private let appCoordinator: AppCoordinatorProtocol + // MARK: - Components public var mainView = DictionaryDetailBaseView() // 타입설정 public var type: DictionaryItemType - public init(type: DictionaryItemType, bookmarkModalFactory: BookmarkModalFactory, loginFactory: LoginFactory) { + public init(type: DictionaryItemType, bookmarkModalFactory: BookmarkModalFactory, loginFactory: LoginFactory, dictionaryDetailFactory: DictionaryDetailFactory, appCoordinator: AppCoordinatorProtocol) { self.type = type self.bookmarkModalFactory = bookmarkModalFactory self.loginFactory = loginFactory + self.appCoordinator = appCoordinator + self.dictionaryDetailFactory = dictionaryDetailFactory mainView.titleLabel.attributedText = .makeStyledString(font: .sub_m_b, text: type.detailTitle) super.init() isBottomTabbarHidden = true @@ -331,15 +337,24 @@ private extension DictionaryDetailBaseViewController { func bindBackButton() { mainView.backButton.rx.tap - .bind { [weak self] in - self?.navigationController?.popViewController(animated: true) + .observe(on: MainScheduler.instance) + .withUnretained(self) + .bind { owner, _ in + owner.navigationController?.popViewController(animated: true) + } + .disposed(by: disposeBag) + + mainView.dictButton.rx.tap + .observe(on: MainScheduler.instance) + .withUnretained(self) + .bind { owner, _ in + owner.appCoordinator.showMainTab() } .disposed(by: disposeBag) } } extension DictionaryDetailBaseViewController { - // DictionaryDetailBaseViewController public func bindReportButton(providerId: Observable, itemName: Observable) { mainView.reportButton.rx.tap .withLatestFrom(Observable.combineLatest(providerId, itemName)) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift index e69107a9..7148e978 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/DictionaryDetailFactoryImpl.swift @@ -7,6 +7,9 @@ import DomainInterface public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { private let loginFactory: () -> LoginFactory private let bookmarkModalFactory: BookmarkModalFactory + private let dictionaryDetailFactory: () -> DictionaryDetailFactory + private let appCoordinator: () -> AppCoordinatorProtocol + private let dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase private let dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase private let dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase @@ -27,6 +30,8 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { public init( loginFactory: @escaping () -> LoginFactory, bookmarkModalFactory: BookmarkModalFactory, + dictionaryDetailFactory: @escaping () -> DictionaryDetailFactory, + appCoordinator: @escaping () -> AppCoordinatorProtocol, dictionaryDetailMapUseCase: FetchDictionaryDetailMapUseCase, dictionaryDetailMapSpawnMonsterUseCase: FetchDictionaryDetailMapSpawnMonsterUseCase, dictionaryDetailMapNpcUseCase: FetchDictionaryDetailMapNpcUseCase, @@ -60,6 +65,8 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { self.dictionaryDetailMonsterMapUseCase = dictionaryDetailMonsterMapUseCase self.checkLoginUseCase = checkLoginUseCase self.setBookmarkUseCase = setBookmarkUseCase + self.appCoordinator = appCoordinator + self.dictionaryDetailFactory = dictionaryDetailFactory } public func make(type: DictionaryType, id: Int) -> BaseViewController { @@ -70,7 +77,7 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { case .collection: break case .item: - viewController = ItemDictionaryDetailViewController(type: .item, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) + viewController = ItemDictionaryDetailViewController(type: .item, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory(), dictionaryDetailFactory: dictionaryDetailFactory(), appCoordinator: appCoordinator()) let reactor = ItemDictionaryDetailReactor( dictionaryDetailItemUseCase: dictionaryDetailItemUseCase, dictionaryDetailItemDropMonsterUseCase: dictionaryDetailItemDropMonsterUseCase, @@ -82,7 +89,7 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { viewController.reactor = reactor } case .monster: - viewController = MonsterDictionaryDetailViewController(type: .monster, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) + viewController = MonsterDictionaryDetailViewController(type: .monster, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory(), dictionaryDetailFactory: dictionaryDetailFactory(), appCoordinator: appCoordinator()) let reactor = MonsterDictionaryDetailReactor( dictionaryDetailMonsterUseCase: dictionaryDetailMonsterUseCase, dictionaryDetailMonsterDropItemUseCase: dictionaryDetailMonsterDropItemUseCase, @@ -103,7 +110,7 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { setBookmarkUseCase: setBookmarkUseCase, id: id ) - viewController = MapDictionaryDetailViewController(type: .map, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) + viewController = MapDictionaryDetailViewController(type: .map, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory(), dictionaryDetailFactory: dictionaryDetailFactory(), appCoordinator: appCoordinator()) if let viewController = viewController as? MapDictionaryDetailViewController { viewController.reactor = reactor } @@ -116,12 +123,12 @@ public final class DictionaryDetailFactoryImpl: DictionaryDetailFactory { setBookmarkUseCase: setBookmarkUseCase, id: id ) - viewController = NpcDictionaryDetailViewController(type: .npc, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) + viewController = NpcDictionaryDetailViewController(type: .npc, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory(), dictionaryDetailFactory: dictionaryDetailFactory(), appCoordinator: appCoordinator()) if let viewController = viewController as? NpcDictionaryDetailViewController { viewController.reactor = reactor } case .quest: - viewController = QuestDictionaryDetailViewController(type: .quest, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory()) + viewController = QuestDictionaryDetailViewController(type: .quest, bookmarkModalFactory: bookmarkModalFactory, loginFactory: loginFactory(), dictionaryDetailFactory: dictionaryDetailFactory(), appCoordinator: appCoordinator()) let reactor = QuestDictionaryDetailReactor( dictionaryDetailQuestUseCase: dictionaryDetailQuestUseCase, dictionaryDetailQuestLinkedQuestUseCase: dictionaryDetailQuestLinkedQuestsUseCase, diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift index 83e8b92e..3531c85c 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailReactor.swift @@ -8,6 +8,7 @@ public final class ItemDictionaryDetailReactor: Reactor { public enum Route { case none case filter(DictionaryType) + case detail(Int) } // MARK: Action @@ -17,11 +18,12 @@ public final class ItemDictionaryDetailReactor: Reactor { case selectFilter(SortType) case toggleBookmark(Bool) case undoLastDeletedBookmark + case dataTapped(index: Int) } // MARK: Mutation public enum Mutation { - case showFilter + case toNavigate(Route) case setDetailData(DictionaryDetailItemResponse) case setDetailDropMonsterData([DictionaryDetailItemDropMonsterResponse]) case setBookmark(DictionaryDetailItemResponse) @@ -77,7 +79,7 @@ public final class ItemDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .filterButtonTapped: - return .just(.showFilter) + return .just(.toNavigate(.filter(currentState.type))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, @@ -127,6 +129,9 @@ public final class ItemDictionaryDetailReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .dataTapped(let index): + guard let id = currentState.monsters[index].monsterId else { return .empty() } + return .just(.toNavigate(.detail(id))) } } @@ -134,8 +139,6 @@ public final class ItemDictionaryDetailReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .showFilter: - newState.route = .filter(newState.type) case let .setDetailData(data): newState.itemDetailInfo = data case let .setDetailDropMonsterData(data): @@ -146,6 +149,8 @@ public final class ItemDictionaryDetailReactor: Reactor { newState.lastDeletedBookmark = item case let .setLoginState(isLogin): newState.isLogin = isLogin + case .toNavigate(let route): + newState.route = route } return newState } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift index 30936979..8354ee09 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Item/ItemDictionaryDetailViewController.swift @@ -170,6 +170,11 @@ extension ItemDictionaryDetailViewController { .map { Reactor.Action.filterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + monsterCardView.tap + .map { Reactor.Action.dataTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -209,6 +214,9 @@ extension ItemDictionaryDetailViewController { owner.tabBarController?.presentModal(viewController) case .none: break + case .detail(let id): + let viewController = owner.dictionaryDetailFactory.make(type: .monster, id: id) + owner.navigationController?.pushViewController(viewController, animated: true) } } .disposed(by: disposeBag) diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift index 926fae83..91afbae1 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailReactor.swift @@ -7,16 +7,20 @@ public final class MapDictionaryDetailReactor: Reactor { public enum Route { case none case filter(DictionaryType) + case detail(type: DictionaryType, id: Int) } public enum Action { case filterButtonTapped case viewWillAppear case toggleBookmark(Bool) case undoLastDeletedBookmark + case monsterTapped(index: Int) + case npcTapped(index: Int) + case selectFilter(SortType) } public enum Mutation { - case showFilter + case toNavigate(Route) case setDetailData(DictionaryDetailMapResponse) case setDetailSpawnMonsters([DictionaryDetailMapSpawnMonsterResponse]) case setDetailNpc([DictionaryDetailMapNpcResponse]) @@ -81,7 +85,7 @@ public final class MapDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .filterButtonTapped: - return Observable.just(.showFilter) + return Observable.just(.toNavigate(.filter(currentState.type))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, @@ -106,6 +110,9 @@ public final class MapDictionaryDetailReactor: Reactor { .map { .setDetailData($0) } ) ) + case let .selectFilter(type): +// return dictionaryDetailMapSpawnMonsterUseCase.execute(id: currentState.id, sort: ["level", "asc"]).map { .setDetailSpawnMonsters($0) } + return .empty() case .undoLastDeletedBookmark: guard let lastDeleted = currentState.lastDeletedBookmark, let mapId = lastDeleted.mapId else { return .empty() } @@ -121,6 +128,12 @@ public final class MapDictionaryDetailReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .monsterTapped(index: let index): + guard let id = currentState.spawnMonsters[index].monsterId else { return .empty() } + return .just(.toNavigate(.detail(type: .monster, id: id))) + case .npcTapped(index: let index): + guard let id = currentState.npcs[index].npcId else { return .empty() } + return .just(.toNavigate(.detail(type: .npc, id: id))) } } @@ -128,8 +141,8 @@ public final class MapDictionaryDetailReactor: Reactor { var newState = state switch mutation { - case .showFilter: - newState.route = .filter(newState.type) + case .toNavigate(let route): + newState.route = route case let .setDetailData(data): newState.mapDetailInfo = data case let .setDetailSpawnMonsters(data): diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift index 65bda22b..83ae69d0 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Map/MapDictionaryDetailViewController.swift @@ -118,6 +118,16 @@ extension MapDictionaryDetailViewController { .map { Reactor.Action.filterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + appearMonsterView.tap + .map { Reactor.Action.monsterTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + appearNpcView.tap + .map { Reactor.Action.npcTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -162,5 +172,28 @@ extension MapDictionaryDetailViewController { bookmarkId: reactor.state.map(\.mapDetailInfo.bookmarkId) ) .disposed(by: disposeBag) + + rx.viewDidAppear + .take(1) + .flatMapLatest { _ in reactor.pulse(\.$route) } // 값이 바뀔때만 이벤트 받음 + .withUnretained(self) + .subscribe { owner, route in + switch route { + case .filter(let type): + let viewController = owner.sortedFactory.make(sortedOptions: type.detailSortedFilter, selectedIndex: owner.selectedIndex) { index in + owner.selectedIndex = index + let selectedFilter = reactor.currentState.type.detailSortedFilter[index] + owner.appearMonsterView.selectFilter(selectedType: selectedFilter) + reactor.action.onNext(.selectFilter(selectedFilter)) + } + owner.tabBarController?.presentModal(viewController) + case .none: + break + case .detail(let type, let id): + let viewController = owner.dictionaryDetailFactory.make(type: type, id: id) + owner.navigationController?.pushViewController(viewController, animated: true) + } + } + .disposed(by: disposeBag) } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift index 4ebd3c2f..b8315205 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailReactor.swift @@ -7,6 +7,7 @@ public final class MonsterDictionaryDetailReactor: Reactor { public enum Route { case none case filter(DictionaryType) + case detail(type: DictionaryType, id: Int) } public struct Info: Equatable { @@ -21,11 +22,13 @@ public final class MonsterDictionaryDetailReactor: Reactor { case selectFilter(SortType) case toggleBookmark(Bool) case undoLastDeletedBookmark + case itemTapped(index: Int) + case mapTapped(index: Int) } // MARK: - Mutation public enum Mutation { - case showFilter(DictionaryType) + case toNavigate(Route) case setDetailData(DictionaryDetailMonsterResponse) case setDetailDropItemData([DictionaryDetailMonsterDropItemResponse]) case setDetailMapData([DictionaryDetailMonsterMapResponse]) @@ -85,7 +88,7 @@ public final class MonsterDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case let .filterButtonTapped(type): - return .just(.showFilter(type)) + return .just(.toNavigate(.filter(type))) case .viewWillAppear: return .merge([ @@ -136,6 +139,10 @@ public final class MonsterDictionaryDetailReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .itemTapped(index: let index): + return .just(.toNavigate(.detail(type: .item, id: currentState.dropItems[index].itemId))) + case .mapTapped(index: let index): + return .just(.toNavigate(.detail(type: .map, id: currentState.spawnMaps[index].mapId))) } } @@ -144,8 +151,8 @@ public final class MonsterDictionaryDetailReactor: Reactor { var newState = state switch mutation { - case let .showFilter(type): - newState.route = .filter(type) + case let .toNavigate(route): + newState.route = route case let .setDetailData(data): newState.monsterDetailInfo = data diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift index d66f6d37..b8a4102d 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Monster/MonsterDictionaryDetailViewController.swift @@ -125,6 +125,16 @@ extension MonsterDictionaryDetailViewController { .map { Reactor.Action.filterButtonTapped(.map) } .bind(to: reactor.action) .disposed(by: disposeBag) + + dropItemView.tap + .map { Reactor.Action.itemTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + appearMapView.tap + .map { Reactor.Action.mapTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -189,7 +199,10 @@ extension MonsterDictionaryDetailViewController { owner.isBottomTabbarHidden = true } owner.tabBarController?.presentModal(viewController) - case .none: + case let .detail(type: type, id: id): + let viewController = owner.dictionaryDetailFactory.make(type: type, id: id) + owner.navigationController?.pushViewController(viewController, animated: true) + default: break } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift index 43a85137..8d42be45 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailReactor.swift @@ -7,6 +7,7 @@ public final class NpcDictionaryDetailReactor: Reactor { public enum Route { case none case filter(DictionaryType) + case detail(type: DictionaryType, id: Int) } // MARK: - Action @@ -16,11 +17,13 @@ public final class NpcDictionaryDetailReactor: Reactor { case selectFilter(SortType) case toggleBookmark(Bool) case undoLastDeletedBookmark + case mapTapped(index: Int) + case questTapped(index: Int) } // MARK: - Mutation public enum Mutation { - case showFilter + case toNavigate(Route) case setDetailData(DictionaryDetailNpcResponse) case setDetailMaps([DictionaryDetailMonsterMapResponse]) case setDetailQuests([DictionaryDetailNpcQuestResponse]) @@ -78,7 +81,7 @@ public final class NpcDictionaryDetailReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .filterButtonTapped: - return .just(.showFilter) + return .just(.toNavigate(.filter(currentState.type))) case .viewWillAppear: return .merge([ checkLoginUseCase.execute().map { .setLoginState($0) }, @@ -129,6 +132,10 @@ public final class NpcDictionaryDetailReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case .mapTapped(index: let index): + return .just(.toNavigate(.detail(type: .map, id: currentState.maps[index].mapId))) + case .questTapped(index: let index): + return .just(.toNavigate(.detail(type: .quest, id: currentState.quests[index].questId))) } } @@ -136,8 +143,8 @@ public final class NpcDictionaryDetailReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .showFilter: - newState.route = .filter(newState.type) + case .toNavigate(let route): + newState.route = route case let .setDetailData(data): newState.npcDetailInfo = data case let .setDetailMaps(data): diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift index 9b64f6ce..abe46a33 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/NPC/NpcDictionaryDetailViewController.swift @@ -93,6 +93,16 @@ extension NpcDictionaryDetailViewController { .map { Reactor.Action.filterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + questView.tap + .map { Reactor.Action.questTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + appearMapView.tap + .map { Reactor.Action.mapTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -114,7 +124,10 @@ extension NpcDictionaryDetailViewController { reactor.action.onNext(.selectFilter(selectedFilter)) } owner.tabBarController?.presentModal(viewController) - case .none: + case .detail(type: let type, id: let id): + let viewController = owner.dictionaryDetailFactory.make(type: type, id: id) + owner.navigationController?.pushViewController(viewController, animated: true) + default: break } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift index ae407c66..61395512 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailReactor.swift @@ -3,14 +3,21 @@ import DomainInterface import ReactorKit public final class QuestDictionaryDetailReactor: Reactor { - // MARK: - Action / Mutation / State + public enum Route { + case none + case filter(DictionaryType) + case detail(id: Int) + } + public enum Action { case viewWillAppear case toggleBookmark(Bool) case undoLastDeletedBookmark + case questTapped(index: Int) } public enum Mutation { + case toNavigate(Route) case setDetailData(DictionaryDetailQuestResponse) case setLinkedQuests(DictionaryDetailQuestLinkedQuestsResponse) case setLoginState(Bool) @@ -18,6 +25,7 @@ public final class QuestDictionaryDetailReactor: Reactor { } public struct State { + @Pulse var route: Route = .none var type: DictionaryType = .quest var id: Int var detailInfo: DictionaryDetailQuestResponse @@ -113,6 +121,19 @@ public final class QuestDictionaryDetailReactor: Reactor { .just(.setLastDeletedBookmark(nil)) ]) ) + case let .questTapped(index): + if let previous = currentState.linkedQuestInfo.previousQuests, !previous.isEmpty { + if index == 0, let questId = previous.first?.questId { + return .just(.toNavigate(.detail(id: questId))) + } else if index == 1, let next = currentState.linkedQuestInfo.nextQuests?.first?.questId { + return .just(.toNavigate(.detail(id: next))) + } + } else { + if let next = currentState.linkedQuestInfo.nextQuests, index == 0, let questId = next.first?.questId { + return .just(.toNavigate(.detail(id: questId))) + } + } + return .empty() } } @@ -127,6 +148,8 @@ public final class QuestDictionaryDetailReactor: Reactor { newState.isLogin = isLogin case let .setLastDeletedBookmark(data): newState.lastDeletedBookmark = data + case .toNavigate(let route): + newState.route = route } return newState } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift index 1caf7486..cbe66d63 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/Quest/QuestDictionaryDetailViewController.swift @@ -127,6 +127,11 @@ extension QuestDictionaryDetailViewController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) + + linkedQuestView.tap + .map { Reactor.Action.questTapped(index: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindViewState(reactor: Reactor) { @@ -162,6 +167,21 @@ extension QuestDictionaryDetailViewController { bookmarkId: reactor.state.map(\.detailInfo.bookmarkId) ) .disposed(by: disposeBag) + + rx.viewDidAppear + .take(1) + .flatMapLatest { _ in reactor.pulse(\.$route) } + .withUnretained(self) + .subscribe { owner, route in + switch route { + case .detail(let id): + let viewController = owner.dictionaryDetailFactory.make(type: .quest, id: id) + owner.navigationController?.pushViewController(viewController, animated: true) + default: + break + } + } + .disposed(by: disposeBag) } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift index 23b218bb..29fc15f1 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryDetail/SectionStackView/DetailStackCardView.swift @@ -4,6 +4,9 @@ import BaseFeature import DesignSystem import DomainInterface +import RxCocoa +import RxGesture +import RxSwift import SnapKit final class DetailStackCardView: UIStackView { @@ -19,7 +22,15 @@ final class DetailStackCardView: UIStackView { static let stackViewInset: UIEdgeInsets = .init(top: 12, left: 16, bottom: 0, right: 16) } + // MARK: - Properties + private let disposeBag = DisposeBag() + + private let tapSubject = PublishSubject() + var tap: Observable { tapSubject.asObservable() } + // MARK: - Components + private var cardViews: [CardList] = [] + private let filterContainerView = UIView() // 몬스터 순서 필터 버튼 public let filterButton: UIButton = { @@ -115,6 +126,8 @@ extension DetailStackCardView { // type별 필터 유무 setFilter(isHidden: input.type.sortFilter.isEmpty) let cardView = CardList() + cardViews.append(cardView) + let currentIndex = cardViews.count - 1 let spacer = UIView() addArrangedSubview(cardView) @@ -153,6 +166,12 @@ extension DetailStackCardView { default: break } + + cardView.rx.tapGesture() + .when(.recognized) + .map { _ in currentIndex } + .bind(to: tapSubject) + .disposed(by: disposeBag) } func setFilter(isHidden: Bool) { @@ -173,13 +192,13 @@ extension DetailStackCardView { } func reset() { + cardViews.removeAll() // 필터 뷰를 제외한 arrangedSubview만 제거 - for subview in self.arrangedSubviews { - + for subview in arrangedSubviews { if subview == filterContainerView { continue } if subview == spacer { continue } - self.removeArrangedSubview(subview) + removeArrangedSubview(subview) subview.removeFromSuperview() } } diff --git a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift index 579cc17b..d490c9a3 100644 --- a/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift +++ b/MLS/Presentation/DictionaryFeature/DictionaryFeature/DictionaryMain/DictionaryMainViewFactoryImpl.swift @@ -18,6 +18,7 @@ public final class DictionaryMainViewFactoryImpl: DictionaryMainViewFactory { public func make() -> BaseViewController { let reactor = DictionaryMainReactor(checkLoginUseCase: checkLoginUseCase) let viewController = DictionaryMainViewController(dictionaryMainListFactory: dictionaryMainListFactory, searchFactory: searchFactory, notificationFactory: notificationFactory, reactor: reactor) + viewController.isBottomTabbarHidden = false viewController.reactor = reactor return viewController } diff --git a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift b/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift index b3670092..25141721 100644 --- a/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift +++ b/MLS/Presentation/MyPageFeature/MyPageFeature/SetProfile/SetProfileReactor.swift @@ -109,7 +109,7 @@ public final class SetProfileReactor: Reactor { .andThen(.just(.toNavigate(.dismiss))) case .withdraw: return withdrawUseCase.execute() - .andThen(.empty()) + .andThen(.just(.toNavigate(.dismiss))) case .viewWillAppear: return fetchProfileUseCase.execute() .map { Mutation.setProfile($0)}