From 5bc0e3114614a13d60ee41f24782a489db57a782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 11:37:07 +0900 Subject: [PATCH 1/9] =?UTF-8?q?DEL::=20[#350]=20auth=20-=20appDelegate=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthFeature/Demo/Sources/Application/AppDelegate.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Projects/Features/AuthFeature/Demo/Sources/Application/AppDelegate.swift b/Projects/Features/AuthFeature/Demo/Sources/Application/AppDelegate.swift index 3ee96d86..1d352b75 100644 --- a/Projects/Features/AuthFeature/Demo/Sources/Application/AppDelegate.swift +++ b/Projects/Features/AuthFeature/Demo/Sources/Application/AppDelegate.swift @@ -8,13 +8,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? -// func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { -// if (AuthApi.isKakaoTalkLoginUrl(url)) { -// return AuthController.handleOpenUrl(url: url) -// } -// return false -// } - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { KakaoSDK.initSDK(appKey: "dcfcd3ab4a997c5a53e2ab26a8ec2a63") return true From ef268001b5ebeec025b4cfa9a685a0d60ed510bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 11:37:42 +0900 Subject: [PATCH 2/9] =?UTF-8?q?DEL::=20[#350]=20auth=20-=20Presenttable=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthFeature/Interface/Sources/AuthPresentable.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Projects/Features/AuthFeature/Interface/Sources/AuthPresentable.swift b/Projects/Features/AuthFeature/Interface/Sources/AuthPresentable.swift index eb9c5daf..19abd2b1 100644 --- a/Projects/Features/AuthFeature/Interface/Sources/AuthPresentable.swift +++ b/Projects/Features/AuthFeature/Interface/Sources/AuthPresentable.swift @@ -7,12 +7,7 @@ import RxCocoa import BaseFeatureDependency import Core -//public protocol AuthViewControllable: ViewControllable {} public protocol AuthCoordintable { -// var goolgeButtonTap: (() -> Void)? { get set } -// var appleButtonTap: (() -> Void)? { get set } -// var kakaoButtonTap: (() -> Bool)? { get set } - var disposeBag: DisposeBag { get set } } From 8888abdf2e0b9538383a201b1024897525dafcc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 11:39:34 +0900 Subject: [PATCH 3/9] =?UTF-8?q?REFACT::=20[#350]=20Auth=20-=20scenDelegate?= =?UTF-8?q?=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 함수형으로 뺐습니다. --- .../Sources/Application/SceneDelegate.swift | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/Projects/Features/AuthFeature/Demo/Sources/Application/SceneDelegate.swift b/Projects/Features/AuthFeature/Demo/Sources/Application/SceneDelegate.swift index 625fe0a3..bdad183e 100644 --- a/Projects/Features/AuthFeature/Demo/Sources/Application/SceneDelegate.swift +++ b/Projects/Features/AuthFeature/Demo/Sources/Application/SceneDelegate.swift @@ -17,44 +17,35 @@ import AuthFeatureInterface class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + var coordinator = FlowCoordinator() + var mainFlow: AuthFlow! func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { if let url = URLContexts.first?.url { - if (AuthApi.isKakaoTalkLoginUrl(url)) { - _ = AuthController.handleOpenUrl(url: url) - } + handleURL(url) } } - var coordinator = FlowCoordinator() - var mainFlow: AuthFlow! - - func scene(_ scene: UIScene, - willConnectTo session: UISceneSession, - options connectionOptions: UIScene.ConnectionOptions) { - guard let scene = (scene as? UIWindowScene) else { return } + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let scene = scene as? UIWindowScene else { return } window = UIWindow(frame: scene.coordinateSpace.bounds) window?.windowScene = scene + configureMainFlow() + window?.makeKeyAndVisible() + } - mainFlow = AuthFlow() + private func handleURL(_ url: URL) { + if AuthApi.isKakaoTalkLoginUrl(url) { + _ = AuthController.handleOpenUrl(url: url) + } + } + private func configureMainFlow() { + mainFlow = AuthFlow() coordinator.coordinate(flow: mainFlow, with: OneStepper(withSingleStep: MGStep.authSplashIsRequired)) - Flows.use(mainFlow, when: .created) { root in - self.window?.rootViewController = root - self.window?.makeKey() + Flows.use(mainFlow, when: .created) { [weak self] root in + self?.window?.rootViewController = root } - window?.makeKeyAndVisible() } - -// private func coordinatorLogStart() { -// coordinator.rx.willNavigate -// .subscribe(onNext: { flow, step in -// let currentFlow = "\(flow)".split(separator: ".").last ?? "no flow" -// print("➡️ will navigate to flow = \(currentFlow) and step = \(step)") -// }) -// .disposed(by: disposeBag) -// -// // didNavigate -// } } From fb940013d42de3a7dcfceab5cacc440c7f637d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 13:18:54 +0900 Subject: [PATCH 4/9] =?UTF-8?q?REFACT::=20[#350]=20introViewModel=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IntroScene/ViewModel/IntroViewModel.swift | 64 ++++++------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift index 6e623408..7eab03aa 100644 --- a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/IntroViewModel.swift @@ -48,45 +48,33 @@ public class IntroViewModel: AuthViewModelType { public func transform(_ input: Input, action: (Output) -> Void) -> Output { - let output = Output(introDatas: introModelSubject.asObservable(), showGoogleAlert: showGoogleAlertSubject.asObservable()) + let output = Output( + introDatas: introModelSubject.asObservable(), + showGoogleAlert: showGoogleAlertSubject.asObservable() + ) action(output) - + bindInputs(input) bindOutput(output: output) - input.goolgeButtonTapped - .asObservable() - .withUnretained(self) - .subscribe(onNext: { owner, _ in - owner.showGoogleAlertSubject.onNext(()) - }) - .disposed(by: disposeBag) - - input.kakaoButtonTapped - .asObservable() - .withUnretained(self) - .subscribe(onNext: { owner, _ in - owner.useCase.kakaoButtonTap() - }) - .disposed(by: disposeBag) - - input.appleButtonTapped - .asObservable() - .withUnretained(self) - .subscribe(onNext: { owner, _ in - owner.useCase.appleButtonTap() - }) - .disposed(by: disposeBag) - - input.getIntroData + return output + } + + private func bindButtonTap(_ driver: Driver, handler: @escaping () -> Void) { + driver .asObservable() .withUnretained(self) .subscribe(onNext: { owner, _ in - owner.useCase.getIntroData() + handler() }) .disposed(by: disposeBag) - - return output + } + + private func bindInputs(_ input: Input) { + bindButtonTap(input.goolgeButtonTapped, handler: { self.showGoogleAlertSubject.onNext(()) }) + bindButtonTap(input.appleButtonTapped, handler: { self.useCase.appleButtonTap() }) + bindButtonTap(input.kakaoButtonTapped, handler: { self.useCase.kakaoButtonTap() }) + bindButtonTap(input.getIntroData, handler: { self.useCase.getIntroData() }) } private func bindOutput(output: Output) { @@ -104,19 +92,3 @@ public class IntroViewModel: AuthViewModelType { .disposed(by: disposeBag) } } - -private extension IntroViewModel { - // func kakaoGetUserInfo() { - // UserApi.shared.me() { (user, error) in - // if let error = error { - // print(error) - // } - // - // let userName = user?.kakaoAccount?.name - // - // _ = "user name : \(String(describing: userName))" - // - // print("user - \(String(describing: user))") - // } - // } -} From fc6dd84ed1c26f43976b48e01c6b2ff176fd97a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 13:19:40 +0900 Subject: [PATCH 5/9] =?UTF-8?q?REFACT::=20[#350]=20AgreeViewController=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SignupScene/VC/AgreeViewController.swift | 341 ++++++++---------- 1 file changed, 153 insertions(+), 188 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift index 254796d3..6080fb4a 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/VC/AgreeViewController.swift @@ -15,22 +15,22 @@ import AuthFeatureInterface import SafariServices public class AgreeViewController: BaseViewController, Stepper, UIGestureRecognizerDelegate { - + private var naviBar = AuthNavigationBarBar() - + private let containerView = BaseView() - + private let agreeLabel = MGLabel( text: "약관동의", font: UIFont.Pretendard.titleLarge ) - + private let textInformation = MGLabel( text: "서비스 이용을 위해 필수 약관동의가 필요해요.", font: UIFont.Pretendard.bodyMedium, textColor: DSKitAsset.Colors.gray600.color ) - + private let decorateLine1 = MGLine() private let allAgreeButton = MGAgreeButton(type: .allAgreeText) private let decorateLine2 = MGLine() @@ -40,80 +40,203 @@ public class AgreeViewController: BaseViewController, Stepper, U private let secondSeemoreButton = AuthSeemoreButton() private let thirdAgreeButton = MGAgreeButton(type: .ageAgreeText) private let fourthAgreeButton = MGAgreeButton(type: .marketingAgreeText, chooseType: true) - + private var checkButton = MGCheckButton(text: "확인").then { $0.isEnabled = false } - + public override func configureNavigationBar() { super.configureNavigationBar() navigationController?.isNavigationBarHidden = true self.view.frame = self.view.frame.inset(by: UIEdgeInsets(top: .zero, left: 0, bottom: .zero, right: 0)) } - + public override func attribute() { super.attribute() navigationController?.interactivePopGestureRecognizer?.delegate = self } - + public override func layout() { + setupUIComponents() + setupConstraints() + } + + public override func bindViewModel() { + super.bindViewModel() + + let navButtonTapped = naviBar.leftButtonTap.asDriver(onErrorDriveWith: .never()) + + let input = AgreeViewModel.Input( + navButtonTapped: navButtonTapped, + allAgreeButtonTap: allAgreeButton.rx.tap.asSignal(), + firstAgreeButtonTap: firstAgreeButton.rx.tap.asSignal(), + firstSeeMoreButtonTap: firstSeemoreButton.rx.tap.asDriver(), + secondAgreeButtonTap: secondAgreeButton.rx.tap.asSignal(), + secondSeeMoreButtonTap: secondSeemoreButton.rx.tap.asSingle(), + thirdAgreeButtonTap: thirdAgreeButton.rx.tap.asSignal(), + fourthAgreeButtonTap: fourthAgreeButton.rx.tap.asSignal(), + nextButtonTap: checkButton.rx.tap.asSignal() + ) + + _ = viewModel.transform(input, action: { output in + let buttonOutputs = [ + output.firstAgreeButtonClickedMessage, + output.secondAgreeButtonClickedMessage, + output.thirdAgreeButtonClickedMessage, + output.fourthAgreeButtonClickedMessage + ] + + Observable.merge(buttonOutputs) + .withUnretained(self) + .subscribe(onNext: { owner, message in + owner.updateAllAgreeButtonState() + _ = owner.buttonActivationChecked(button: owner.checkButton) + MGLogger.verbose(message) + }) + .disposed(by: disposeBag) + + output.allAgreeButtonClickedMessage + .withUnretained(self) + .subscribe(onNext: { owner, message in + owner.setAllAgreeButtonState(!(owner.allAgreeButtonState)) + _ = owner.buttonActivationChecked(button: owner.checkButton) + }) + .disposed(by: disposeBag) + + output.nextButtonClicked + .drive(onNext: { message in + AuthStepper.shared.steps.accept(MGStep.authNickNameIsRequired) + MGLogger.verbose(message) + }) + .disposed(by: disposeBag) + }) + } + + + public override func bindActions() { + super.bindActions() + + firstSeemoreButton.rx.tap.subscribe(onNext: { [weak self] _ in + self?.openURL("https://info-dsm.notion.site/e9d45a0490674b81a419bbc4cbdd5a9d?pvs=4") + }).disposed(by: disposeBag) + + secondSeemoreButton.rx.tap.subscribe(onNext: { [weak self] _ in + self?.openURL("https://info-dsm.notion.site/2a0474e87f754fbe8f53d58f2003ccb2?pvs=4") + }).disposed(by: disposeBag) + } + + private func openURL(_ urlString: String) { + if let url = URL(string: urlString) { + let safariView = SFSafariViewController(url: url) + safariView.modalPresentationStyle = .formSheet + present(safariView, animated: true, completion: nil) + } + } + + public var allAgreeButtonState: Bool { + return [ + firstAgreeButton, + secondAgreeButton, + thirdAgreeButton, + fourthAgreeButton + ].allSatisfy { $0.checked } + } + + public func setAllAgreeButtonState(_ isEnabled: Bool) { + [ + firstAgreeButton, + secondAgreeButton, + thirdAgreeButton, + fourthAgreeButton + ].forEach { + isEnabled ? $0.buttonYesChecked() : $0.buttonNoChecked() + } + updateAllAgreeButtonState() + } + + public func updateAllAgreeButtonState() { + if allAgreeButtonState { + allAgreeButton.buttonYesChecked() + } else { + allAgreeButton.buttonNoChecked() + } + } + + public func buttonActivationChecked(button: MGCheckButton) -> Bool { + let allRequiredButtonsChecked = firstAgreeButton.checked && + secondAgreeButton.checked && + thirdAgreeButton.checked + let shouldActivateButton = allRequiredButtonsChecked && (fourthAgreeButton.checked || !fourthAgreeButton.checked) + + button.isEnabled = shouldActivateButton + button.backgroundColor = shouldActivateButton ? DSKitAsset.Colors.blue500.color : DSKitAsset.Colors.gray400.color + button.textLabel.textColor = shouldActivateButton ? .white : DSKitAsset.Colors.gray200.color + + return shouldActivateButton + } +} + +private extension AgreeViewController { + func setupUIComponents() { view.addSubviews([naviBar, agreeLabel, textInformation, containerView, checkButton]) + containerView.addSubviews([decorateLine1, + allAgreeButton, + decorateLine2, + firstAgreeButton, + secondAgreeButton, + thirdAgreeButton, + fourthAgreeButton]) + firstAgreeButton.addSubview(firstSeemoreButton) + secondAgreeButton.addSubview(secondSeemoreButton) + } + + func setupConstraints() { naviBar.snp.makeConstraints { $0.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) } - + agreeLabel.snp.makeConstraints { $0.top.equalTo(naviBar.snp.bottom).offset(20.0) $0.leading.equalToSuperview().offset(20.0) $0.width.equalTo(125.0) } - + textInformation.snp.makeConstraints { $0.top.equalTo(agreeLabel.snp.bottom).offset(8.0) $0.leading.equalToSuperview().offset(20.0) $0.width.equalTo(287.0) } - + containerView.snp.makeConstraints { $0.top.equalTo(textInformation.snp.bottom).offset(40.0) $0.height.equalTo(284.0) $0.leading.trailing.equalToSuperview().inset(20.0) } - + checkButton.snp.makeConstraints { $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-20.0) $0.leading.equalToSuperview().offset(20.0) $0.trailing.equalToSuperview().offset(-20.0) $0.height.equalTo(58.0) } - - containerView.addSubviews([decorateLine1, - allAgreeButton, - decorateLine2, - firstAgreeButton, - secondAgreeButton, - thirdAgreeButton, - fourthAgreeButton]) - firstAgreeButton.addSubview(firstSeemoreButton) - secondAgreeButton.addSubview(secondSeemoreButton) - + decorateLine1.snp.makeConstraints { $0.top.equalToSuperview() $0.centerX.equalToSuperview() } - + allAgreeButton.snp.makeConstraints { $0.top.equalTo(decorateLine1.snp.bottom).offset(12.0) $0.leading.trailing.equalToSuperview() $0.height.equalTo(44.0) } - + decorateLine2.snp.makeConstraints { $0.top.equalTo(allAgreeButton.snp.bottom).offset(12.0) $0.centerX.equalToSuperview() } - + firstAgreeButton.snp.makeConstraints { $0.top.equalTo(decorateLine2.snp.bottom).offset(8.0) $0.leading.trailing.equalToSuperview() @@ -126,7 +249,7 @@ public class AgreeViewController: BaseViewController, Stepper, U $0.trailing.equalToSuperview() $0.width.equalTo(64.0) } - + secondAgreeButton.snp.makeConstraints { $0.top.equalTo(firstAgreeButton.snp.bottom).offset(8.0) $0.leading.trailing.equalToSuperview() @@ -138,175 +261,17 @@ public class AgreeViewController: BaseViewController, Stepper, U $0.trailing.equalToSuperview() $0.width.equalTo(64.0) } - + thirdAgreeButton.snp.makeConstraints { $0.top.equalTo(secondAgreeButton.snp.bottom).offset(8.0) $0.leading.trailing.equalToSuperview() $0.height.equalTo(44.0) } - + fourthAgreeButton.snp.makeConstraints { $0.top.equalTo(thirdAgreeButton.snp.bottom).offset(8.0) $0.leading.trailing.equalToSuperview() $0.height.equalTo(44.0) } } - - public override func bindViewModel() { - super.bindViewModel() - - let navButtonTapped = naviBar.leftButtonTap.asDriver(onErrorDriveWith: .never()) - - let input = AgreeViewModel.Input( - navButtonTapped: navButtonTapped, - allAgreeButtonTap: allAgreeButton.rx.tap.asSignal(), - firstAgreeButtonTap: firstAgreeButton.rx.tap.asSignal(), - firstSeeMoreButtonTap: firstSeemoreButton.rx.tap.asDriver(), - secondAgreeButtonTap: secondAgreeButton.rx.tap.asSignal(), - secondSeeMoreButtonTap: secondSeemoreButton.rx.tap.asSingle(), - thirdAgreeButtonTap: thirdAgreeButton.rx.tap.asSignal(), - fourthAgreeButtonTap: fourthAgreeButton.rx.tap.asSignal(), - nextButtonTap: checkButton.rx.tap.asSignal() - ) - - _ = viewModel.transform(input, action: { output in - output.allAgreeButtonClickedMessage - .withUnretained(self) - .subscribe(onNext: { owner, message in - owner.setAllAgreeButtonState(!(owner.allAgreeButtonState)) - _ = owner.buttonActivationChecked(button: owner.checkButton) - }) - .disposed(by: disposeBag) - - let agreeButtons = [ - output.firstAgreeButtonClickedMessage, - output.secondAgreeButtonClickedMessage, - output.thirdAgreeButtonClickedMessage, - output.fourthAgreeButtonClickedMessage - ] - - agreeButtons.forEach { buttonOutput in - buttonOutput - .withUnretained(self) - .subscribe(onNext: { owner, message in - owner.updateAllAgreeButtonState() - MGLogger.verbose(message) - - }) - .disposed(by: disposeBag) - } - - output.firstAgreeButtonClickedMessage - .withUnretained(self) - .subscribe(onNext: { owner, message in - owner.updateAllAgreeButtonState() - _ = owner.buttonActivationChecked(button: owner.checkButton) - MGLogger.verbose(message) - - }) - .disposed(by: disposeBag) - - output.secondAgreeButtonClickedMessage - .withUnretained(self) - .subscribe(onNext: { owner, message in - owner.updateAllAgreeButtonState() - _ = owner.buttonActivationChecked(button: owner.checkButton) - MGLogger.verbose(message) - - }) - .disposed(by: disposeBag) - - output.thirdAgreeButtonClickedMessage - .withUnretained(self) - .subscribe(onNext: { owner, message in - owner.updateAllAgreeButtonState() - _ = owner.buttonActivationChecked(button: owner.checkButton) - MGLogger.verbose(message) - - }) - .disposed(by: disposeBag) - - output.fourthAgreeButtonClickedMessage - .withUnretained(self) - .subscribe(onNext: { owner, message in - MGLogger.verbose(message) - - }) - .disposed(by: disposeBag) - - output.nextButtonClicked - .drive(onNext: { message in - AuthStepper.shared.steps.accept(MGStep.authNickNameIsRequired) - MGLogger.verbose(message) - }) - .disposed(by: disposeBag) - }) - } - - public override func bindActions() { - super.bindActions() - - firstSeemoreButton.rx.tap - .subscribe(onNext: { _ in - let url = NSURL(string: "https://info-dsm.notion.site/e9d45a0490674b81a419bbc4cbdd5a9d?pvs=4") - - let safariView: SFSafariViewController = SFSafariViewController(url: url! as URL) - self.present(safariView, animated: true, completion: nil) - }).disposed(by: disposeBag) - - secondSeemoreButton.rx.tap - .subscribe(onNext: { - let url = NSURL(string: "https://info-dsm.notion.site/2a0474e87f754fbe8f53d58f2003ccb2?pvs=4") - - let safariView: SFSafariViewController = SFSafariViewController(url: url! as URL) - self.present(safariView, animated: true, completion: nil) - }).disposed(by: disposeBag) - } - - public var allAgreeButtonState: Bool { - return firstAgreeButton.checked && - secondAgreeButton.checked && - thirdAgreeButton.checked && - fourthAgreeButton.checked - } - - public func setAllAgreeButtonState(_ isEnabled: Bool) { - allAgreeButton.checked = isEnabled - if isEnabled { - firstAgreeButton.buttonYesChecked() - secondAgreeButton.buttonYesChecked() - thirdAgreeButton.buttonYesChecked() - fourthAgreeButton.buttonYesChecked() - updateAllAgreeButtonState() - } else { - firstAgreeButton.buttonNoChecked() - secondAgreeButton.buttonNoChecked() - thirdAgreeButton.buttonNoChecked() - fourthAgreeButton.buttonNoChecked() - updateAllAgreeButtonState() - } - } - - public func updateAllAgreeButtonState() { - if allAgreeButtonState == true { - allAgreeButton.buttonYesChecked() - } else { - allAgreeButton.buttonNoChecked() - } - } - - public func buttonActivationChecked(button: MGCheckButton) -> Bool { - let shouldActivateButton = firstAgreeButton.checked && - secondAgreeButton.checked && - thirdAgreeButton.checked && - !fourthAgreeButton.checked || firstAgreeButton.checked && - secondAgreeButton.checked && - thirdAgreeButton.checked && - fourthAgreeButton.checked - button.isEnabled = shouldActivateButton - button.backgroundColor = shouldActivateButton ? DSKitAsset.Colors.blue500.color : DSKitAsset.Colors.gray400.color - button.textLabel.textColor = shouldActivateButton ? .white : DSKitAsset.Colors.gray200.color - - return shouldActivateButton - } } From 8170e5c706f36f34a24a8cdf2b305555bd32bd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 13:26:09 +0900 Subject: [PATCH 6/9] =?UTF-8?q?DEL::=20[#350]=20splashViewModel=20?= =?UTF-8?q?=EC=95=88=EC=93=B0=EB=8A=94=20=EA=B1=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/IntroScene/ViewModel/SplashViewModel.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/SplashViewModel.swift b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/SplashViewModel.swift index 7683a421..7500015e 100644 --- a/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/SplashViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/IntroScene/ViewModel/SplashViewModel.swift @@ -30,8 +30,6 @@ public class SplashViewModel: AuthViewModelType { self.useCase = authUseCase self.disposeBag = DisposeBag() useCase.tokenReIssue() -// useCase.getAllPoseData() - } public func transform(_ input: Input, action: (Output) -> Void) -> Output { From c4f42694ee55ec1efe5e66198b28384cf7510ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 13:26:50 +0900 Subject: [PATCH 7/9] =?UTF-8?q?DEL::=20[#350]=20AuthSeemoreButton=20?= =?UTF-8?q?=EC=95=88=EC=93=B0=EB=8A=94=EA=B1=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/SignupScene/Supporter/AuthSeemoreButton.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/Supporter/AuthSeemoreButton.swift b/Projects/Features/AuthFeature/Sources/SignupScene/Supporter/AuthSeemoreButton.swift index 466451e1..7dd43ead 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/Supporter/AuthSeemoreButton.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/Supporter/AuthSeemoreButton.swift @@ -8,9 +8,7 @@ import Core import DSKit public class AuthSeemoreButton: BaseButton { - -// private var containerView = UIView() - + private var textLabel = MGLabel(text: "자세히 보기", font: UIFont.Pretendard.labelSmall, textColor: DSKitAsset.Colors.gray300.color, isCenter: true) private var lineView = MGLine(lineColor: DSKitAsset.Colors.gray300.color, lineWidth: 1.0) From 608ae41b53d1f34fd77b7d6630c439ddc66517ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 13:54:36 +0900 Subject: [PATCH 8/9] =?UTF-8?q?REFACT::=20[#350]=20Nickname=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VC/NicknameViewController.swift | 224 +++++++++--------- 1 file changed, 107 insertions(+), 117 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift b/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift index 1415a07b..62a3e721 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/VC/NicknameViewController.swift @@ -1,107 +1,97 @@ import UIKit - import RxSwift import RxCocoa import RxFlow - import SnapKit import Then - import Core import DSKit - import Domain import MGLogger - import AuthFeatureInterface public class NicknameViewController: BaseViewController, Stepper { private lazy var naviBar = AuthNavigationBarBar() - - private var bottomConstraint: Constraint? - - private let nicknameTitle = MGLabel(text: "닉네임", - font: UIFont.Pretendard.titleLarge, - isCenter: false) - - private let textInformation = MGLabel(text: "자신만의 닉네임을 입력해 주세요.", - font: UIFont.Pretendard.bodyMedium, - textColor: DSKitAsset.Colors.gray600.color, - isCenter: false) - + private let nicknameTitle = MGLabel(text: "닉네임", font: UIFont.Pretendard.titleLarge, isCenter: false) + private let textInformation = MGLabel(text: "자신만의 닉네임을 입력해 주세요.", font: UIFont.Pretendard.bodyMedium, textColor: DSKitAsset.Colors.gray600.color, isCenter: false) private let nicknameTF = MGTextField(placeholder: "닉네임").then { $0.clearButtonMode = .whileEditing } - public var nextButton = MGCheckButton(text: "회원가입").then { $0.isEnabled = true } - + + private var bottomConstraint: Constraint? + public override func configureNavigationBar() { super.configureNavigationBar() navigationController?.isNavigationBarHidden = true - self.view.frame = self.view.frame.inset(by: UIEdgeInsets(top: .zero, left: 0, bottom: .zero, right: 0)) + view.frame = view.frame.inset(by: UIEdgeInsets(top: .zero, left: 0, bottom: .zero, right: 0)) } public override func attribute() { super.attribute() - keyboardBind() + keyboard() + } - NotificationCenter.default.addObserver(self, - selector: #selector(keyboardWillShow), - name: UIResponder.keyboardWillShowNotification, - object: nil) - NotificationCenter.default.addObserver(self, - selector: #selector(keyboardWillHide), - name: UIResponder.keyboardWillHideNotification, - object: nil) + public override func layout() { + super.layout() + setupLayout() + setupConstraints() + } - let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) - view.addGestureRecognizer(tap) + public override func bindViewModel() { + setupViewModelBindings() } - public override func layout() { - view.addSubviews([naviBar, - nicknameTitle, - textInformation, - nicknameTF, - nextButton]) + public override func bindActions() { + setupTextFieldBindings() + } +} +// MARK: - UI Setup +private extension NicknameViewController { + func setupLayout() { + view.addSubviews([naviBar, nicknameTitle, textInformation, nicknameTF, nextButton]) + } + + func setupConstraints() { naviBar.snp.makeConstraints { $0.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) } nicknameTitle.snp.makeConstraints { - $0.top.equalTo(naviBar.snp.bottom).offset(20.0) - $0.leading.equalToSuperview().offset(20.0) - $0.width.equalTo(165.0) + $0.top.equalTo(naviBar.snp.bottom).offset(20) + $0.leading.equalToSuperview().offset(20) + $0.width.equalTo(165) } textInformation.snp.makeConstraints { - $0.top.equalTo(nicknameTitle.snp.bottom).offset(8.0) - $0.leading.equalToSuperview().offset(20.0) - $0.width.equalTo(287.0) + $0.top.equalTo(nicknameTitle.snp.bottom).offset(8) + $0.leading.equalToSuperview().offset(20) + $0.width.equalTo(287) } nicknameTF.snp.makeConstraints { - $0.leading.equalToSuperview().offset(20.0) - $0.top.equalTo(textInformation.snp.bottom).offset(50.0) - $0.height.equalTo(40.0) - $0.width.equalTo(390.0) + $0.leading.equalToSuperview().offset(20) + $0.top.equalTo(textInformation.snp.bottom).offset(50) + $0.height.equalTo(40) + $0.width.equalTo(390) } nextButton.snp.makeConstraints { - $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-20.0) - $0.leading.equalToSuperview().offset(20.0) - $0.trailing.equalToSuperview().offset(-20.0) - $0.height.equalTo(58.0) + $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-20) + $0.leading.trailing.equalToSuperview().inset(20) + $0.height.equalTo(58) } } +} - public override func bindViewModel() { - +// MARK: - ViewModel Binding +private extension NicknameViewController { + func setupViewModelBindings() { let navButtonTapped = naviBar.leftButtonTap.asDriver(onErrorDriveWith: .never()) let nextButtonTapped = nextButton.rx.tap .withLatestFrom(nicknameTF.rx.text) @@ -113,7 +103,7 @@ public class NicknameViewController: BaseViewController, Step output.navButtonTap.drive(onNext: { _ in AuthStepper.shared.steps.accept(MGStep.authBack) }).disposed(by: disposeBag) - + output.nicknameState .withUnretained(self) .subscribe(onNext: { owner, state in @@ -122,8 +112,11 @@ public class NicknameViewController: BaseViewController, Step }).disposed(by: disposeBag) }) } - - public override func bindActions() { +} + +// MARK: - TextField Binding +private extension NicknameViewController { + func setupTextFieldBindings() { nicknameTF.rx.controlEvent([.editingDidBegin]) .asObservable() .withUnretained(self) @@ -133,76 +126,40 @@ public class NicknameViewController: BaseViewController, Step .disposed(by: disposeBag) } - private func nicknameTFTapped() { - nicknameTF.rx.text - .map { ($0?.count ?? 0) >= 2 && ($0?.count ?? 0) <= 10 ? true : false } - .subscribe( - onNext: { [weak self] state in - MGLogger.debug(state) - switch state { - case true: - self?.nextButton.isEnabled = state - self?.nicknameTF.showError = false - self?.nextButton.backgroundColor = DSKitAsset.Colors.blue500.color - self?.nextButton.changeTextColor(textColor: .white) - case false: - self?.nicknameTF.errorMessage = "닉네임은 2~10자로 공백을 포함할 수 없어요." - self?.nicknameTF.showError = true - self?.nextButton.isEnabled = state - self?.nextButton.backgroundColor = DSKitAsset.Colors.gray400.color - self?.nextButton.changeTextColor(textColor: DSKitAsset.Colors.gray200.color) - } - }).disposed(by: disposeBag) - - nicknameTF.rx.text.orEmpty - .map { $0.replacingOccurrences(of: " ", with: "") } - .bind(to: nicknameTF.rx.text) - .disposed(by: disposeBag) + func nicknameTFTapped() { + nicknameTF.rx.text.map { text in + let length = text?.count ?? 0 + return length >= 2 && length <= 10 + }.subscribe(onNext: { [weak self] isValid in + self?.configureNicknameField(isValid: isValid) + }).disposed(by: disposeBag) } - func animateButtonWithKeyboard(notification: NSNotification, show: Bool) { - guard let keyboardSize = (notification.userInfo?[ - UIResponder.keyboardFrameEndUserInfoKey - ] as? NSValue)?.cgRectValue, - let keyboardAnimationDuration = notification.userInfo?[ - UIResponder.keyboardAnimationDurationUserInfoKey - ] as? TimeInterval else { - return + func configureNicknameField(isValid: Bool) { + if isValid { + nextButton.isEnabled = true + nicknameTF.showError = false + nextButton.backgroundColor = DSKitAsset.Colors.blue500.color + nextButton.changeTextColor(textColor: .white) + } else { + nicknameTF.errorMessage = "닉네임은 2~10자로 공백을 포함할 수 없어요." + nicknameTF.showError = true + nextButton.isEnabled = false + nextButton.backgroundColor = DSKitAsset.Colors.gray400.color + nextButton.changeTextColor(textColor: DSKitAsset.Colors.gray200.color) } - - let offset = show ? -keyboardSize.height : -20.0 - bottomConstraint?.update(offset: offset) - - UIView.animate(withDuration: keyboardAnimationDuration) { [weak self] in - self?.view.layoutIfNeeded() - } - } - - @objc func keyboardWillShow(notification: NSNotification) { - animateButtonWithKeyboard(notification: notification, show: true) - } - - @objc func keyboardWillHide(notification: NSNotification) { - animateButtonWithKeyboard(notification: notification, show: false) } +} - @objc func dismissKeyboard() { - view.endEditing(true) - } - - private func keyboardBind() { - let keyboardWillShowObservable = NotificationCenter.default.rx.notification( - UIResponder.keyboardWillShowNotification) - .map { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height ?? 48 } - - let keyboardWillHideObservable = NotificationCenter.default.rx.notification( - UIResponder.keyboardWillHideNotification) - .map { _ in CGFloat(48) } +// MARK: - Keyboard Handling +private extension NicknameViewController { + func keyboardBind() { + let keyboardWillShowObservable = NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification).map { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height ?? 48 } + let keyboardWillHideObservable = NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification).map { _ in CGFloat(48) } Observable.merge(keyboardWillShowObservable) .withUnretained(self) .subscribe(onNext: { owner, height in - owner.nextButton.snp.remakeConstraints { owner.bottomConstraint = $0.bottom.equalToSuperview().offset(-height).constraint $0.width.equalToSuperview() @@ -233,4 +190,37 @@ public class NicknameViewController: BaseViewController, Step }) .disposed(by: disposeBag) } + + func keyboard() { + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) + + let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) + view.addGestureRecognizer(tap) + } + + @objc func keyboardWillShow(notification: NSNotification) { + animateButtonWithKeyboard(notification: notification, show: true) + } + + @objc func keyboardWillHide(notification: NSNotification) { + animateButtonWithKeyboard(notification: notification, show: false) + } + + @objc func dismissKeyboard() { + view.endEditing(true) + } + + func animateButtonWithKeyboard(notification: NSNotification, show: Bool) { + guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let keyboardAnimationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { + return + } + + let offset = show ? -keyboardSize.height : -20.0 + bottomConstraint?.update(offset: offset) + + UIView.animate(withDuration: keyboardAnimationDuration) { [weak self] in + self?.view.layoutIfNeeded() + } + } } From 7a909c69ee3820945c2b0fdaaa9bada85355f442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=A4=80=ED=95=98?= Date: Wed, 26 Jun 2024 14:09:36 +0900 Subject: [PATCH 9/9] =?UTF-8?q?REFACT::=20[#350]=20AgreeViewModel=20?= =?UTF-8?q?=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/AgreeViewModel.swift | 126 ++++++++++++++---- 1 file changed, 102 insertions(+), 24 deletions(-) diff --git a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift index de8f1e21..16182fd3 100644 --- a/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift +++ b/Projects/Features/AuthFeature/Sources/SignupScene/ViewModel/AgreeViewModel.swift @@ -1,5 +1,83 @@ -import Foundation +//import Foundation +// +//import RxSwift +//import RxCocoa +//import RxFlow +// +//import Core +//import Domain +//import SafariServices +// +//public class AgreeViewModel: BaseViewModel { +// +// public typealias ViewModel = AgreeViewModel +// +// private let useCase: AuthUseCase +// +// public var disposeBag: DisposeBag = DisposeBag() +// +// public struct Input { +// let navButtonTapped: Driver +// let allAgreeButtonTap: Signal +// let firstAgreeButtonTap: Signal +// let firstSeeMoreButtonTap: Driver +// let secondAgreeButtonTap: Signal +// let secondSeeMoreButtonTap: Single +// let thirdAgreeButtonTap: Signal +// let fourthAgreeButtonTap: Signal +// let nextButtonTap: Signal +// } +// +// public struct Output { +// let navButtonTapped: Driver +// let allAgreeButtonClickedMessage: Observable +// let firstAgreeButtonClickedMessage: Observable +// let secondAgreeButtonClickedMessage: Observable +// let thirdAgreeButtonClickedMessage: Observable +// let fourthAgreeButtonClickedMessage: Observable +// let nextButtonClicked: Driver +// } +// +// public init(useCase: AuthUseCase) { +// self.useCase = useCase +// } +// +// public func transform(_ input: Input, action: (Output) -> Void) -> Output { +// +// let allAgreeClickedMessage = input.allAgreeButtonTap.map { "전체 클릭" }.asObservable() +// let firstAgreeClickedMessage = input.firstAgreeButtonTap.map { "첫 번째 동의 클릭" }.asObservable() +// let secondAgreeClickedMessage = input.secondAgreeButtonTap.map { "두 번째 동의 클릭" }.asObservable() +// let thirdAgreeClickedMessage = input.thirdAgreeButtonTap.map { "세 번째 동의 클릭" }.asObservable() +// let fourthAgreeClickedMessage = input.fourthAgreeButtonTap.map { "네 번째 동의 클릭" }.asObservable() +// let nextButtonClicked = input.nextButtonTap.map { true }.asDriver(onErrorJustReturn: false) +// +// let output = Output(navButtonTapped: input.navButtonTapped.asDriver(), +// allAgreeButtonClickedMessage: allAgreeClickedMessage, +// firstAgreeButtonClickedMessage: firstAgreeClickedMessage, +// secondAgreeButtonClickedMessage: secondAgreeClickedMessage, +// thirdAgreeButtonClickedMessage: thirdAgreeClickedMessage, +// fourthAgreeButtonClickedMessage: fourthAgreeClickedMessage, +// nextButtonClicked: nextButtonClicked) +// +// action(output) +// +// input.navButtonTapped +// .drive(onNext: { _ in +// AuthStepper.shared.steps.accept(MGStep.authBack) +// }).disposed(by: disposeBag) +// +// input.firstSeeMoreButtonTap +// .drive(onNext: { _ in +// print("sex") +// +// }).disposed(by: disposeBag) +// +// +// return output +// } +//} +import Foundation import RxSwift import RxCocoa import RxFlow @@ -13,8 +91,7 @@ public class AgreeViewModel: BaseViewModel { public typealias ViewModel = AgreeViewModel private let useCase: AuthUseCase - - public var disposeBag: DisposeBag = DisposeBag() + private let disposeBag = DisposeBag() public struct Input { let navButtonTapped: Driver @@ -43,36 +120,37 @@ public class AgreeViewModel: BaseViewModel { } public func transform(_ input: Input, action: (Output) -> Void) -> Output { + let output = createOutput(from: input) + action(output) + handleSideEffects(input) + return output + } - let allAgreeClickedMessage = input.allAgreeButtonTap.map { "전체 클릭" }.asObservable() - let firstAgreeClickedMessage = input.firstAgreeButtonTap.map { "첫 번째 동의 클릭" }.asObservable() - let secondAgreeClickedMessage = input.secondAgreeButtonTap.map { "두 번째 동의 클릭" }.asObservable() - let thirdAgreeClickedMessage = input.thirdAgreeButtonTap.map { "세 번째 동의 클릭" }.asObservable() - let fourthAgreeClickedMessage = input.fourthAgreeButtonTap.map { "네 번째 동의 클릭" }.asObservable() - let nextButtonClicked = input.nextButtonTap.map { true }.asDriver(onErrorJustReturn: false) - - let output = Output(navButtonTapped: input.navButtonTapped.asDriver(), - allAgreeButtonClickedMessage: allAgreeClickedMessage, - firstAgreeButtonClickedMessage: firstAgreeClickedMessage, - secondAgreeButtonClickedMessage: secondAgreeClickedMessage, - thirdAgreeButtonClickedMessage: thirdAgreeClickedMessage, - fourthAgreeButtonClickedMessage: fourthAgreeClickedMessage, - nextButtonClicked: nextButtonClicked) + private func createOutput(from input: Input) -> Output { + Output( + navButtonTapped: input.navButtonTapped.asDriver(), + allAgreeButtonClickedMessage: createMessageObservable(for: input.allAgreeButtonTap, message: "전체 클릭"), + firstAgreeButtonClickedMessage: createMessageObservable(for: input.firstAgreeButtonTap, message: "첫 번째 동의 클릭"), + secondAgreeButtonClickedMessage: createMessageObservable(for: input.secondAgreeButtonTap, message: "두 번째 동의 클릭"), + thirdAgreeButtonClickedMessage: createMessageObservable(for: input.thirdAgreeButtonTap, message: "세 번째 동의 클릭"), + fourthAgreeButtonClickedMessage: createMessageObservable(for: input.fourthAgreeButtonTap, message: "네 번째 동의 클릭"), + nextButtonClicked: input.nextButtonTap.map { true }.asDriver(onErrorJustReturn: false) + ) + } - action(output) + private func createMessageObservable(for signal: Signal, message: String) -> Observable { + signal.map { message }.asObservable() + } + private func handleSideEffects(_ input: Input) { input.navButtonTapped .drive(onNext: { _ in AuthStepper.shared.steps.accept(MGStep.authBack) }).disposed(by: disposeBag) - + input.firstSeeMoreButtonTap .drive(onNext: { _ in - print("sex") - + print("클릭") }).disposed(by: disposeBag) - - - return output } }