diff --git a/Core/Utils/UIKit/UIViewController+.swift b/Core/Utils/UIKit/UIViewController+.swift index 1d8b9408..11bff624 100644 --- a/Core/Utils/UIKit/UIViewController+.swift +++ b/Core/Utils/UIKit/UIViewController+.swift @@ -7,7 +7,7 @@ import UIKit -extension UIViewController { +public extension UIViewController { func hideKeyboardWhenTapped() { let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) @@ -20,4 +20,45 @@ extension UIViewController { @objc func dismissKeyboard() { self.view.endEditing(true) } + + func showAlert( + title: String? = nil, + message: String, + type: AlertActionType, + okActionTitle: String = "확인", + okActionHandler: (() -> Void)? = nil, + cancelActionTitle: String = "취소", + cancelActionHandler: (() -> Void)? = nil + ) { + let alertController = UIAlertController( + title: title, + message: message, + preferredStyle: .alert + ) + + if type == .double { + let cancelAction = UIAlertAction(title: cancelActionTitle, style: .default) { _ in + if let cancelActionHandler = cancelActionHandler { + cancelActionHandler() + } + } + alertController.addAction(cancelAction) + } + + let okAction = UIAlertAction(title: okActionTitle, style: .default) { _ in + if let okActionHandler = okActionHandler { + okActionHandler() + } + } + + alertController.addAction(okAction) + self.present(alertController, animated: true, completion: nil) + } +} + +extension UIViewController { + public enum AlertActionType { + case single + case double + } } diff --git a/Features/SignIn/SignInData/Implement/dummy.swift b/Features/Auth/AuthData/Implement/dummy.swift similarity index 100% rename from Features/SignIn/SignInData/Implement/dummy.swift rename to Features/Auth/AuthData/Implement/dummy.swift diff --git a/Features/SignUp/SignUpData/Interface/JoinRepository.swift b/Features/Auth/AuthData/Interface/SignUp/JoinRepository.swift similarity index 100% rename from Features/SignUp/SignUpData/Interface/JoinRepository.swift rename to Features/Auth/AuthData/Interface/SignUp/JoinRepository.swift diff --git a/Features/SignIn/SignInData/Project.swift b/Features/Auth/AuthData/Project.swift similarity index 78% rename from Features/SignIn/SignInData/Project.swift rename to Features/Auth/AuthData/Project.swift index 9a2bff48..a483d43f 100644 --- a/Features/SignIn/SignInData/Project.swift +++ b/Features/Auth/AuthData/Project.swift @@ -1,7 +1,7 @@ // -// SignInAppDelegate.swift +// AuthAppDelegate.swift // -// SignIn +// Auth // // Created by hyerin // @@ -11,10 +11,11 @@ import ProjectDescriptionHelpers import UtilityPlugin let project = Project.invertedDualTargetProject( - name: "SignInData", + name: "AuthData", platform: .iOS, iOSTargetVersion: "16.0.0", interfaceDependencies: [ + .ThirdParty.RxSwift ], implementDependencies: [ ] diff --git a/Features/SignIn/SignInData/Interface/dummy.swift b/Features/Auth/AuthData/Tests/dummy.swift similarity index 100% rename from Features/SignIn/SignInData/Interface/dummy.swift rename to Features/Auth/AuthData/Tests/dummy.swift diff --git a/Features/SignUp/SignUpDomain/Implement/FetchRandomNumberUseCaseImpl.swift b/Features/Auth/AuthDomain/Implement/FetchRandomNumberUseCaseImpl.swift similarity index 95% rename from Features/SignUp/SignUpDomain/Implement/FetchRandomNumberUseCaseImpl.swift rename to Features/Auth/AuthDomain/Implement/FetchRandomNumberUseCaseImpl.swift index 6bc0d23a..e17cf054 100644 --- a/Features/SignUp/SignUpDomain/Implement/FetchRandomNumberUseCaseImpl.swift +++ b/Features/Auth/AuthDomain/Implement/FetchRandomNumberUseCaseImpl.swift @@ -8,7 +8,7 @@ import Foundation -import SignUpDomain +import AuthDomain public final class FetchRandomNumberUseCaseImpl: FetchRandomNumberUseCase { diff --git a/Features/SignUp/SignUpDomain/Implement/PostJoinInfoUseCaseImpl.swift b/Features/Auth/AuthDomain/Implement/PostJoinInfoUseCaseImpl.swift similarity index 94% rename from Features/SignUp/SignUpDomain/Implement/PostJoinInfoUseCaseImpl.swift rename to Features/Auth/AuthDomain/Implement/PostJoinInfoUseCaseImpl.swift index 7724ff7e..b8bf9af4 100644 --- a/Features/SignUp/SignUpDomain/Implement/PostJoinInfoUseCaseImpl.swift +++ b/Features/Auth/AuthDomain/Implement/PostJoinInfoUseCaseImpl.swift @@ -8,8 +8,8 @@ import Foundation -import SignUpData -import SignUpDomain +import AuthData +import AuthDomain import RxSwift diff --git a/Features/Auth/AuthDomain/Implement/SaveTokenUseCaseImpl.swift b/Features/Auth/AuthDomain/Implement/SaveTokenUseCaseImpl.swift new file mode 100644 index 00000000..746414c7 --- /dev/null +++ b/Features/Auth/AuthDomain/Implement/SaveTokenUseCaseImpl.swift @@ -0,0 +1,27 @@ +// +// SaveTokenUseCaseImpl.swift +// AuthDomainImpl +// +// Created by 최혜린 on 2023/07/27. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +import AuthDomain +import TokenManager + +public final class SaveTokenUseCaseImpl: SaveTokenUseCase { + + private let tokenManager: TokenManager + + public init( + tokenManager: TokenManager + ) { + self.tokenManager = tokenManager + } + + public func execute(token: String) { + tokenManager.save(token: token, with: .authorizationToken) + } +} diff --git a/Features/SignUp/SignUpDomain/Interface/FetchRandomNumberUseCase.swift b/Features/Auth/AuthDomain/Interface/FetchRandomNumberUseCase.swift similarity index 100% rename from Features/SignUp/SignUpDomain/Interface/FetchRandomNumberUseCase.swift rename to Features/Auth/AuthDomain/Interface/FetchRandomNumberUseCase.swift diff --git a/Features/Auth/AuthDomain/Interface/MOITSignInResponse.swift b/Features/Auth/AuthDomain/Interface/MOITSignInResponse.swift new file mode 100644 index 00000000..d766605c --- /dev/null +++ b/Features/Auth/AuthDomain/Interface/MOITSignInResponse.swift @@ -0,0 +1,24 @@ +// +// MOITSignInResponse.swift +// MOITWeb +// +// Created by 최혜린 on 2023/07/12. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation +import WebKit + +public struct MOITSignInResponse { + let providerUniqueKey: String + let nickname: String + let email: String + + public init( + headerFields: [AnyHashable : Any] + ) { + self.providerUniqueKey = headerFields["X-MOIT-USER-PROVIDER"] as? String ?? "" + self.nickname = headerFields["X-MOIT-USER-NICKNAME"] as? String ?? "" + self.email = headerFields["X-MOIT-USER-EMAIL"] as? String ?? "" + } +} diff --git a/Features/SignUp/SignUpDomain/Interface/PostJoinInfoUseCase.swift b/Features/Auth/AuthDomain/Interface/PostJoinInfoUseCase.swift similarity index 100% rename from Features/SignUp/SignUpDomain/Interface/PostJoinInfoUseCase.swift rename to Features/Auth/AuthDomain/Interface/PostJoinInfoUseCase.swift diff --git a/Features/Auth/AuthDomain/Interface/SaveTokenUseCase.swift b/Features/Auth/AuthDomain/Interface/SaveTokenUseCase.swift new file mode 100644 index 00000000..a770d4ce --- /dev/null +++ b/Features/Auth/AuthDomain/Interface/SaveTokenUseCase.swift @@ -0,0 +1,13 @@ +// +// SaveTokenUseCase.swift +// AuthDomain +// +// Created by 최혜린 on 2023/07/27. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +public protocol SaveTokenUseCase { + func execute(token: String) +} diff --git a/Features/SignIn/SignInDomain/Project.swift b/Features/Auth/AuthDomain/Project.swift similarity index 73% rename from Features/SignIn/SignInDomain/Project.swift rename to Features/Auth/AuthDomain/Project.swift index 5e56bf83..925ba7a7 100644 --- a/Features/SignIn/SignInDomain/Project.swift +++ b/Features/Auth/AuthDomain/Project.swift @@ -1,7 +1,7 @@ // -// SignInAppDelegate.swift +// AuthAppDelegate.swift // -// SignIn +// Auth // // Created by hyerin // @@ -11,12 +11,14 @@ import ProjectDescriptionHelpers import UtilityPlugin let project = Project.invertedDualTargetProject( - name: "SignInDomain", + name: "AuthDomain", platform: .iOS, iOSTargetVersion: "16.0.0", interfaceDependencies: [ + .ThirdParty.RxSwift ], implementDependencies: [ + .TokenManager.Interface ] ) diff --git a/Features/SignIn/SignInData/Tests/dummy.swift b/Features/Auth/AuthDomain/Tests/dummy.swift similarity index 100% rename from Features/SignIn/SignInData/Tests/dummy.swift rename to Features/Auth/AuthDomain/Tests/dummy.swift diff --git a/Features/SignIn/SignInUserInterface/DemoApp/Resources/LaunchScreen.storyboard b/Features/Auth/AuthUserInterface/DemoApp/Resources/LaunchScreen.storyboard similarity index 100% rename from Features/SignIn/SignInUserInterface/DemoApp/Resources/LaunchScreen.storyboard rename to Features/Auth/AuthUserInterface/DemoApp/Resources/LaunchScreen.storyboard diff --git a/Features/Auth/AuthUserInterface/DemoApp/Sources/AuthUserInterfaceAppDelegate.swift b/Features/Auth/AuthUserInterface/DemoApp/Sources/AuthUserInterfaceAppDelegate.swift new file mode 100644 index 00000000..9a95ee1a --- /dev/null +++ b/Features/Auth/AuthUserInterface/DemoApp/Sources/AuthUserInterfaceAppDelegate.swift @@ -0,0 +1,43 @@ +// +// AuthAppDelegate.swift +// +// Auth +// +// Created by hyerin on . +// + +import UIKit + +import AuthUserInterface +import AuthUserInterfaceImpl +import AuthDomain +import AuthDomainImpl +import AuthData + +import RIBs +import RxSwift + +@main +final class AuthAppDelegate: UIResponder, UIApplicationDelegate { + + private final class MockSignInListener: LoggedOutListener { } + + var window: UIWindow? + private var router: ViewableRouting? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + let window = UIWindow(frame: UIScreen.main.bounds) + + router = LoggedOutBuilder(dependency: MOCKAuthComponent()) + .build(withListener: MockSignInListener()) + router?.interactable.activate() + router?.load() + + window.rootViewController = self.router?.viewControllable.uiviewController + window.makeKeyAndVisible() + self.window = window + + return true + } +} + diff --git a/Features/Auth/AuthUserInterface/DemoApp/Sources/MockComponent.swift b/Features/Auth/AuthUserInterface/DemoApp/Sources/MockComponent.swift new file mode 100644 index 00000000..5079a9c4 --- /dev/null +++ b/Features/Auth/AuthUserInterface/DemoApp/Sources/MockComponent.swift @@ -0,0 +1,59 @@ +// +// MockComponent.swift +// AuthUserInterfaceDemoApp +// +// Created by 최혜린 on 2023/07/14. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +import AuthUserInterface +import AuthUserInterfaceImpl +import AuthDomain +import AuthDomainImpl +import AuthData +import MOITWeb +import MOITWebImpl +import TokenManager +import TokenManagerImpl + +import RIBs +import RxSwift + +final class MOCKAuthComponent: Component, + LoggedOutDependency, + ProfileSelectDependency, + SignUpDependency, + MOITWebDependency, + LoggedOutInteractorDependency { + + init() { + super.init(dependency: EmptyComponent()) + } + + var fetchRandomNumberUseCase: FetchRandomNumberUseCase = FetchRandomNumberUseCaseImpl() + + var postJoinInfoUseCase: PostJoinInfoUseCase = PostJoinInfoUseCaseImpl(joinRepository: MockJoinRepository()) + + var saveTokenUseCase: SaveTokenUseCase = SaveTokenUseCaseImpl(tokenManager: TokenManagerImpl()) + + lazy var profileSelectBuildable: ProfileSelectBuildable = { + return ProfileSelectBuilder(dependency: self) + }() + + lazy var signUpBuildable: SignUpBuildable = { + return SignUpBuilder(dependency: self) + }() + + lazy var moitWebBuildable: MOITWebBuildable = { + return MOITWebBuilder(dependency: self) + }() +} + +final class MockJoinRepository: JoinRepository { + + func post(imageIndex: Int, name: String, inviteCode: String?) -> Single { + Single.just(3) + } +} diff --git a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift similarity index 53% rename from Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift rename to Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift index 1fe04371..4d2851a7 100644 --- a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift +++ b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutBuilder.swift @@ -6,14 +6,16 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignInUserInterface +import Foundation -import RIBs +import AuthUserInterface +import AuthDomain -public protocol LoggedOutDependency: Dependency { -} +import RIBs -final class LoggedOutComponent: Component { +final class LoggedOutComponent: Component, + LoggedOutInteractorDependency { + var saveTokenUseCase: SaveTokenUseCase { dependency.saveTokenUseCase } } // MARK: - Builder @@ -27,8 +29,17 @@ public final class LoggedOutBuilder: Builder, LoggedOutBuil public func build(withListener listener: LoggedOutListener) -> ViewableRouting { let component = LoggedOutComponent(dependency: dependency) let viewController = LoggedOutViewController() - let interactor = LoggedOutInteractor(presenter: viewController) + let interactor = LoggedOutInteractor( + presenter: viewController, + dependency: component + ) interactor.listener = listener - return LoggedOutRouter(interactor: interactor, viewController: viewController) + + return LoggedOutRouter( + interactor: interactor, + viewController: viewController, + signInWebBuildable: dependency.moitWebBuildable, + signUpBuildable: dependency.signUpBuildable + ) } } diff --git a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift similarity index 52% rename from Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift rename to Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift index 8ddf5a42..9eced792 100644 --- a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift +++ b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutInteractor.swift @@ -6,25 +6,38 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // +import AuthUserInterface +import AuthDomain + import RIBs import RxSwift -import CSLogger -import SignInUserInterface - protocol LoggedOutRouting: ViewableRouting { + func attachSignInWeb() + func detachSignInWeb() + func routeToSignUp(with response: MOITSignInResponse) } protocol LoggedOutPresentable: Presentable { var listener: LoggedOutPresentableListener? { get set } } +public protocol LoggedOutInteractorDependency: AnyObject { + var saveTokenUseCase: SaveTokenUseCase { get } +} + final class LoggedOutInteractor: PresentableInteractor, LoggedOutInteractable, LoggedOutPresentableListener { weak var router: LoggedOutRouting? weak var listener: LoggedOutListener? + + private let dependency: LoggedOutInteractorDependency - override init(presenter: LoggedOutPresentable) { + init( + presenter: LoggedOutPresentable, + dependency: LoggedOutInteractorDependency + ) { + self.dependency = dependency super.init(presenter: presenter) presenter.listener = self } @@ -38,10 +51,23 @@ final class LoggedOutInteractor: PresentableInteractor, Lo } func kakaoSignInButtonDidTap() { - CSLogger.Logger.debug("kakaoSignIn") + router?.attachSignInWeb() } func appleSignInButtonDidTap() { - CSLogger.Logger.debug("appleSignIn") +// CSLogger.Logger.debug("appleSignIn") + } + +// MARK: - MOITWeb + func shouldDetach(withPop: Bool) { + router?.detachSignInWeb() + } + + func authorizationDidFinish(with signInResponse: MOITSignInResponse) { + router?.routeToSignUp(with: signInResponse) + } + + func didSignIn(with token: String) { + dependency.saveTokenUseCase.execute(token: token) } } diff --git a/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutRouter.swift b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutRouter.swift new file mode 100644 index 00000000..acda1c77 --- /dev/null +++ b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutRouter.swift @@ -0,0 +1,98 @@ +// +// LoggedOutRouter.swift +// SignInUserInterfaceImpl +// +// Created by 최혜린 on 2023/06/19. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import RIBs + +import AuthUserInterface +import AuthDomain +import AuthUserInterface +import Utils +import MOITWeb + +import RxRelay + +protocol LoggedOutInteractable: Interactable, MOITWebListener, SignUpListener { + var router: LoggedOutRouting? { get set } + var listener: LoggedOutListener? { get set } +} + +protocol LoggedOutViewControllable: ViewControllable { +} + +final class LoggedOutRouter: ViewableRouter, LoggedOutRouting { + + private let signInWebBuildable: MOITWebBuildable + private var signInWebRouting: Routing? + + private let signUpBuildable: SignUpBuildable + private var signUpRouting: Routing? + + init( + interactor: LoggedOutInteractable, + viewController: LoggedOutViewControllable, + signInWebBuildable: MOITWebBuildable, + signUpBuildable: SignUpBuildable + ) { + self.signInWebBuildable = signInWebBuildable + self.signUpBuildable = signUpBuildable + super.init(interactor: interactor, viewController: viewController) + interactor.router = self + } + + func attachSignInWeb() { + if signInWebRouting != nil { return } + + let router = signInWebBuildable.build( + withListener: interactor, + path: .signIn + ) + let viewController = router.viewControllable + router.viewControllable.uiviewController.modalPresentationStyle = .fullScreen + viewControllable.present( + viewController, + animated: true, + completion: nil + ) + + signInWebRouting = router + attachChild(router) + } + + func detachSignInWeb() { + guard let router = signInWebRouting else { return } + + viewControllable.dismiss(completion: nil) + signInWebRouting = nil + detachChild(router) + } + + func routeToSignUp(with response: MOITSignInResponse) { + detachSignInWeb() + attachSignUp(with: response) + } + + private func attachSignUp(with response: MOITSignInResponse) { + if signUpRouting != nil { return } + + let router = signUpBuildable.build( + withListener: interactor, + signInResponse: response + ) + let viewController = router.viewControllable + router.viewControllable.uiviewController.modalPresentationStyle = .fullScreen + + signUpRouting = router + attachChild(router) + + viewControllable.present( + viewController, + animated: true, + completion: nil + ) + } +} diff --git a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutViewController.swift b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutViewController.swift similarity index 98% rename from Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutViewController.swift rename to Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutViewController.swift index 918598da..1a0e759d 100644 --- a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutViewController.swift +++ b/Features/Auth/AuthUserInterface/Implement/LoggedOut/LoggedOutViewController.swift @@ -22,8 +22,6 @@ protocol LoggedOutPresentableListener: AnyObject { } final class LoggedOutViewController: UIViewController, LoggedOutPresentable, LoggedOutViewControllable { - - weak var listener: LoggedOutPresentableListener? // MARK: - UI @@ -59,6 +57,7 @@ final class LoggedOutViewController: UIViewController, LoggedOutPresentable, Log // MARK: - property + weak var listener: LoggedOutPresentableListener? private let disposeBag = DisposeBag() private let lastGradientColor = UIColor(red: 236 / 255, green: 236 / 255, blue: 239 / 255, alpha: 1) diff --git a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift similarity index 97% rename from Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift rename to Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift index 279fcbd3..7988ad00 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift +++ b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectBuilder.swift @@ -5,7 +5,7 @@ // Created by 김찬수 on 2023/06/21. // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface +import AuthUserInterface import RIBs diff --git a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift similarity index 90% rename from Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift rename to Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift index 34d1b696..58a80585 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift +++ b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectInteractor.swift @@ -5,7 +5,7 @@ // Created by 김찬수 on 2023/06/21. // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface +import AuthUserInterface import RIBs import RxSwift @@ -20,7 +20,9 @@ protocol ProfileSelectPresentable: Presentable { func updateProfileIndex(index: Int?) } -final class ProfileSelectInteractor: PresentableInteractor, ProfileSelectInteractable, ProfileSelectPresentableListener { +final class ProfileSelectInteractor: PresentableInteractor, + ProfileSelectInteractable, + ProfileSelectPresentableListener { weak var router: ProfileSelectRouting? weak var listener: ProfileSelectListener? diff --git a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift similarity index 96% rename from Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift rename to Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift index 5bf313ad..3bd79379 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift +++ b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectRouter.swift @@ -6,7 +6,7 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface +import AuthUserInterface import RIBs diff --git a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectView.swift b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectView.swift similarity index 99% rename from Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectView.swift rename to Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectView.swift index fc931079..ea7fab9c 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectView.swift +++ b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectView.swift @@ -8,7 +8,7 @@ import UIKit -import SignUpUserInterface +import AuthUserInterface import Utils import ResourceKit import DesignSystem diff --git a/Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectViewController.swift b/Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectViewController.swift similarity index 100% rename from Features/SignUp/SignUpUserInterface/Implement/ProfileSelect/ProfileSelectViewController.swift rename to Features/Auth/AuthUserInterface/Implement/ProfileSelect/ProfileSelectViewController.swift diff --git a/Features/SignUp/SignUpUserInterface/Implement/SignUpBuilder.swift b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpBuilder.swift similarity index 69% rename from Features/SignUp/SignUpUserInterface/Implement/SignUpBuilder.swift rename to Features/Auth/AuthUserInterface/Implement/SignUp/SignUpBuilder.swift index 6282a53c..22531f23 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/SignUpBuilder.swift +++ b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpBuilder.swift @@ -6,17 +6,26 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface - -import SignUpDomain +import AuthUserInterface +import AuthDomain import RIBs -public final class SignUpComponent: Component, SignUpInteractorDependency { +public final class SignUpComponent: Component, + SignUpInteractorDependency { var fetchRandomNumberUseCase: FetchRandomNumberUseCase { dependency.fetchRandomNumberUseCase } var postJoinInfoUseCase: PostJoinInfoUseCase { dependency.postJoinInfoUseCase } var profileSelectBuildable: ProfileSelectBuildable { dependency.profileSelectBuildable } + let signInResponse: MOITSignInResponse + + init( + dependency: SignUpDependency, + signInResponse: MOITSignInResponse + ) { + self.signInResponse = signInResponse + super.init(dependency: dependency) + } } // MARK: - Builder @@ -27,8 +36,14 @@ public final class SignUpBuilder: Builder, SignUpBuildable { super.init(dependency: dependency) } - public func build(withListener listener: SignUpListener) -> ViewableRouting { - let component = SignUpComponent(dependency: dependency) + public func build( + withListener listener: SignUpListener, + signInResponse: MOITSignInResponse + ) -> ViewableRouting { + let component = SignUpComponent( + dependency: dependency, + signInResponse: signInResponse + ) let viewController = SignUpViewController() let interactor = SignUpInteractor( presenter: viewController, diff --git a/Features/SignUp/SignUpUserInterface/Implement/SignUpInteractor.swift b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpInteractor.swift similarity index 97% rename from Features/SignUp/SignUpUserInterface/Implement/SignUpInteractor.swift rename to Features/Auth/AuthUserInterface/Implement/SignUp/SignUpInteractor.swift index 828f25d2..36c29347 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/SignUpInteractor.swift +++ b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpInteractor.swift @@ -6,9 +6,9 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface -import SignUpData -import SignUpDomain +import AuthUserInterface +import AuthData +import AuthDomain import DesignSystem import Utils @@ -34,6 +34,7 @@ protocol SignUpPresentable: Presentable { protocol SignUpInteractorDependency { var fetchRandomNumberUseCase: FetchRandomNumberUseCase { get } var postJoinInfoUseCase: PostJoinInfoUseCase { get } + var signInResponse: MOITSignInResponse { get } } final class SignUpInteractor: PresentableInteractor, diff --git a/Features/SignUp/SignUpUserInterface/Implement/SignUpRouter.swift b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpRouter.swift similarity index 98% rename from Features/SignUp/SignUpUserInterface/Implement/SignUpRouter.swift rename to Features/Auth/AuthUserInterface/Implement/SignUp/SignUpRouter.swift index d98157c2..d90c44ee 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/SignUpRouter.swift +++ b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpRouter.swift @@ -6,7 +6,7 @@ // Copyright © 2023 chansoo.MOIT. All rights reserved. // -import SignUpUserInterface +import AuthUserInterface import RIBs import Utils diff --git a/Features/SignUp/SignUpUserInterface/Implement/SignUpViewController.swift b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpViewController.swift similarity index 99% rename from Features/SignUp/SignUpUserInterface/Implement/SignUpViewController.swift rename to Features/Auth/AuthUserInterface/Implement/SignUp/SignUpViewController.swift index b1b6d9cc..2d004d56 100644 --- a/Features/SignUp/SignUpUserInterface/Implement/SignUpViewController.swift +++ b/Features/Auth/AuthUserInterface/Implement/SignUp/SignUpViewController.swift @@ -7,7 +7,7 @@ // import UIKit -import SignUpUserInterface +import AuthUserInterface import DesignSystem import Utils import ResourceKit diff --git a/Features/SignIn/SignInUserInterface/Interface/LoggedOut/LoggedOutBuildable.swift b/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutBuildable.swift similarity index 100% rename from Features/SignIn/SignInUserInterface/Interface/LoggedOut/LoggedOutBuildable.swift rename to Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutBuildable.swift diff --git a/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutDependency.swift b/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutDependency.swift new file mode 100644 index 00000000..16e5bcc6 --- /dev/null +++ b/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutDependency.swift @@ -0,0 +1,20 @@ +// +// LoggedOutDependency.swift +// SignInUserInterface +// +// Created by 최혜린 on 2023/07/13. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +import MOITWeb +import AuthDomain + +import RIBs + +public protocol LoggedOutDependency: Dependency { + var signUpBuildable: SignUpBuildable { get } + var moitWebBuildable: MOITWebBuildable { get } + var saveTokenUseCase: SaveTokenUseCase { get } +} diff --git a/Features/SignIn/SignInUserInterface/Interface/LoggedOut/LoggedOutListener.swift b/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutListener.swift similarity index 77% rename from Features/SignIn/SignInUserInterface/Interface/LoggedOut/LoggedOutListener.swift rename to Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutListener.swift index 1f6b9bbf..3bfa9052 100644 --- a/Features/SignIn/SignInUserInterface/Interface/LoggedOut/LoggedOutListener.swift +++ b/Features/Auth/AuthUserInterface/Interface/LoggedOut/LoggedOutListener.swift @@ -8,5 +8,4 @@ import RIBs -public protocol LoggedOutListener: AnyObject { -} +public protocol LoggedOutListener: AnyObject { } diff --git a/Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectBuildable.swift b/Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectBuildable.swift similarity index 100% rename from Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectBuildable.swift rename to Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectBuildable.swift diff --git a/Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectDependency.swift b/Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectDependency.swift similarity index 100% rename from Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectDependency.swift rename to Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectDependency.swift diff --git a/Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectListener.swift b/Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectListener.swift similarity index 100% rename from Features/SignUp/SignUpUserInterface/Interface/ProfileSelect/ProfileSelectListener.swift rename to Features/Auth/AuthUserInterface/Interface/ProfileSelect/ProfileSelectListener.swift diff --git a/Features/SignUp/SignUpUserInterface/Interface/SignUpBuildable.swift b/Features/Auth/AuthUserInterface/Interface/SignUp/SignUpBuildable.swift similarity index 63% rename from Features/SignUp/SignUpUserInterface/Interface/SignUpBuildable.swift rename to Features/Auth/AuthUserInterface/Interface/SignUp/SignUpBuildable.swift index e6a841b3..84af4713 100644 --- a/Features/SignUp/SignUpUserInterface/Interface/SignUpBuildable.swift +++ b/Features/Auth/AuthUserInterface/Interface/SignUp/SignUpBuildable.swift @@ -8,8 +8,13 @@ import UIKit +import AuthDomain + import RIBs public protocol SignUpBuildable: Buildable { - func build(withListener listener: SignUpListener) -> ViewableRouting + func build( + withListener listener: SignUpListener, + signInResponse: MOITSignInResponse + ) -> ViewableRouting } diff --git a/Features/SignUp/SignUpUserInterface/Interface/SignUpDependency.swift b/Features/Auth/AuthUserInterface/Interface/SignUp/SignUpDependency.swift similarity index 95% rename from Features/SignUp/SignUpUserInterface/Interface/SignUpDependency.swift rename to Features/Auth/AuthUserInterface/Interface/SignUp/SignUpDependency.swift index 64fefa05..38ea7918 100644 --- a/Features/SignUp/SignUpUserInterface/Interface/SignUpDependency.swift +++ b/Features/Auth/AuthUserInterface/Interface/SignUp/SignUpDependency.swift @@ -8,7 +8,7 @@ import UIKit -import SignUpDomain +import AuthDomain import RIBs diff --git a/Features/SignUp/SignUpUserInterface/Interface/SignUpListener.swift b/Features/Auth/AuthUserInterface/Interface/SignUp/SignUpListener.swift similarity index 100% rename from Features/SignUp/SignUpUserInterface/Interface/SignUpListener.swift rename to Features/Auth/AuthUserInterface/Interface/SignUp/SignUpListener.swift diff --git a/Features/Auth/AuthUserInterface/Project.swift b/Features/Auth/AuthUserInterface/Project.swift new file mode 100644 index 00000000..86263a09 --- /dev/null +++ b/Features/Auth/AuthUserInterface/Project.swift @@ -0,0 +1,48 @@ +// +// AuthAppDelegate.swift +// +// Auth +// +// Created by hyerin +// + +import ProjectDescription +import ProjectDescriptionHelpers +import UtilityPlugin + +let project = Project.invertedDualTargetProjectWithDemoApp( + name: "AuthUserInterface", + platform: .iOS, + iOSTargetVersion: "16.0.0", + interfaceDependencies: [ + .ThirdParty.RIBs, + .ThirdParty.RxSwift, + .ThirdParty.RxRelay, + + .Feature.MOITWeb.Interface, + .Feature.Auth.Domain.Interface, + + .TokenManager.Interface + ], + implementDependencies: [ + .ThirdParty.RxGesture, + .ThirdParty.RxCocoa, + .ThirdParty.FlexLayout, + .ThirdParty.PinLayout, + + .ResourceKit, + .DesignSystem, + + .Core.Utils, + + .Feature.Auth.Data.Interface + ], + demoAppDependencies: [ + .Feature.MOITWeb.Implement, + .Feature.Auth.Domain.Implement, + + .TokenManager.Implement + ], + isUserInterface: true +) + diff --git a/Features/SignIn/SignInDomain/Implement/dummy.swift b/Features/Auth/AuthUserInterface/Tests/dummy.swift similarity index 100% rename from Features/SignIn/SignInDomain/Implement/dummy.swift rename to Features/Auth/AuthUserInterface/Tests/dummy.swift diff --git a/Features/MOITParticipate/MOITParticipateUserInterface/Implement/ParticipationSuccess/ParticipationSuccessViewController.swift b/Features/MOITParticipate/MOITParticipateUserInterface/Implement/ParticipationSuccess/ParticipationSuccessViewController.swift index 97fdff4d..4a87d8e1 100644 --- a/Features/MOITParticipate/MOITParticipateUserInterface/Implement/ParticipationSuccess/ParticipationSuccessViewController.swift +++ b/Features/MOITParticipate/MOITParticipateUserInterface/Implement/ParticipationSuccess/ParticipationSuccessViewController.swift @@ -8,9 +8,9 @@ import UIKit -import MOITFoundation import DesignSystem import ResourceKit +import Utils import RIBs import RxSwift diff --git a/Features/MOITWeb/DemoApp/Sources/MOITWebDemoRootViewController.swift b/Features/MOITWeb/DemoApp/Sources/MOITWebDemoRootViewController.swift index 3008462b..c23bf3c8 100644 --- a/Features/MOITWeb/DemoApp/Sources/MOITWebDemoRootViewController.swift +++ b/Features/MOITWeb/DemoApp/Sources/MOITWebDemoRootViewController.swift @@ -8,8 +8,11 @@ import Foundation import UIKit + import MOITWeb import MOITWebImpl +import AuthDomain + import RIBs final class MOITWebDemoRootViewController: UITableViewController { @@ -97,6 +100,10 @@ extension MOITWebDemoRootViewController { // MARK: - MOITWebListener extension MOITWebDemoRootViewController: MOITWebListener { + func authorizationDidFinish(with signInResponse: MOITSignInResponse) { + + } + func shouldDetach(withPop: Bool) { guard let router = self.webRouter else { return } self.webRouter = nil diff --git a/Features/MOITWeb/Implement/MOITWebInteractor.swift b/Features/MOITWeb/Implement/MOITWebInteractor.swift index 511b59cc..e73459c5 100644 --- a/Features/MOITWeb/Implement/MOITWebInteractor.swift +++ b/Features/MOITWeb/Implement/MOITWebInteractor.swift @@ -6,21 +6,27 @@ // Copyright © 2023 chansoo.io. All rights reserved. // +import WebKit + +import MOITWeb +import AuthDomain + import RIBs import RxSwift -import MOITWeb -protocol MOITWebRouting: ViewableRouting { -} + +protocol MOITWebRouting: ViewableRouting { } protocol MOITWebPresentable: Presentable { var listener: MOITWebPresentableListener? { get set } + func render(with path: String) + func showErrorAlert() } final class MOITWebInteractor: PresentableInteractor, MOITWebInteractable, - MOITWebPresentableListener { + MOITWebPresentableListener { // MARK: - Properties @@ -58,8 +64,25 @@ extension MOITWebInteractor { func didSwipeBack() { self.listener?.shouldDetach(withPop: false) } - + + func notRegisteredMemeberDidSignIn(with headerFields: [AnyHashable: Any]) { + let signInResponse = MOITSignInResponse(headerFields: headerFields) + listener?.authorizationDidFinish(with: signInResponse) + } + + func registeredMemberDidSignIn(with headerFields: [AnyHashable: Any]) { + if let authorizationToken = headerFields ["Authorization"] as? String { + listener?.didSignIn(with: authorizationToken) + } else { + presenter.showErrorAlert() + } + } + func didTapBackButton() { self.listener?.shouldDetach(withPop: true) } + + func didTapErrorAlertOkButton() { + self.listener?.shouldDetach(withPop: false) + } } diff --git a/Features/MOITWeb/Implement/MOITWebRouter.swift b/Features/MOITWeb/Implement/MOITWebRouter.swift index 6c132582..f60e725b 100644 --- a/Features/MOITWeb/Implement/MOITWebRouter.swift +++ b/Features/MOITWeb/Implement/MOITWebRouter.swift @@ -6,9 +6,10 @@ // Copyright © 2023 chansoo.io. All rights reserved. // -import RIBs import MOITWeb +import RIBs + protocol MOITWebInteractable: Interactable { var router: MOITWebRouting? { get set } var listener: MOITWebListener? { get set } @@ -18,8 +19,8 @@ protocol MOITWebViewControllable: ViewControllable { } final class MOITWebRouter: ViewableRouter, - MOITWebRouting { - + MOITWebRouting { + override init( interactor: MOITWebInteractable, viewController: MOITWebViewControllable diff --git a/Features/MOITWeb/Implement/MOITWebViewController.swift b/Features/MOITWeb/Implement/MOITWebViewController.swift index 45586dcb..9dd760af 100644 --- a/Features/MOITWeb/Implement/MOITWebViewController.swift +++ b/Features/MOITWeb/Implement/MOITWebViewController.swift @@ -6,15 +6,21 @@ // Copyright © 2023 chansoo.io. All rights reserved. // -import RIBs -import RxSwift import UIKit import WebKit +import CSLogger +import Utils + +import RIBs +import RxSwift protocol MOITWebPresentableListener: AnyObject { func didSwipeBack() + func notRegisteredMemeberDidSignIn(with headerFields: [AnyHashable: Any]) + func registeredMemberDidSignIn(with headerFields: [AnyHashable: Any]) func didTapBackButton() + func didTapErrorAlertOkButton() } final class MOITWebViewController: UIViewController, @@ -25,7 +31,8 @@ final class MOITWebViewController: UIViewController, static let messageName = "MOIT" // TODO: 합의 후 수정 필요 - static let domain = "https://dev-moit-web.vercel.app" + static let domain = "http://moit-backend-eb-env.eba-qtcnkjjy.ap-northeast-2.elasticbeanstalk.com/api/v1/" +// static let domain = "https://dev-moit-web.vercel.app" } weak var listener: MOITWebPresentableListener? @@ -38,8 +45,10 @@ final class MOITWebViewController: UIViewController, override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) + + self.removeWKScriptMessageHandler(messageName: Self.Constant.messageName) + if self.isMovingFromParent { - self.removeWKScriptMessageHandler(messageName: Self.Constant.messageName) self.listener?.didSwipeBack() } } @@ -55,6 +64,7 @@ extension MOITWebViewController { let configuration = self.setWebConfiguration(with: cookie) let webView = WKWebView(frame: self.view.frame, configuration: configuration) webView.uiDelegate = self + webView.navigationDelegate = self self.view.addSubview(webView) guard let url = URL(string: "\(Self.Constant.domain)\(path)") else { return } @@ -142,4 +152,81 @@ extension MOITWebViewController: WKUIDelegate { // MARK: - WKNavigationDelegate extension MOITWebViewController: WKNavigationDelegate { + func webView( + _ webView: WKWebView, + decidePolicyFor navigationResponse: WKNavigationResponse, + decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void + ) { + let currentPath = navigationResponse.response.url?.path() ?? "" + let redirectURL = RedirectURL(pathRawValue: currentPath) + + switch redirectURL { + case .signInSuccess: + guard let response = navigationResponse.response as? HTTPURLResponse else { return decisionHandler(.allow) } + + let responsePolicy = executeResponseForSignIn(with: response) + decisionHandler(responsePolicy) + default: + decisionHandler(.allow) + } + } + + private func executeResponseForSignIn( + with response: HTTPURLResponse + ) -> WKNavigationResponsePolicy { + + Logger.debug(response.statusCode) + + let headerFields = response.allHeaderFields + + switch response.statusCode { + case (200...299): + listener?.registeredMemberDidSignIn(with: headerFields) + return .allow + case 401: + listener?.notRegisteredMemeberDidSignIn(with: headerFields) + return .cancel + default: + return .allow + } + } +} + +// MARK: - presentable +extension MOITWebViewController { + func showErrorAlert() { + showAlert( + message: StringResource.errorMessage.value, + type: .single, + okActionHandler: { [weak self] in + self?.listener?.didTapErrorAlertOkButton() + } + ) + } +} + +extension MOITWebViewController { + enum RedirectURL: String { + case none = "" + case signInSuccess = "/api/v1/auth/sign-in/success" + + init( + pathRawValue: String + ) { + self = RedirectURL(rawValue: pathRawValue) ?? .none + } + } +} + +extension MOITWebViewController { + enum StringResource { + case errorMessage + + var value: String { + switch self { + case .errorMessage: + return "네트워크 에러가 발생했습니다. 다시 시도해주세요!" + } + } + } } diff --git a/Features/MOITWeb/Interface/MOITWebListener.swift b/Features/MOITWeb/Interface/MOITWebListener.swift index 639f3516..6e452405 100644 --- a/Features/MOITWeb/Interface/MOITWebListener.swift +++ b/Features/MOITWeb/Interface/MOITWebListener.swift @@ -8,6 +8,10 @@ import Foundation +import AuthDomain + public protocol MOITWebListener: AnyObject { func shouldDetach(withPop: Bool) + func authorizationDidFinish(with signInResponse: MOITSignInResponse) + func didSignIn(with token: String) } diff --git a/Features/MOITWeb/Interface/MOITWebPath.swift b/Features/MOITWeb/Interface/MOITWebPath.swift index 3cb04d19..9881811a 100644 --- a/Features/MOITWeb/Interface/MOITWebPath.swift +++ b/Features/MOITWeb/Interface/MOITWebPath.swift @@ -13,6 +13,7 @@ public enum MOITWebPath { case modify(id: String) case attendance case attendanceResult + case signIn public var path: String { switch self { @@ -20,6 +21,7 @@ public enum MOITWebPath { case .register: return "/register" case .modify(let id): return "/register?id=\(id)" case .attendanceResult: return "/attendanceResult" + case .signIn: return "auth/sign-in" } } } diff --git a/Features/MOITWeb/Project.swift b/Features/MOITWeb/Project.swift index 05167dba..7ae00d2b 100644 --- a/Features/MOITWeb/Project.swift +++ b/Features/MOITWeb/Project.swift @@ -13,13 +13,18 @@ import UtilityPlugin let project = Project.invertedDualTargetProjectWithDemoApp( name: "MOITWeb", interfaceDependencies: [ - .ThirdParty.RIBs + .ThirdParty.RIBs, + .Feature.Auth.Domain.Interface ], implementDependencies: [ - .ThirdParty.RIBs, .ThirdParty.PinLayout, - .ThirdParty.FlexLayout + .ThirdParty.FlexLayout, + + .Core.CSLogger, + .Core.Utils ], + demoAppDependencies: [ + ], useTestTarget: true, isUserInterface: true ) diff --git a/Features/SignIn/SignInDomain/Interface/dummy.swift b/Features/SignIn/SignInDomain/Interface/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignIn/SignInDomain/Interface/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignIn/SignInDomain/Tests/dummy.swift b/Features/SignIn/SignInDomain/Tests/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignIn/SignInDomain/Tests/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignIn/SignInUserInterface/DemoApp/Sources/SignInUserInterfaceAppDelegate.swift b/Features/SignIn/SignInUserInterface/DemoApp/Sources/SignInUserInterfaceAppDelegate.swift deleted file mode 100644 index b0e45f2d..00000000 --- a/Features/SignIn/SignInUserInterface/DemoApp/Sources/SignInUserInterfaceAppDelegate.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// SignInAppDelegate.swift -// -// SignIn -// -// Created by hyerin on . -// - -import UIKit - -import SignInUserInterface -import SignInUserInterfaceImpl - -import RIBs - -@main -final class SignInAppDelegate: UIResponder, UIApplicationDelegate { - - private final class MockSignInDependency: LoggedOutDependency { - - } - - private final class MockSignInListener: LoggedOutListener { - - } - - var window: UIWindow? - - private var router: ViewableRouting? - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - let window = UIWindow(frame: UIScreen.main.bounds) - - router = LoggedOutBuilder(dependency: MockSignInDependency()) - .build(withListener: MockSignInListener()) - router?.load() - router?.interactable.activate() - - window.rootViewController = self.router?.viewControllable.uiviewController - window.makeKeyAndVisible() - self.window = window - - return true - } -} - diff --git a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutRouter.swift b/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutRouter.swift deleted file mode 100644 index f1a5d04a..00000000 --- a/Features/SignIn/SignInUserInterface/Implement/LoggedOut/LoggedOutRouter.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// LoggedOutRouter.swift -// SignInUserInterfaceImpl -// -// Created by 최혜린 on 2023/06/19. -// Copyright © 2023 chansoo.MOIT. All rights reserved. -// - -import RIBs - -import SignInUserInterface - -protocol LoggedOutInteractable: Interactable { - var router: LoggedOutRouting? { get set } - var listener: LoggedOutListener? { get set } -} - -protocol LoggedOutViewControllable: ViewControllable { -} - -final class LoggedOutRouter: ViewableRouter, LoggedOutRouting { - - override init(interactor: LoggedOutInteractable, viewController: LoggedOutViewControllable) { - super.init(interactor: interactor, viewController: viewController) - interactor.router = self - } -} diff --git a/Features/SignIn/SignInUserInterface/Project.swift b/Features/SignIn/SignInUserInterface/Project.swift deleted file mode 100644 index 0a7cde7b..00000000 --- a/Features/SignIn/SignInUserInterface/Project.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// SignInAppDelegate.swift -// -// SignIn -// -// Created by hyerin -// - -import ProjectDescription -import ProjectDescriptionHelpers -import UtilityPlugin - -let project = Project.invertedDualTargetProjectWithDemoApp( - name: "SignInUserInterface", - platform: .iOS, - iOSTargetVersion: "16.0.0", - interfaceDependencies: [ - .ThirdParty.RIBs - ], - implementDependencies: [ - .ThirdParty.RIBs, - .ThirdParty.FlexLayout, - .ThirdParty.PinLayout, - .ResourceKit, - .DesignSystem - ], - isUserInterface: true -) - diff --git a/Features/SignIn/SignInUserInterface/Tests/dummy.swift b/Features/SignIn/SignInUserInterface/Tests/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignIn/SignInUserInterface/Tests/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignUp/SignUpData/Implement/dummy.swift b/Features/SignUp/SignUpData/Implement/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignUp/SignUpData/Implement/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignUp/SignUpData/Interface/dummy.swift b/Features/SignUp/SignUpData/Interface/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignUp/SignUpData/Interface/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignUp/SignUpData/Project.swift b/Features/SignUp/SignUpData/Project.swift deleted file mode 100644 index ae6af667..00000000 --- a/Features/SignUp/SignUpData/Project.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// SignUpAppDelegate.swift -// -// SignUp -// -// Created by kimchansoo -// - -import ProjectDescription -import ProjectDescriptionHelpers -import UtilityPlugin - -let project = Project.invertedDualTargetProject( - name: "SignUpData", - platform: .iOS, - iOSTargetVersion: "16.0.0", - interfaceDependencies: [ - ], - implementDependencies: [ - ] -) - diff --git a/Features/SignUp/SignUpData/Tests/dummy.swift b/Features/SignUp/SignUpData/Tests/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignUp/SignUpData/Tests/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignUp/SignUpDomain/Project.swift b/Features/SignUp/SignUpDomain/Project.swift deleted file mode 100644 index e8437cb2..00000000 --- a/Features/SignUp/SignUpDomain/Project.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// SignUpAppDelegate.swift -// -// SignUp -// -// Created by kimchansoo -// - -import ProjectDescription -import ProjectDescriptionHelpers -import UtilityPlugin - -let project = Project.invertedDualTargetProject( - name: "SignUpDomain", - platform: .iOS, - iOSTargetVersion: "16.0.0", - interfaceDependencies: [ - ], - implementDependencies: [ - ] -) - diff --git a/Features/SignUp/SignUpDomain/Tests/dummy.swift b/Features/SignUp/SignUpDomain/Tests/dummy.swift deleted file mode 100644 index 9997fb44..00000000 --- a/Features/SignUp/SignUpDomain/Tests/dummy.swift +++ /dev/null @@ -1,4 +0,0 @@ -// -// dummy.swift -// - diff --git a/Features/SignUp/SignUpUserInterface/DemoApp/Resources/LaunchScreen.storyboard b/Features/SignUp/SignUpUserInterface/DemoApp/Resources/LaunchScreen.storyboard deleted file mode 100644 index eae8f562..00000000 --- a/Features/SignUp/SignUpUserInterface/DemoApp/Resources/LaunchScreen.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Features/SignUp/SignUpUserInterface/DemoApp/Sources/MockComponent.swift b/Features/SignUp/SignUpUserInterface/DemoApp/Sources/MockComponent.swift deleted file mode 100644 index 2b0b4258..00000000 --- a/Features/SignUp/SignUpUserInterface/DemoApp/Sources/MockComponent.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// AppRoot.swift -// SignUpUserInterfaceDemoApp -// -// Created by 김찬수 on 2023/06/19. -// Copyright © 2023 chansoo.MOIT. All rights reserved. -// - -import Foundation - -import SignUpUserInterface -import SignUpUserInterfaceImpl -import SignUpDomain -import SignUpDomainImpl -import SignUpData -import SignUpDataImpl - -import RIBs -import RxSwift - -final class MOCKSignUpComponent: Component, - SignUpDependency, - ProfileSelectDependency { - - init() { - super.init(dependency: EmptyComponent()) - } - - var fetchRandomNumberUseCase: FetchRandomNumberUseCase = FetchRandomNumberUseCaseImpl() - - var postJoinInfoUseCase: PostJoinInfoUseCase = PostJoinInfoUseCaseImpl(joinRepository: MockJoinRepository()) - lazy var profileSelectBuildable: ProfileSelectBuildable = { - return ProfileSelectBuilder(dependency: self) - }() -} - -final class MockJoinRepository: JoinRepository { - - func post(imageIndex: Int, name: String, inviteCode: String?) -> Single { - Single.just(3) - } -} diff --git a/Features/SignUp/SignUpUserInterface/DemoApp/Sources/SignUpUserInterfaceAppDelegate.swift b/Features/SignUp/SignUpUserInterface/DemoApp/Sources/SignUpUserInterfaceAppDelegate.swift deleted file mode 100644 index 38b7384b..00000000 --- a/Features/SignUp/SignUpUserInterface/DemoApp/Sources/SignUpUserInterfaceAppDelegate.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// SignUpAppDelegate.swift -// -// SignUp -// -// Created by kimchansoo on . -// - -import UIKit - -import SignUpUserInterfaceImpl -import SignUpUserInterface -import DesignSystem - -import RIBs - -@main -final class SignUpAppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - var router: ViewableRouting? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - - let window = UIWindow(frame: UIScreen.main.bounds) - - let signUpBuilder = SignUpBuilder(dependency: MOCKSignUpComponent()) - self.router = signUpBuilder.build(withListener: MOCKMOITSignUpListener()) - - router?.load() - router?.interactable.activate() - window.rootViewController = router?.viewControllable.uiviewController - - window.makeKeyAndVisible() - self.window = window - return true - } - - private final class MOCKMOITSignUpListener: SignUpListener {} -} diff --git a/Features/SignUp/SignUpUserInterface/Project.swift b/Features/SignUp/SignUpUserInterface/Project.swift deleted file mode 100644 index b39bb50e..00000000 --- a/Features/SignUp/SignUpUserInterface/Project.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// SignUpAppDelegate.swift -// -// SignUp -// -// Created by kimchansoo -// - -import ProjectDescription -import ProjectDescriptionHelpers -import UtilityPlugin - -let project = Project.invertedDualTargetProjectWithDemoApp( - name: "SignUpUserInterface", - platform: .iOS, - iOSTargetVersion: "16.0.0", - interfaceDependencies: [ - .ThirdParty.RIBs, - .ThirdParty.RxSwift, - ], - implementDependencies: [ - .ThirdParty.RxGesture, - .ThirdParty.RxSwift, - .ThirdParty.RxCocoa, - .ThirdParty.RIBs, - - .ResourceKit, - .DesignSystem, - .Core.Utils, - - .Feature.SignUp.Domain.Interface, - .Feature.SignUp.Data.Interface, - ], - demoAppDependencies: [ - .Feature.SignUp.Domain.Implement, - .Feature.SignUp.Data.Implement, - ], - isUserInterface: true -) diff --git a/Features/SignUp/SignUpUserInterface/Tests/dummy.swift b/Features/SignUp/SignUpUserInterface/Tests/dummy.swift deleted file mode 100644 index cfe6c36c..00000000 --- a/Features/SignUp/SignUpUserInterface/Tests/dummy.swift +++ /dev/null @@ -1,3 +0,0 @@ -// -// dummy.swift -// diff --git a/MOITNetwork/Project.swift b/MOITNetwork/Project.swift index 7260a211..49be3bce 100644 --- a/MOITNetwork/Project.swift +++ b/MOITNetwork/Project.swift @@ -17,9 +17,6 @@ let project = Project.invertedDualTargetProject( .ThirdParty.RxCocoa ], implementDependencies: [ - .ThirdParty.RxSwift, - .ThirdParty.RxCocoa, - .Core.CSLogger ] ) diff --git a/Plugins/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift b/Plugins/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift index a78b5308..91f36503 100644 --- a/Plugins/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift +++ b/Plugins/UtilityPlugin/ProjectDescriptionHelpers/Dependency+Project.swift @@ -11,7 +11,7 @@ extension TargetDependency { public struct UserInterface {} } - public struct SignUp { + public struct Auth { public struct Data {} public struct Domain {} public struct UserInterface {} @@ -40,6 +40,8 @@ extension TargetDependency { } public struct MOITNetwork {} + + public struct TokenManager {} public static let ResourceKit = TargetDependency.project( target: "ResourceKit", @@ -67,6 +69,18 @@ public extension TargetDependency.Core { static let MOITFoundation = project(name: "MOITFoundation") } +public extension TargetDependency.TokenManager { + static let folderName = "TokenManager" + static func project(name: String, isInterface: Bool) -> TargetDependency { + let postfix: String = isInterface ? "" : "Impl" + return .project(target: "\(name)\(postfix)", + path: .relativeToRoot("\(folderName)")) + } + + static let Interface = project(name: "TokenManager", isInterface: true) + static let Implement = project(name: "TokenManager", isInterface: false) +} + // MARK: - Features/Home public extension TargetDependency.Feature.StudyList { static let folderName = "StudyList" @@ -92,29 +106,30 @@ public extension TargetDependency.Feature.StudyList.Data { static let Implement = TargetDependency.Feature.StudyList.project(name: "Data", isInterface: false) } -// MARK: - Features/SignUp -public extension TargetDependency.Feature.SignUp { - static let folderName = "SignUp" - static func project(name: String, isInterface: Bool) -> TargetDependency { - let postfix: String = isInterface ? "" : "Impl" - return .project(target: "\(folderName)\(name)\(postfix)", - path: .relativeToRoot("Features/\(folderName)/\(folderName)\(name)")) - } +// MARK: - Features/SignIn + +public extension TargetDependency.Feature.Auth { + static let folderName = "Auth" + static func project(name: String, isInterface: Bool) -> TargetDependency { + let postfix: String = isInterface ? "" : "Impl" + return .project(target: "\(folderName)\(name)\(postfix)", + path: .relativeToRoot("Features/\(folderName)/\(folderName)\(name)")) + } } -public extension TargetDependency.Feature.SignUp.UserInterface { - static let Interface = TargetDependency.Feature.SignUp.project(name: "UserInterface", isInterface: true) - static let Implement = TargetDependency.Feature.SignUp.project(name: "UserInterface", isInterface: false) +public extension TargetDependency.Feature.Auth.UserInterface { + static let Interface = TargetDependency.Feature.Auth.project(name: "UserInterface", isInterface: true) + static let Implement = TargetDependency.Feature.Auth.project(name: "UserInterface", isInterface: false) } -public extension TargetDependency.Feature.SignUp.Domain { - static let Interface = TargetDependency.Feature.SignUp.project(name: "Domain", isInterface: true) - static let Implement = TargetDependency.Feature.SignUp.project(name: "Domain", isInterface: false) +public extension TargetDependency.Feature.Auth.Domain { + static let Interface = TargetDependency.Feature.Auth.project(name: "Domain", isInterface: true) + static let Implement = TargetDependency.Feature.Auth.project(name: "Domain", isInterface: false) } -public extension TargetDependency.Feature.SignUp.Data { - static let Interface = TargetDependency.Feature.SignUp.project(name: "Data", isInterface: true) - static let Implement = TargetDependency.Feature.SignUp.project(name: "Data", isInterface: false) +public extension TargetDependency.Feature.Auth.Data { + static let Interface = TargetDependency.Feature.Auth.project(name: "Data", isInterface: true) + static let Implement = TargetDependency.Feature.Auth.project(name: "Data", isInterface: false) } // MARK: - Features/MOITParticipate diff --git a/TokenManager/Implement/TokenManagerImpl.swift b/TokenManager/Implement/TokenManagerImpl.swift new file mode 100644 index 00000000..b752a65c --- /dev/null +++ b/TokenManager/Implement/TokenManagerImpl.swift @@ -0,0 +1,123 @@ +// +// TokenManagerImpl.swift +// TokenManagerImpl +// +// Created by 최혜린 on 2023/07/17. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +import TokenManager +import CSLogger + +public struct TokenManagerImpl: TokenManager { + + private let keychainClass = kSecClassGenericPassword + + public init() {} + + public func get(key: KeychainType) -> String? { + + let query: [CFString: Any] = [ + kSecClass: keychainClass, + kSecAttrAccount: key.rawValue, + kSecMatchLimit: kSecMatchLimitOne, + kSecReturnAttributes: true, + kSecReturnData: true + ] + + var item: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &item) + guard status != errSecItemNotFound else { + Logger.debug("Keychain item not found") + return nil + } + guard status == errSecSuccess else { + Logger.debug("Keychain read Error") + return nil + } + + guard let existingItem = item as? [String: Any], + let data = existingItem[kSecValueData as String] as? Data, + let token = String(data: data, encoding: .utf8) else { return nil } + + Logger.debug("get token: \(token)") + + return token + } + + @discardableResult + public func save(token: String, with key: KeychainType) -> Bool { + + guard let tokenData = token.data(using: .utf8) else { return false } + + let query: [CFString: Any] = [ + kSecClass: keychainClass, + kSecAttrAccount: key.rawValue, + kSecValueData: tokenData + ] + + let status = SecItemAdd(query as CFDictionary, nil) + + Logger.debug("save token status: \(status)") + + switch status { + case errSecSuccess: + Logger.debug("save requested token: \(token), saved token: \(get(key: key))") + return true + case errSecDuplicateItem: + return update(token: token, with: key) + default: + return false + } + } + + @discardableResult + public func update(token: String, with key: KeychainType) -> Bool { + + guard let tokenData = token.data(using: .utf8) else { return false } + + let searchQuery: [CFString: Any] = [ + kSecClass: keychainClass, + kSecAttrAccount: key.rawValue + ] + + let updateQuery: [CFString: Any] = [ + kSecAttrAccount: key.rawValue, + kSecValueData: tokenData + ] + + let status = SecItemUpdate(searchQuery as CFDictionary, updateQuery as CFDictionary) + + Logger.debug("update token status: \(status)") + + return status == errSecSuccess + + switch status { + case errSecSuccess: + Logger.debug("update requested token: \(token), updated token: \(get(key: key))") + return true + case errSecItemNotFound: + return save(token: token, with: key) + default: + return true + } + } + + @discardableResult + public func delete(key: KeychainType) -> Bool { + + let query: [CFString: Any] = [ + kSecClass: keychainClass, + kSecAttrAccount: key.rawValue + ] + + let status = SecItemDelete(query as CFDictionary) + + Logger.debug("check empty token: Is \(get(key: key)) empty?") + Logger.debug("delete token status: \(status)") + + return status == errSecSuccess + } +} diff --git a/TokenManager/Interface/KeychainType.swift b/TokenManager/Interface/KeychainType.swift new file mode 100644 index 00000000..b2ca8078 --- /dev/null +++ b/TokenManager/Interface/KeychainType.swift @@ -0,0 +1,13 @@ +// +// KeychainType.swift +// TokenManager +// +// Created by 최혜린 on 2023/07/17. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +public enum KeychainType: String { + case authorizationToken +} diff --git a/TokenManager/Interface/TokenManager.swift b/TokenManager/Interface/TokenManager.swift new file mode 100644 index 00000000..ea279458 --- /dev/null +++ b/TokenManager/Interface/TokenManager.swift @@ -0,0 +1,23 @@ +// +// TokenManager.swift +// TokenManager +// +// Created by 최혜린 on 2023/07/17. +// Copyright © 2023 chansoo.MOIT. All rights reserved. +// + +import Foundation + +public protocol TokenManager { + + func get(key: KeychainType) -> String? + + @discardableResult + func save(token: String, with key: KeychainType) -> Bool + + @discardableResult + func update(token: String, with key: KeychainType) -> Bool + + @discardableResult + func delete(key: KeychainType) -> Bool +} diff --git a/TokenManager/Project.swift b/TokenManager/Project.swift new file mode 100644 index 00000000..5d18afa1 --- /dev/null +++ b/TokenManager/Project.swift @@ -0,0 +1,21 @@ +// +// Project.swift +// ProjectDescriptionHelpers +// +// Created by 최혜린 on 2023/07/17. +// + +import ProjectDescription +import ProjectDescriptionHelpers +import UtilityPlugin + +let project = Project.invertedDualTargetProject( + name: "TokenManager", + platform: .iOS, + iOSTargetVersion: "16.0.0", + interfaceDependencies: [ + ], + implementDependencies: [ + .Core.CSLogger + ] +) diff --git a/TokenManager/Tests/dummy.swift b/TokenManager/Tests/dummy.swift new file mode 100644 index 00000000..d702331a --- /dev/null +++ b/TokenManager/Tests/dummy.swift @@ -0,0 +1,6 @@ +// +// Project.swift +// ProjectDescriptionHelpers +// +// Created by 최혜린 on 2023/07/17. +// diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index a3c0bfc3..c57d69b0 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift @@ -142,11 +142,11 @@ extension Project { [ "CFBundleDevelopmentRegion": "ko_KR", "CFBundleShortVersionString": "1.0", - "CFBundleVersion": "1", + "CFBundleVersion": "1.0.0", "UILaunchStoryboardName": "LaunchScreen", - "NSAppTransportSecurity": [ - "NSAllowsArbitraryLoads": true - ] + "NSAppTransportSecurity" : [ + "NSAllowsArbitraryLoads": true + ] ] ), sources: ["./DemoApp/Sources/**"],