From c2581b0a95c71475d0033227a6c1fd20dbab7542 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 14:45:24 +0900 Subject: [PATCH 01/24] =?UTF-8?q?[IDLE-000]=20=EC=84=A4=EC=A0=95=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=A0=20FullRowButto?= =?UTF-8?q?n=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommonUI/Button/FullRowButton.swift | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/Button/FullRowButton.swift diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/FullRowButton.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/FullRowButton.swift new file mode 100644 index 00000000..f1005010 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/FullRowButton.swift @@ -0,0 +1,81 @@ +// +// FullRowButton.swift +// DSKit +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import RxSwift +import RxCocoa + +public class FullRowButton: TappableUIView { + + let label: IdleLabel = { + let label = IdleLabel(typography: .Body2) + label.attrTextColor = DSColor.gray500.color + return label + }() + + let disposeBag = DisposeBag() + + public init(labelText: String) { + self.label.textString = labelText + super.init() + setApearance() + setLayout() + setObservable() + } + required init?(coder: NSCoder) { return nil } + + private func setApearance() { + self.backgroundColor = DSColor.gray0.color + } + private func setLayout() { + + self.layoutMargins = .init( + top: 12, + left: 20, + bottom: 12, + right: 20 + ) + + let chevronImage = DSIcon.chevronRight.image.toView() + chevronImage.tintColor = DSColor.gray200.color + let mainStack = HStack([ + label, + Spacer(), + chevronImage + ], alignment: .center, distribution: .fill) + + [ + mainStack + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + + NSLayoutConstraint.activate([ + mainStack.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor), + mainStack.leftAnchor.constraint(equalTo: self.layoutMarginsGuide.leftAnchor), + mainStack.rightAnchor.constraint(equalTo: self.layoutMarginsGuide.rightAnchor), + mainStack.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), + ]) + } + private func setObservable() { + self.rx.tap + .subscribe(onNext: { [weak self] _ in + self?.backgroundColor = DSColor.gray050.color + UIView.animate(withDuration: 0.35) { + self?.backgroundColor = DSColor.gray0.color + } + }) + .disposed(by: disposeBag) + } +} + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + FullRowButton(labelText: "내 프로필") +} From b2af266bea9886071b3173869644d8f24ebc492e Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 14:56:26 +0900 Subject: [PATCH 02/24] =?UTF-8?q?[IDLE-000]=20IdleUnderLineLabelButton?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 언더라인이 그어진 라벨로 구성된 버튼입니다. --- .../Button/IdleUnderLineLabelButton.swift | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/Button/IdleUnderLineLabelButton.swift diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/IdleUnderLineLabelButton.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/IdleUnderLineLabelButton.swift new file mode 100644 index 00000000..ac7142fe --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Button/IdleUnderLineLabelButton.swift @@ -0,0 +1,67 @@ +// +// IdleUnderLineLabelButton.swift +// DSKit +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import RxSwift +import RxCocoa + +public class IdleUnderLineLabelButton: TappableUIView { + + let label: IdleLabel = { + let label = IdleLabel(typography: .Body2) + label.attrTextColor = DSColor.gray300.color + label.setAttr(attr: .underlineStyle, value: NSUnderlineStyle.single.rawValue) + return label + }() + + let disposeBag = DisposeBag() + + public init(labelText: String) { + self.label.textString = labelText + super.init() + setApearance() + setLayout() + setObservable() + } + required init?(coder: NSCoder) { return nil } + + private func setApearance() { + self.backgroundColor = DSColor.gray0.color + } + private func setLayout() { + + [ + label + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: self.topAnchor), + label.leftAnchor.constraint(equalTo: self.leftAnchor), + label.rightAnchor.constraint(equalTo: self.rightAnchor), + label.bottomAnchor.constraint(equalTo: self.bottomAnchor), + ]) + } + private func setObservable() { + self.rx.tap + .subscribe(onNext: { [weak self] _ in + self?.label.alpha = 0.5 + UIView.animate(withDuration: 0.35) { + self?.label.alpha = 1.0 + } + }) + .disposed(by: disposeBag) + } +} + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + IdleUnderLineLabelButton(labelText: "로그아웃") +} From 8bbb9cca37a7ef339f934ad72a69e2bdb4360788 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 15:46:40 +0900 Subject: [PATCH 03/24] =?UTF-8?q?[IDLE-000]=20=EC=84=BC=ED=84=B0=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=99=94=EB=A9=B4=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Profile/Center/CenterInfoCardView.swift | 10 +- .../CommonUI/Navigation/IdleTitleBar.swift | 79 +++++++++ .../ExampleApp/Sources/SceneDelegate.swift | 2 +- .../View/Setting/WorkerSettingVC.swift | 165 ++++++++++++++++++ 4 files changed, 252 insertions(+), 4 deletions(-) create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/Navigation/IdleTitleBar.swift create mode 100644 project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Card/Profile/Center/CenterInfoCardView.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Card/Profile/Center/CenterInfoCardView.swift index 1f6422f5..e1186f0b 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Card/Profile/Center/CenterInfoCardView.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Card/Profile/Center/CenterInfoCardView.swift @@ -45,10 +45,17 @@ public class CenterInfoCardView: TappableUIView { } + public let chevronLeftImage: UIImageView = { + let view = DSKitAsset.Icons.chevronRight.image.toView() + view.tintColor = DSKitAsset.Colors.gray200.color + return view + }() + private func setLayout() { let locationImageView = DSKitAsset.Icons.location.image.toView() locationImageView.tintColor = DSColor.gray400.color + let locationStack = HStack( [ locationImageView, @@ -59,9 +66,6 @@ public class CenterInfoCardView: TappableUIView { let labelStack = VStack([nameLabel,locationStack], spacing: 4, alignment: .leading) - let chevronLeftImage = DSKitAsset.Icons.chevronRight.image.toView() - chevronLeftImage.tintColor = DSKitAsset.Colors.gray200.color - NSLayoutConstraint.activate([ locationImageView.widthAnchor.constraint(equalToConstant: 20), locationImageView.heightAnchor.constraint(equalTo: locationImageView.widthAnchor), diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Navigation/IdleTitleBar.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Navigation/IdleTitleBar.swift new file mode 100644 index 00000000..207a17d0 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Navigation/IdleTitleBar.swift @@ -0,0 +1,79 @@ +// +// IdleTitleBar.swift +// DSKit +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import RxSwift +import RxCocoa + +public class IdleTitleBar: UIView { + + // Init parameters + + // View + public lazy var titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Heading1) + label.textAlignment = .left + return label + }() + + private let disposeBag = DisposeBag() + + public init( + titleText: String = "", + innerViews: [UIView] + ) { + super.init(frame: .zero) + + self.titleLabel.textString = titleText + + setApearance() + setAutoLayout(innerViews: innerViews) + } + + public required init(coder: NSCoder) { fatalError() } + + func setApearance() { + + } + + private func setAutoLayout(innerViews: [UIView]) { + + self.layoutMargins = .init( + top: 20.43, + left: 20, + bottom: 12, + right: 20 + ) + + let mainStack = HStack( + [ + [ + titleLabel, + Spacer(), + ], + innerViews + ].flatMap { $0 }, + alignment: .center, + distribution: .fill + ) + + [ + mainStack + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + + NSLayoutConstraint.activate([ + mainStack.leftAnchor.constraint(equalTo: self.layoutMarginsGuide.leftAnchor), + mainStack.rightAnchor.constraint(equalTo: self.layoutMarginsGuide.rightAnchor), + mainStack.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor), + mainStack.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), + ]) + + } +} diff --git a/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift index a4ea3d36..f0624b79 100644 --- a/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift @@ -47,7 +47,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // ) // ) // - let vc = CenterRecruitmentPostBoardVC() + let vc = WorkerSettingVC() // vc.bind(viewModel: vm) diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift new file mode 100644 index 00000000..b41a4e89 --- /dev/null +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift @@ -0,0 +1,165 @@ +// +// WorkerSettingVC.swift +// CenterFeature +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import BaseFeature +import PresentationCore +import RxCocoa +import RxSwift +import Entity +import DSKit + +public class WorkerSettingVC: BaseViewController { + + // Init + + // View + let titleBar: IdleTitleBar = { + let bar = IdleTitleBar(titleText: "설정", innerViews: []) + return bar + }() + + let myCenterInfoButton: FullRowButton = { + let button = FullRowButton(labelText: "내 센터 정보") + return button + }() + let centerInfoCard: CenterInfoCardView = { + let view = CenterInfoCardView() + view.chevronLeftImage.isHidden = true + return view + }() + let frequentQuestionButton: FullRowButton = { + let button = FullRowButton(labelText: "자주 묻는 질문") + return button + }() + let askButton: FullRowButton = { + let button = FullRowButton(labelText: "문의하기") + return button + }() + let applicationPolicyButton: FullRowButton = { + let button = FullRowButton(labelText: "약관및 정책") + return button + }() + let personalDataProcessingPolicyButton: FullRowButton = { + let button = FullRowButton(labelText: "개인정보 처리방침") + return button + }() + let signOutButton: IdleUnderLineLabelButton = { + let button = IdleUnderLineLabelButton(labelText: "로그아웃") + button.backgroundColor = .clear + return button + }() + let removeAccountButton: IdleUnderLineLabelButton = { + let button = IdleUnderLineLabelButton(labelText: "회원 탈퇴") + button.backgroundColor = .clear + return button + }() + + + // Observable + private let disposeBag = DisposeBag() + + public init() { + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder: NSCoder) { fatalError() } + + public override func viewDidLoad() { + super.viewDidLoad() + setAppearance() + setLayout() + setObservable() + } + + private func setAppearance() { + view.backgroundColor = DSColor.gray0.color + } + + private func setLayout() { + + let scrollView = UIScrollView() + scrollView.backgroundColor = DSColor.gray050.color + + let contentGuide = scrollView.contentLayoutGuide + let frameGuide = scrollView.frameLayoutGuide + + let myCenterInfoContainer = VStack([ + myCenterInfoButton, + HStack([ + Spacer(width: 20), + centerInfoCard, + Spacer(width: 20), + ]), + Spacer(height: 12), + ], alignment: .fill) + + myCenterInfoContainer.backgroundColor = DSColor.gray0.color + + let viewList = [ + myCenterInfoContainer, + Spacer(height: 8), + frequentQuestionButton, + askButton, + Spacer(height: 8), + applicationPolicyButton, + Spacer(height: 8), + personalDataProcessingPolicyButton, + Spacer(height: 20), + HStack([Spacer(width: 20), signOutButton, Spacer()]), + Spacer(height: 24), + HStack([Spacer(width: 20), removeAccountButton, Spacer()]), + ] + + let contentView = VStack(viewList, alignment: .fill) + + scrollView.addSubview(contentView) + contentView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + + contentView.topAnchor.constraint(equalTo: contentGuide.topAnchor), + contentView.leftAnchor.constraint(equalTo: contentGuide.leftAnchor), + contentView.rightAnchor.constraint(equalTo: contentGuide.rightAnchor), + contentView.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor), + + contentView.widthAnchor.constraint(equalTo: frameGuide.widthAnchor), + ]) + + + // main view + [ + titleBar, + scrollView + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + } + + NSLayoutConstraint.activate([ + titleBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + titleBar.leftAnchor.constraint(equalTo: view.leftAnchor), + titleBar.rightAnchor.constraint(equalTo: view.rightAnchor), + + scrollView.topAnchor.constraint(equalTo: titleBar.bottomAnchor), + scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + scrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + } + + private func setObservable() { + + } +} + + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + WorkerSettingVC() +} From 600771ae2bec3a5cb8fcf091166ccf968ca7d6ec Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 16:12:39 +0900 Subject: [PATCH 04/24] =?UTF-8?q?[IDLE-000]=20=EC=84=A4=EC=A0=95=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=A0=20=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=EC=88=98=EC=8B=A0=EB=8F=99=EC=9D=98=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20UI=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PushNotificationAuthRow.swift | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift new file mode 100644 index 00000000..b81823f7 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift @@ -0,0 +1,85 @@ +// +// PushNotificationAuthRow.swift +// DSKit +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import RxSwift +import RxCocoa + +public class PushNotificationAuthRow: UIView { + + let label: IdleLabel = { + let label = IdleLabel(typography: .Body2) + label.attrTextColor = DSColor.gray500.color + label.textString = "알림 설정" + return label + }() + + public let `switch`: UISwitch = { + let view = UISwitch() + view.onTintColor = DSColor.orange500.color + view.isOn = false + return view + }() + + public init() { + super.init(frame: .zero) + setApearance() + setLayout() + setObservable() + } + required init?(coder: NSCoder) { return nil } + + private func setApearance() { + self.backgroundColor = DSColor.gray0.color + } + private func setLayout() { + + self.layoutMargins = .init( + top: 12, + left: 20, + bottom: 12, + right: 20 + ) + + let mainStack = HStack([ + label, + Spacer(), + `switch` + ], alignment: .center, distribution: .fill) + + [ + mainStack + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + + NSLayoutConstraint.activate([ + mainStack.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor), + mainStack.leftAnchor.constraint(equalTo: self.layoutMarginsGuide.leftAnchor), + mainStack.rightAnchor.constraint(equalTo: self.layoutMarginsGuide.rightAnchor), + mainStack.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), + ]) + } + private func setObservable() { + + } + + public override func draw(_ rect: CGRect) { + if `switch`.bounds.height > label.bounds.height { + let per = label.bounds.height / `switch`.bounds.height + `switch`.transform = `switch`.transform.scaledBy(x: per, y: per) + } + } + +} + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + PushNotificationAuthRow() +} From 0da3c005a172aed37ec6855da9ddf30386ece970 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 21:33:48 +0900 Subject: [PATCH 05/24] =?UTF-8?q?[IDLE-000]=20Feat,=20CenterSettingVM?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 오탈자 수정: WorkerSetting -> CenterSetting --- .../Setting/DefaultSettingUseCase.swift | 75 +++++++++ .../Setting/SettingScreenUseCase .swift | 24 +++ ...rSettingVC.swift => CenterSettingVC.swift} | 98 ++++++++++- .../ViewModel/Setting/CenterSettingVM.swift | 154 ++++++++++++++++++ .../{SettingVC.swift => TestSettingVC.swift} | 0 5 files changed, 348 insertions(+), 3 deletions(-) create mode 100644 project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift create mode 100644 project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift rename project/Projects/Presentation/Feature/Center/Sources/View/Setting/{WorkerSettingVC.swift => CenterSettingVC.swift} (62%) create mode 100644 project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift rename project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/{SettingVC.swift => TestSettingVC.swift} (100%) diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift new file mode 100644 index 00000000..5b48a9de --- /dev/null +++ b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift @@ -0,0 +1,75 @@ +// +// DefaultSettingUseCase.swift +// UseCaseInterface +// +// Created by choijunios on 8/19/24. +// + +import Foundation +import RxSwift +import UserNotifications + +public enum NotificationApproveAction: Equatable { + case openSystemSetting + case granted + case error(message: String) +} + +public class DefaultSettingUseCase: SettingScreenUseCase { + + public func checkPushNotificationApproved() -> Single { + Single.create { single in + let center = UNUserNotificationCenter.current() + center.getNotificationSettings { settings in + switch settings.authorizationStatus { + case .notDetermined, .denied: + single(.success(true)) + case .authorized, .provisional, .ephemeral: + single(.success(false)) + @unknown default: + single(.success(false)) + } + } + } + } + + public func requestNotificationPermission() -> Maybe { + Maybe.create { maybe in + + let current = UNUserNotificationCenter.current() + + current.getNotificationSettings { [maybe] settings in + switch settings.authorizationStatus { + case .notDetermined: + // Request permission since the user hasn't decided yet. + current.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in + if let error = error { + maybe(.success(.error(message: "알람동의를 수행할 수 없습니다."))) + } else { + maybe(.success(.granted)) + } + } + case .denied: + // 사용자가 요청을 거부했던 상태로 설정앱을 엽니다. + maybe(.success(.openSystemSetting)) + return + case .authorized, .provisional, .ephemeral: + maybe(.success(.granted)) + default: + maybe(.completed) + break + } + } + + return Disposables.create { } + } + } + + public func getPersonalDataUsageDescriptionUrl() -> URL { + <#code#> + } + + public func getApplicationPolicyUrl() -> URL { + <#code#> + } +} diff --git a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift new file mode 100644 index 00000000..9b2b9bd9 --- /dev/null +++ b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift @@ -0,0 +1,24 @@ +// +// SettingScreenUseCase .swift +// UseCaseInterface +// +// Created by choijunios on 8/19/24. +// + +import Foundation +import RxSwift + +public protocol SettingScreenUseCase { + + /// 현재 알람수신 동의 여부를 확인합니다. + func checkPushNotificationApproved() -> Single + + /// 알림동의를 요청합니다. + func requestNotificationPermission() -> Maybe + + /// 개인정보 처리방침 웹 URL을 가져옵니다. + func getPersonalDataUsageDescriptionUrl() -> URL + + /// 어플리케이션 이용약관을 가져옵니다. + func getApplicationPolicyUrl() -> URL +} diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift similarity index 62% rename from project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift rename to project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift index b41a4e89..aa93c39c 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/WorkerSettingVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift @@ -1,5 +1,5 @@ // -// WorkerSettingVC.swift +// CenterSettingVC.swift // CenterFeature // // Created by choijunios on 8/19/24. @@ -13,7 +13,7 @@ import RxSwift import Entity import DSKit -public class WorkerSettingVC: BaseViewController { +public class CenterSettingVC: BaseViewController { // Init @@ -32,6 +32,10 @@ public class WorkerSettingVC: BaseViewController { view.chevronLeftImage.isHidden = true return view }() + let pushNotificationAuthRow: PushNotificationAuthRow = { + let row = PushNotificationAuthRow() + return row + }() let frequentQuestionButton: FullRowButton = { let button = FullRowButton(labelText: "자주 묻는 질문") return button @@ -61,6 +65,8 @@ public class WorkerSettingVC: BaseViewController { // Observable + private let signOutComfirmed: PublishRelay = .init() + private let disposeBag = DisposeBag() public init() { @@ -69,6 +75,57 @@ public class WorkerSettingVC: BaseViewController { public required init?(coder: NSCoder) { fatalError() } + public func bind(viewModel: CenterSettingVMable) { + + // Input + Observable.merge( + myCenterInfoButton.rx.tap.asObservable(), + centerInfoCard.rx.tap.asObservable() + ) + .bind(to: viewModel.myCenterProfileButtonClicked) + .disposed(by: disposeBag) + + pushNotificationAuthRow.`switch`.rx.isOn + .map({ [pushNotificationAuthRow] isOn in + if isOn { + // On여부는 아웃풋에 한해서만 설정되도록한다. + pushNotificationAuthRow.`switch`.setOn(false, animated: false) + } + return isOn + }) + .bind(to: viewModel.approveToPushNotification) + .disposed(by: disposeBag) + + signOutComfirmed + .bind(to: viewModel.signOutButtonComfirmed) + .disposed(by: disposeBag) + + removeAccountButton.rx.tap + .bind(to: viewModel.removeAccountButtonClicked) + .disposed(by: disposeBag) + + // Output + viewModel + .pushNotificationApproveState? + .drive(onNext: { [weak self] isOn in + self?.pushNotificationAuthRow.`switch`.setOn(isOn, animated: true) + }) + .disposed(by: disposeBag) + + viewModel + .centerInfo? + .drive(onNext: { [weak self] info in + self?.centerInfoCard.bind(nameText: info.name, locationText: info.location) + }) + .disposed(by: disposeBag) + + viewModel.alert? + .drive(onNext: { [weak self] alertVO in + self?.showAlert(vo: alertVO) + }) + .disposed(by: disposeBag) + } + public override func viewDidLoad() { super.viewDidLoad() setAppearance() @@ -103,6 +160,8 @@ public class WorkerSettingVC: BaseViewController { let viewList = [ myCenterInfoContainer, Spacer(height: 8), + pushNotificationAuthRow, + Spacer(height: 8), frequentQuestionButton, askButton, Spacer(height: 8), @@ -154,6 +213,39 @@ public class WorkerSettingVC: BaseViewController { private func setObservable() { + // 설정화면에 종속적인 뷰들입니다. + + frequentQuestionButton.rx.tap + .subscribe(onNext: { + + // 자주하는 질문뷰 + + }) + .disposed(by: disposeBag) + + askButton.rx.tap + .subscribe(onNext: { + + // 문의하기 뷰 + + }) + .disposed(by: disposeBag) + + applicationPolicyButton.rx.tap + .subscribe(onNext: { + + // 약관및 정책 + + }) + .disposed(by: disposeBag) + + personalDataProcessingPolicyButton.rx.tap + .subscribe(onNext: { + + // 개인정보 처리방침 + + }) + .disposed(by: disposeBag) } } @@ -161,5 +253,5 @@ public class WorkerSettingVC: BaseViewController { @available(iOS 17.0, *) #Preview("Preview", traits: .defaultLayout) { - WorkerSettingVC() + CenterSettingVC() } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift new file mode 100644 index 00000000..6379b327 --- /dev/null +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -0,0 +1,154 @@ +// +// CenterSettingVM.swift +// CenterFeature +// +// Created by choijunios on 8/19/24. +// + +import UIKit +import BaseFeature +import PresentationCore +import RxCocoa +import RxSwift +import Entity +import DSKit +import UseCaseInterface +import UserNotifications + +public protocol CenterSettingScreenCoordinatable: ParentCoordinator { + /// 로그인및 회원가입 화면으로 이동합니다. + func showAuthScreen() + + /// 시설 관리자 계정을 지우는 작업을 시작합니다. + func startRemoveCenterAccountFlow() +} + +public protocol CenterSettingVMable { + + // Input + var viewWillAppear: PublishRelay { get } + var myCenterProfileButtonClicked: PublishRelay { get } + var approveToPushNotification: PublishRelay { get } + + var signOutButtonComfirmed: PublishRelay { get } + var removeAccountButtonClicked: PublishRelay { get } + + // Output + var pushNotificationApproveState: Driver? { get } + var showSettingAlert: Driver? { get } + var centerInfo: Driver<(name: String, location: String)>? { get } + var alert: Driver? { get } +} + + +public class CenterSettingVM: CenterSettingVMable { + + public var viewWillAppear: RxRelay.PublishRelay = .init() + public var myCenterProfileButtonClicked: RxRelay.PublishRelay = .init() + public var approveToPushNotification: RxRelay.PublishRelay = .init() + + public var signOutButtonComfirmed: RxRelay.PublishRelay = .init() + public var removeAccountButtonClicked: RxRelay.PublishRelay = .init() + + public var pushNotificationApproveState: RxCocoa.Driver? + public var centerInfo: RxCocoa.Driver<(name: String, location: String)>? + public var showSettingAlert: Driver? + public var alert: RxCocoa.Driver? + + let disposeBag = DisposeBag() + + weak var coordinator: CenterSettingScreenCoordinatable? + let settingUseCase: SettingScreenUseCase + let centerProfileUseCase: CenterProfileUseCase + + public init( + coordinator: CenterSettingScreenCoordinatable?, + settingUseCase: SettingScreenUseCase, + centerProfileUseCase: CenterProfileUseCase + ) + { + self.settingUseCase = settingUseCase + self.centerProfileUseCase = centerProfileUseCase + + // 기존의 알람수신 동의 여부 확인 + let currentNotificationAuthStatus = viewWillAppear + .flatMap { [settingUseCase] _ in + settingUseCase.checkPushNotificationApproved() + } + + + let requestApproveNotification = approveToPushNotification.filter { $0 }.map { _ in () } + let requestDenyNotification = approveToPushNotification.filter { !$0 }.map { _ in () } + + let approveRequestResult = requestApproveNotification + .flatMap { [settingUseCase] _ in + settingUseCase + .requestNotificationPermission() + } + + let approveGranted = approveRequestResult.filter { $0 == .granted } + let openSettingAppToApprove = approveRequestResult.filter { $0 == .openSystemSetting }.map { _ in () } + let approveRequestError = approveRequestResult.filter { + if case let .error(message) = $0 { return true } + return false + } + + // MARK: 뷰가 표시할 알람수신 동의 상태 + pushNotificationApproveState = Observable.merge( + currentNotificationAuthStatus, + approveGranted.map { _ in true } + ) + .asDriver(onErrorJustReturn: false) + + + // MARK: 세팅앱 열기 + showSettingAlert = Observable.merge( + openSettingAppToApprove, + requestDenyNotification + ) + .asDriver(onErrorJustReturn: ()) + + + // MARK: 센터카드 정보 알아내기 + let fetchCenterProfileResult = viewWillAppear + .flatMap { [centerProfileUseCase] _ in + centerProfileUseCase.getProfile(mode: .myProfile) + } + + let fetchCenterProfileSuccess = fetchCenterProfileResult.compactMap { $0.value } + let fetchCenterProfileFailure = fetchCenterProfileResult.compactMap { $0.error } + + centerInfo = fetchCenterProfileSuccess + .map { centerProfileVO in + ( + centerProfileVO.centerName, + centerProfileVO.roadNameAddress + ) + } + .asDriver(onErrorJustReturn: ("재가요양센터", "대한민국")) + + // MARK: 로그아웃 확인됨 + signOutButtonComfirmed + .subscribe(onNext: { [weak self] _ in + + // ‼️ ‼️ 계정 정보 삭제 ‼️ ‼️ + + self?.coordinator?.showAuthScreen() + }) + .disposed(by: disposeBag) + + + // MARK: Alert + alert = Observable.merge( + approveRequestError.map { _ in "알람수신 동의 실패" }, + fetchCenterProfileFailure.map { $0.message } + ) + .map({ message in + AlertWithCompletionVO( + title: "환경설정 오류", + message: message + ) + }) + .asDriver(onErrorJustReturn: .default) + } +} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/SettingVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/SettingVC.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift From b599bc76785397992402297d20eec8c850466738 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 21:42:10 +0900 Subject: [PATCH 06/24] =?UTF-8?q?[IDLE-000]=20IdleBigAlertController=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 --- .../Alert /IdleBigAlertController.swift | 72 +++---------------- 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift index bf565e09..731b5c1e 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift @@ -10,37 +10,15 @@ import RxSwift import RxCocoa import Entity -public protocol IdleAlertViewModelable { - - var button1Tapped: PublishRelay { get } - var button2Tapped: PublishRelay { get } -} - public class IdleBigAlertController: UIViewController { - public struct ButtonInfo { - let text: String - let backgroundColor: UIColor - let accentColor: UIColor - - public init(text: String, backgroundColor: UIColor, accentColor: UIColor) { - self.text = text - self.backgroundColor = backgroundColor - self.accentColor = accentColor - } - } - // Init values let titleText: String let descriptionText: String - let button1Info: ButtonInfo - let button2Info: ButtonInfo - public init(titleText: String, descriptionText: String, button1Info: ButtonInfo, button2Info: ButtonInfo) { + public init(titleText: String, descriptionText: String) { self.titleText = titleText self.descriptionText = descriptionText - self.button1Info = button1Info - self.button2Info = button2Info super.init(nibName: nil, bundle: nil) @@ -51,7 +29,6 @@ public class IdleBigAlertController: UIViewController { public required init?(coder: NSCoder) { fatalError() } // Not init - private var viewModel: IdleAlertViewModelable? private let disposeBag = DisposeBag() // View @@ -72,25 +49,15 @@ public class IdleBigAlertController: UIViewController { return label }() - private(set) lazy var button1: TextButtonType1 = { - let btn = TextButtonType1( - labelText: button1Info.text, - originBackground: button1Info.backgroundColor, - accentBackgroundColor: button1Info.accentColor - ) - btn.layer.borderColor = DSKitAsset.Colors.gray100.color.cgColor - btn.layer.borderWidth = 1 - btn.label.typography = .Heading4 - return btn + private(set) lazy var cancelButton: IdleThirdinaryButton = { + let button = IdleThirdinaryButton(level: .medium) + button.label.textString = "" + return button }() - private(set) lazy var button2: TextButtonType1 = { - let btn = TextButtonType1( - labelText: button2Info.text, - originBackground: button2Info.backgroundColor, - accentBackgroundColor: button2Info.accentColor - ) - btn.label.typography = .Heading4 - return btn + private(set) lazy var acceptButton: IdlePrimaryButton = { + let button = IdlePrimaryButton(level: .mediumRed) + button.label.textString = "" + return button }() private func setAppearance() { @@ -124,8 +91,8 @@ public class IdleBigAlertController: UIViewController { // 버튼 스택 let buttonStack = HStack( [ - button1, - button2 + cancelButton, + acceptButton ], spacing: 8, alignment: .fill, @@ -184,21 +151,4 @@ public class IdleBigAlertController: UIViewController { alertContainer.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), ]) } - - public func bind(viewModel vm: IdleAlertViewModelable) { - // RC=1 - self.viewModel = vm - - button1 - .eventPublisher - .map { _ in () } - .bind(to: vm.button1Tapped) - .disposed(by: disposeBag) - - button2 - .eventPublisher - .map { _ in () } - .bind(to: vm.button2Tapped) - .disposed(by: disposeBag) - } } From b82ead74f4fe7692cdeb8460a515f6bc9d4f5c36 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Mon, 19 Aug 2024 22:26:17 +0900 Subject: [PATCH 07/24] =?UTF-8?q?[IDLE-000]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=EC=8B=9C=20=EC=BB=A4=EC=8A=A4=ED=85=80=20Alert?= =?UTF-8?q?=EB=85=B8=EC=B6=9C=EB=B0=8F=20=EB=B2=84=ED=8A=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=88=98=EC=8B=A0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Setting/DefaultSettingUseCase.swift | 19 +- .../Setting/SettingScreenUseCase .swift | 6 + .../Sources/AlertViewController.swift | 12 +- .../Alert /IdleBigAlertController.swift | 175 ++++++++++++++---- .../Alert /IdleSmallAlertController.swift | 67 ++----- .../Base/BaseViewController.swift | 22 ++- .../ExampleApp/Sources/SceneDelegate.swift | 12 +- .../View/Setting/CenterSettingVC.swift | 14 +- .../ViewModel/Setting/CenterSettingVM.swift | 44 ++++- 9 files changed, 247 insertions(+), 124 deletions(-) diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift index 5b48a9de..5bcf3ffe 100644 --- a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift @@ -1,6 +1,6 @@ // // DefaultSettingUseCase.swift -// UseCaseInterface +// ConcreteUseCase // // Created by choijunios on 8/19/24. // @@ -8,15 +8,12 @@ import Foundation import RxSwift import UserNotifications - -public enum NotificationApproveAction: Equatable { - case openSystemSetting - case granted - case error(message: String) -} +import UseCaseInterface public class DefaultSettingUseCase: SettingScreenUseCase { + public init() { } + public func checkPushNotificationApproved() -> Single { Single.create { single in let center = UNUserNotificationCenter.current() @@ -30,6 +27,8 @@ public class DefaultSettingUseCase: SettingScreenUseCase { single(.success(false)) } } + + return Disposables.create { } } } @@ -43,7 +42,7 @@ public class DefaultSettingUseCase: SettingScreenUseCase { case .notDetermined: // Request permission since the user hasn't decided yet. current.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in - if let error = error { + if error != nil { maybe(.success(.error(message: "알람동의를 수행할 수 없습니다."))) } else { maybe(.success(.granted)) @@ -66,10 +65,10 @@ public class DefaultSettingUseCase: SettingScreenUseCase { } public func getPersonalDataUsageDescriptionUrl() -> URL { - <#code#> + URL(string: "")! } public func getApplicationPolicyUrl() -> URL { - <#code#> + URL(string: "")! } } diff --git a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift index 9b2b9bd9..4890118e 100644 --- a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift +++ b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift @@ -8,6 +8,12 @@ import Foundation import RxSwift +public enum NotificationApproveAction: Equatable { + case openSystemSetting + case granted + case error(message: String) +} + public protocol SettingScreenUseCase { /// 현재 알람수신 동의 여부를 확인합니다. diff --git a/project/Projects/Presentation/DSKit/ExampleApp/Sources/AlertViewController.swift b/project/Projects/Presentation/DSKit/ExampleApp/Sources/AlertViewController.swift index 87ebb128..03282175 100644 --- a/project/Projects/Presentation/DSKit/ExampleApp/Sources/AlertViewController.swift +++ b/project/Projects/Presentation/DSKit/ExampleApp/Sources/AlertViewController.swift @@ -18,17 +18,7 @@ class AlertViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { let alertVC = IdleSmallAlertController( - titleText: "정말 탈퇴하시겠어요?", - button1Info: .init( - text: "취소하기", - backgroundColor: .white, - accentColor: DSKitAsset.Colors.gray100.color - ), - button2Info: .init( - text: "탈퇴하기", - backgroundColor: DSKitAsset.Colors.orange500.color, - accentColor: DSKitAsset.Colors.orange700.color - ) + titleText: "정말 탈퇴하시겠어요?" ) alertVC.modalPresentationStyle = .fullScreen diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift index 731b5c1e..74984abb 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift @@ -10,38 +10,32 @@ import RxSwift import RxCocoa import Entity -public class IdleBigAlertController: UIViewController { - - // Init values - let titleText: String - let descriptionText: String +public protocol IdleAlertViewModelable { - public init(titleText: String, descriptionText: String) { - self.titleText = titleText - self.descriptionText = descriptionText - - super.init(nibName: nil, bundle: nil) - - setAppearance() - setAutoLayout() - } + var acceptButtonClicked: PublishRelay { get } + var cancelButtonClicked: PublishRelay { get } + var acceptButtonLabelText: String { get } + var cancelButtonLabelText: String { get } + var title: String { get } + var description: String { get } +} + +public class IdleBigAlertController: UIViewController { - public required init?(coder: NSCoder) { fatalError() } + let customTranstionDelegate = CustomTransitionDelegate() // Not init private let disposeBag = DisposeBag() // View - private(set) lazy var titleLabel: IdleLabel = { + let titleLabel: IdleLabel = { let label = IdleLabel(typography: .Subtitle1) - label.textString = titleText label.textAlignment = .center return label }() - private(set) lazy var descriptionLabel: IdleLabel = { + let descriptionLabel: IdleLabel = { let label = IdleLabel(typography: .Body3) - label.textString = descriptionText label.attrTextColor = DSKitAsset.Colors.gray500.color label.lineBreakMode = .byWordWrapping label.textAlignment = .center @@ -49,17 +43,30 @@ public class IdleBigAlertController: UIViewController { return label }() - private(set) lazy var cancelButton: IdleThirdinaryButton = { + public let cancelButton: IdleThirdinaryButton = { let button = IdleThirdinaryButton(level: .medium) button.label.textString = "" return button }() - private(set) lazy var acceptButton: IdlePrimaryButton = { + public let acceptButton: IdlePrimaryButton = { let button = IdlePrimaryButton(level: .mediumRed) button.label.textString = "" return button }() + public init() { + + super.init(nibName: nil, bundle: nil) + + self.transitioningDelegate = customTranstionDelegate + + setAppearance() + setAutoLayout() + setObservable() + } + + public required init?(coder: NSCoder) { fatalError() } + private func setAppearance() { view.backgroundColor = DSKitAsset.Colors.gray500.color.withAlphaComponent(0.5) @@ -70,23 +77,14 @@ public class IdleBigAlertController: UIViewController { private func setAutoLayout() { // 라벨 스택 - let labels = [ - titleLabel, - descriptionLabel - ] let textStack = VStack( - labels, + [ + Spacer(height: 8), + titleLabel + ], spacing: 8, alignment: .center ) - labels.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } - NSLayoutConstraint.activate([ - titleLabel.topAnchor.constraint(equalTo: textStack.topAnchor, constant: 8), - descriptionLabel.bottomAnchor.constraint(equalTo: textStack.bottomAnchor, constant: -8), - - descriptionLabel.leftAnchor.constraint(equalTo: textStack.leftAnchor, constant: 38), - descriptionLabel.rightAnchor.constraint(equalTo: textStack.rightAnchor, constant: -38), - ]) // 버튼 스택 let buttonStack = HStack( @@ -102,10 +100,14 @@ public class IdleBigAlertController: UIViewController { // 라벨 + 버튼 스택 let alertContentsStack = VStack( [ - textStack, + HStack([ + Spacer(width: 41.5), + textStack, + Spacer(width: 41.5) + ], alignment: .fill), buttonStack ], - spacing: 16, + spacing: 24, alignment: .fill ) @@ -121,7 +123,7 @@ public class IdleBigAlertController: UIViewController { alertContainer.layer.cornerRadius = 12 alertContainer.clipsToBounds = true - alertContainer.layoutMargins = .init(top: 12, left: 12, bottom: 12, right: 12) + alertContainer.layoutMargins = .init(top: 20, left: 12, bottom: 12, right: 12) [ alertContentsStack @@ -151,4 +153,103 @@ public class IdleBigAlertController: UIViewController { alertContainer.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), ]) } + + private func setObservable() { + cancelButton.rx.tap + .subscribe(onNext: { [weak self] _ in + guard let self else { return } + modalPresentationStyle = .custom + dismiss(animated: true) + }) + .disposed(by: disposeBag) + } + + public func bind(viewModel vm: IdleAlertViewModelable) { + + titleLabel.textString = vm.title + descriptionLabel.textString = vm.description + + acceptButton.label.textString = vm.acceptButtonLabelText + cancelButton.label.textString = vm.cancelButtonLabelText + + acceptButton.rx.tap + .bind(to: vm.acceptButtonClicked) + .disposed(by: disposeBag) + + cancelButton + .rx.tap + .bind(to: vm.cancelButtonClicked) + .disposed(by: disposeBag) + } +} + +class FadeInAnimator: NSObject, UIViewControllerAnimatedTransitioning { + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return 0.35 // 애니메이션 지속 시간 + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let fromView = transitionContext.view(forKey: .from) else { return } + + let containerView = transitionContext.containerView + containerView.addSubview(fromView) + + // 애니메이션 시작 상태 설정 + fromView.alpha = 1.0 + + let duration = transitionDuration(using: transitionContext) + UIView.animate(withDuration: duration, animations: { + // 애니메이션 적용 + fromView.alpha = 0.0 + }) { _ in + // 애니메이션 완료 후 처리 + fromView.removeFromSuperview() + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + } + } +} + +class FadeOutAnimator: NSObject, UIViewControllerAnimatedTransitioning { + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return 0.35 // 애니메이션 지속 시간 + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let toView = transitionContext.view(forKey: .to) else { return } + + let containerView = transitionContext.containerView + containerView.addSubview(toView) + + // 애니메이션 시작 상태 설정 + toView.alpha = 0.0 + + let duration = transitionDuration(using: transitionContext) + UIView.animate(withDuration: duration, animations: { + // 애니메이션 적용 + toView.alpha = 1.0 + }) { _ in + // 애니메이션 완료 후 처리 + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + } + } +} + +class CustomTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate { + + func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return FadeInAnimator() // 우리가 만든 사용자 정의 애니메이터를 반환 + } + + func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? { + return FadeOutAnimator() + } +} + + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + IdleBigAlertController() } diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleSmallAlertController.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleSmallAlertController.swift index 0fffb44a..dcc0c84f 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleSmallAlertController.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleSmallAlertController.swift @@ -12,27 +12,11 @@ import Entity public class IdleSmallAlertController: UIViewController { - public struct ButtonInfo { - let text: String - let backgroundColor: UIColor - let accentColor: UIColor - - public init(text: String, backgroundColor: UIColor, accentColor: UIColor) { - self.text = text - self.backgroundColor = backgroundColor - self.accentColor = accentColor - } - } - // Init values let titleText: String - let button1Info: ButtonInfo - let button2Info: ButtonInfo - public init(titleText: String, button1Info: ButtonInfo, button2Info: ButtonInfo) { + public init(titleText: String) { self.titleText = titleText - self.button1Info = button1Info - self.button2Info = button2Info super.init(nibName: nil, bundle: nil) @@ -43,7 +27,6 @@ public class IdleSmallAlertController: UIViewController { public required init?(coder: NSCoder) { fatalError() } // Not init - private var viewModel: IdleAlertViewModelable? private let disposeBag = DisposeBag() // View @@ -54,26 +37,15 @@ public class IdleSmallAlertController: UIViewController { return label }() - - private(set) lazy var button1: TextButtonType1 = { - let btn = TextButtonType1( - labelText: button1Info.text, - originBackground: button1Info.backgroundColor, - accentBackgroundColor: button1Info.accentColor - ) - btn.layer.borderColor = DSKitAsset.Colors.gray100.color.cgColor - btn.layer.borderWidth = 1 - btn.label.typography = .Heading4 - return btn + private(set) lazy var cancelButton: IdleThirdinaryButton = { + let button = IdleThirdinaryButton(level: .medium) + button.label.textString = "" + return button }() - private(set) lazy var button2: TextButtonType1 = { - let btn = TextButtonType1( - labelText: button2Info.text, - originBackground: button2Info.backgroundColor, - accentBackgroundColor: button2Info.accentColor - ) - btn.label.typography = .Heading4 - return btn + private(set) lazy var acceptButton: IdlePrimaryButton = { + let button = IdlePrimaryButton(level: .mediumRed) + button.label.textString = "" + return button }() private func setAppearance() { @@ -89,8 +61,8 @@ public class IdleSmallAlertController: UIViewController { // 버튼 스택 let buttonStack = HStack( [ - button1, - button2 + cancelButton, + acceptButton ], spacing: 8, alignment: .fill, @@ -149,21 +121,4 @@ public class IdleSmallAlertController: UIViewController { alertContainer.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), ]) } - - public func bind(viewModel vm: IdleAlertViewModelable) { - // RC=1 - self.viewModel = vm - - button1 - .eventPublisher - .map { _ in () } - .bind(to: vm.button1Tapped) - .disposed(by: disposeBag) - - button2 - .eventPublisher - .map { _ in () } - .bind(to: vm.button2Tapped) - .disposed(by: disposeBag) - } } diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift index 9e4810ca..b6bf9b04 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift @@ -7,6 +7,7 @@ import UIKit import Entity +import DSKit open class BaseViewController: UIViewController { } @@ -14,23 +15,32 @@ open class BaseViewController: UIViewController { } public extension BaseViewController { func showAlert(vo: DefaultAlertContentVO, onClose: (() -> ())? = nil) { - let alret = UIAlertController(title: vo.title, message: vo.message, preferredStyle: .alert) + let alert = UIAlertController(title: vo.title, message: vo.message, preferredStyle: .alert) let close = UIAlertAction(title: "닫기", style: .default) { _ in onClose?() } - alret.addAction(close) - present(alret, animated: true, completion: nil) + alert.addAction(close) + present(alert, animated: true, completion: nil) } func showAlert(vo: AlertWithCompletionVO) { - let alret = UIAlertController(title: vo.title, message: vo.message, preferredStyle: .alert) + let alert = UIAlertController(title: vo.title, message: vo.message, preferredStyle: .alert) vo.buttonInfo.forEach { (buttonTitle: String, completion: AlertWithCompletionVO.AlertCompletion?) in let button = UIAlertAction(title: buttonTitle, style: .default) { _ in completion?() } - alret.addAction(button) + alert.addAction(button) } - present(alret, animated: true, completion: nil) + present(alert, animated: true, completion: nil) + } + + func showIdleModal( + viewModel: IdleAlertViewModelable + ) { + let alertVC = IdleBigAlertController() + alertVC.bind(viewModel: viewModel) + alertVC.modalPresentationStyle = .custom + present(alertVC, animated: true, completion: nil) } } diff --git a/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift index f0624b79..3a572918 100644 --- a/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Center/ExampleApp/Sources/SceneDelegate.swift @@ -47,9 +47,17 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // ) // ) // - let vc = WorkerSettingVC() + let vc = CenterSettingVC() + let vm = CenterSettingVM( + coordinator: nil, + settingUseCase: DefaultSettingUseCase(), + centerProfileUseCase: DefaultCenterProfileUseCase( + repository: DefaultUserProfileRepository() + ) + + ) -// vc.bind(viewModel: vm) + vc.bind(viewModel: vm) window = UIWindow(windowScene: windowScene) window?.rootViewController = vc diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift index aa93c39c..2fbbdfb1 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift @@ -15,6 +15,8 @@ import DSKit public class CenterSettingVC: BaseViewController { + var viewModel: CenterSettingVMable? + // Init // View @@ -77,6 +79,8 @@ public class CenterSettingVC: BaseViewController { public func bind(viewModel: CenterSettingVMable) { + self.viewModel = viewModel + // Input Observable.merge( myCenterInfoButton.rx.tap.asObservable(), @@ -214,7 +218,6 @@ public class CenterSettingVC: BaseViewController { private func setObservable() { // 설정화면에 종속적인 뷰들입니다. - frequentQuestionButton.rx.tap .subscribe(onNext: { @@ -246,6 +249,15 @@ public class CenterSettingVC: BaseViewController { }) .disposed(by: disposeBag) + + signOutButton + .rx.tap + .subscribe(onNext: { [weak self] _ in + guard let self, let vm = viewModel else { return } + let signOutVM = vm.createSingOutVM() + showIdleModal(viewModel: signOutVM) + }) + .disposed(by: disposeBag) } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index 6379b327..541b2153 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -38,9 +38,11 @@ public protocol CenterSettingVMable { var showSettingAlert: Driver? { get } var centerInfo: Driver<(name: String, location: String)>? { get } var alert: Driver? { get } + + // SignOut + func createSingOutVM() -> IdleAlertViewModelable } - public class CenterSettingVM: CenterSettingVMable { public var viewWillAppear: RxRelay.PublishRelay = .init() @@ -151,4 +153,44 @@ public class CenterSettingVM: CenterSettingVMable { }) .asDriver(onErrorJustReturn: .default) } + + public func createSingOutVM() -> any DSKit.IdleAlertViewModelable { + let viewModel = CenterSingOutVM( + title: "로그아웃하시겠어요?", + description: "", + acceptButtonLabelText: "로그아웃", + cancelButtonLabelText: "취소하기" + ) + + viewModel + .acceptButtonClicked + .bind(to: signOutButtonComfirmed) + .disposed(by: disposeBag) + + return viewModel + } +} + +class CenterSingOutVM: IdleAlertViewModelable { + + var acceptButtonLabelText: String + var cancelButtonLabelText: String + + var acceptButtonClicked: RxRelay.PublishRelay = .init() + var cancelButtonClicked: RxRelay.PublishRelay = .init() + + var title: String + var description: String + + init( + title: String, + description: String, + acceptButtonLabelText: String, + cancelButtonLabelText: String + ) { + self.title = title + self.description = description + self.acceptButtonLabelText = acceptButtonLabelText + self.cancelButtonLabelText = cancelButtonLabelText + } } From ff3b27ed71894fab88a765ceaf2c943d22e1de72 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 12:57:02 +0900 Subject: [PATCH 08/24] =?UTF-8?q?[IDLE-000]=20=EC=84=A4=EC=A0=95=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20=EC=95=8C=EB=A6=BC=EC=88=98?= =?UTF-8?q?=EC=8B=A0=20=EC=97=AC=EB=B6=80=EB=A5=BC=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=ED=95=9C=20=EC=9D=B4=ED=9B=84=20=EB=8B=A4=EC=8B=9C=20=EB=8F=8C?= =?UTF-8?q?=EC=95=84=EC=98=AC=20=EA=B2=BD=EC=9A=B0=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Setting/DefaultSettingUseCase.swift | 4 ++-- .../PushNotificationAuthRow.swift | 10 +++++++-- .../View/Setting/CenterSettingVC.swift | 21 +++++++++++++++---- .../ViewModel/Setting/CenterSettingVM.swift | 17 +++++++++++---- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift index 5bcf3ffe..0259d9a9 100644 --- a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift @@ -20,9 +20,9 @@ public class DefaultSettingUseCase: SettingScreenUseCase { center.getNotificationSettings { settings in switch settings.authorizationStatus { case .notDetermined, .denied: - single(.success(true)) - case .authorized, .provisional, .ephemeral: single(.success(false)) + case .authorized, .provisional, .ephemeral: + single(.success(true)) @unknown default: single(.success(false)) } diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift index b81823f7..e1a0752d 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/SettingComponent/PushNotificationAuthRow.swift @@ -25,6 +25,8 @@ public class PushNotificationAuthRow: UIView { return view }() + private var onFirstLoad: Bool = true + public init() { super.init(frame: .zero) setApearance() @@ -69,13 +71,17 @@ public class PushNotificationAuthRow: UIView { } + public override func layoutSubviews() { + super.layoutSubviews() + } + public override func draw(_ rect: CGRect) { - if `switch`.bounds.height > label.bounds.height { + if onFirstLoad, `switch`.bounds.height > label.bounds.height { + onFirstLoad = false let per = label.bounds.height / `switch`.bounds.height `switch`.transform = `switch`.transform.scaledBy(x: per, y: per) } } - } @available(iOS 17.0, *) diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift index 2fbbdfb1..5763f20d 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift @@ -91,10 +91,10 @@ public class CenterSettingVC: BaseViewController { pushNotificationAuthRow.`switch`.rx.isOn .map({ [pushNotificationAuthRow] isOn in - if isOn { - // On여부는 아웃풋에 한해서만 설정되도록한다. - pushNotificationAuthRow.`switch`.setOn(false, animated: false) - } + + // On / Off 여부는 ViewModel이 설정한다. + pushNotificationAuthRow.`switch`.setOn(false, animated: false) + return isOn }) .bind(to: viewModel.approveToPushNotification) @@ -123,11 +123,24 @@ public class CenterSettingVC: BaseViewController { }) .disposed(by: disposeBag) + // MARK: 세팅화면으로 이동 + viewModel + .showSettingAlert? + .drive(onNext: { + if let settingsUrl = URL(string: UIApplication.openSettingsURLString) { + if UIApplication.shared.canOpenURL(settingsUrl) { + UIApplication.shared.open(settingsUrl, options: [:], completionHandler: nil) + } + } + }) + .disposed(by: disposeBag) + viewModel.alert? .drive(onNext: { [weak self] alertVO in self?.showAlert(vo: alertVO) }) .disposed(by: disposeBag) + } public override func viewDidLoad() { diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index 541b2153..fac2ee68 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -72,12 +72,18 @@ public class CenterSettingVM: CenterSettingVMable { self.settingUseCase = settingUseCase self.centerProfileUseCase = centerProfileUseCase + // 기존의 알람수신 동의 여부 확인 - let currentNotificationAuthStatus = viewWillAppear + // 설정화면에서 다시돌아온 경우 이벤트 수신 + let refreshNotificationStatusRequest = Observable.merge( + NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification).map {_ in }, + viewWillAppear.asObservable() + ) + + let currentNotificationAuthStatus = refreshNotificationStatusRequest .flatMap { [settingUseCase] _ in settingUseCase.checkPushNotificationApproved() } - let requestApproveNotification = approveToPushNotification.filter { $0 }.map { _ in () } let requestDenyNotification = approveToPushNotification.filter { !$0 }.map { _ in () } @@ -91,7 +97,10 @@ public class CenterSettingVM: CenterSettingVMable { let approveGranted = approveRequestResult.filter { $0 == .granted } let openSettingAppToApprove = approveRequestResult.filter { $0 == .openSystemSetting }.map { _ in () } let approveRequestError = approveRequestResult.filter { - if case let .error(message) = $0 { return true } + if case let .error(message) = $0 { + printIfDebug("알림동의 실패: \(message)") + return true + } return false } @@ -133,7 +142,7 @@ public class CenterSettingVM: CenterSettingVMable { signOutButtonComfirmed .subscribe(onNext: { [weak self] _ in - // ‼️ ‼️ 계정 정보 삭제 ‼️ ‼️ + // ‼️ ‼️ 로컬에 저장된 계정 정보 삭제 ‼️ ‼️ self?.coordinator?.showAuthScreen() }) From 619645eabc755947e72a6e488a54303cc2162825 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 13:38:04 +0900 Subject: [PATCH 09/24] =?UTF-8?q?[IDLE-000]=20CheckBoxWithLabelView?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../check_box_icon.imageset/Contents.json | 15 ++ .../check_box_icon.svg | 3 + .../CheckBox/CheckBoxWithLabelView.swift | 129 ++++++++++++++++++ .../Deregister/DeregisterVC.swift | 42 ++++++ 4 files changed, 189 insertions(+) create mode 100644 project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/Contents.json create mode 100644 project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/check_box_icon.svg create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift create mode 100644 project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift diff --git a/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/Contents.json b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/Contents.json new file mode 100644 index 00000000..6510b0a1 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "check_box_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/check_box_icon.svg b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/check_box_icon.svg new file mode 100644 index 00000000..1c9d0052 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/check_box_icon.imageset/check_box_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift new file mode 100644 index 00000000..1473ebe6 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift @@ -0,0 +1,129 @@ +// +// CheckBoxWithLabelView.swift +// DSKit +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import RxSwift +import RxCocoa + +public class CheckBoxWithLabelView: HStack { + + public enum State { + case idle + case checked(text: String) + } + var currentState: State = .idle + + // Init + let labelText: String + + // View + let label: IdleLabel = { + let label = IdleLabel(typography: .Body2) + return label + }() + + let checkIconView = DSIcon.checkBoxIcon.image.toView() + private lazy var checkBox: TappableUIView = { + let view = TappableUIView() + view.layer.cornerRadius = 2 + view.clipsToBounds = true + + view.addSubview(checkIconView) + checkIconView.translatesAutoresizingMaskIntoConstraints = false + checkIconView.tintColor = DSColor.gray0.color + view.layoutMargins = .init(top: 6, left: 4, bottom: 5, right: 3) + NSLayoutConstraint.activate([ + checkIconView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor), + checkIconView.leftAnchor.constraint(equalTo: view.layoutMarginsGuide.leftAnchor), + checkIconView.rightAnchor.constraint(equalTo: view.layoutMarginsGuide.rightAnchor), + checkIconView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor), + ]) + + // border설정 + view.layer.borderWidth = 1 + + return view + }() + + // Observable + public lazy var opTap: PublishRelay = .init() + let disposeBag = DisposeBag() + + public init(labelText: String) { + self.labelText = labelText + self.label.textString = labelText + super.init([], spacing: 12, alignment: .center) + setAppearance() + setLayout() + setObservable() + + // 초기상태 + currentState = .idle + toIdle() + } + public required init(coder: NSCoder) { fatalError() } + + func toIdle() { + checkBox.layer.borderColor = DSColor.gray100.color.cgColor + checkBox.backgroundColor = DSColor.gray0.color + checkIconView.isHidden = true + } + + func toChecked() { + checkBox.layer.borderColor = DSColor.orange500.color.cgColor + checkBox.backgroundColor = DSColor.orange500.color + checkIconView.isHidden = false + } + + func setAppearance() { + + } + + func setLayout() { + + [ + checkBox, + label, + Spacer() + ].forEach { + self.addArrangedSubview($0) + } + NSLayoutConstraint.activate([ + checkBox.widthAnchor.constraint(equalToConstant: 19), + checkBox.heightAnchor.constraint(equalToConstant: 19), + ]) + } + + func setObservable() { + checkBox + .rx.tap + .observe(on: MainScheduler.instance) + .compactMap { [weak self] _ -> State? in + guard let self else { return nil } + + UIView.animate(withDuration: 0.2) { + switch self.currentState { + case .idle: + self.currentState = .checked(text: self.labelText) + self.toChecked() + case .checked: + self.currentState = .idle + self.toIdle() + } + } + return self.currentState + } + .bind(to: opTap) + .disposed(by: disposeBag) + } +} + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + CheckBoxWithLabelView(labelText: "매칭매칭매칭매칭매칭매칭매칭") +} diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift new file mode 100644 index 00000000..8a259ef2 --- /dev/null +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift @@ -0,0 +1,42 @@ +// +// DeregisterVC.swift +// BaseFeature +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import PresentationCore +import RxCocoa +import RxSwift +import Entity +import DSKit + +public class DeregisterVC: BaseViewController { + + // Init + + // View + + // Observable + private let disposeBag = DisposeBag() + + public init() { + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder: NSCoder) { fatalError() } + + private func setAppearance() { + + } + + private func setLayout() { + + } + + private func setObservable() { + + } +} + From 1184412a9bd1263ece39d6d2f071433ae2d7917e Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 14:27:31 +0900 Subject: [PATCH 10/24] =?UTF-8?q?[IDLE-000]=20DeregisterVC=20UI=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VO/Deregister/DeregisterReasonVO.swift | 37 +++++ .../ExampleApp/Sources/SceneDelegate.swift | 2 +- .../Deregister/DeregisterVC.swift | 142 +++++++++++++++++- 3 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 project/Projects/Domain/Entity/VO/Deregister/DeregisterReasonVO.swift diff --git a/project/Projects/Domain/Entity/VO/Deregister/DeregisterReasonVO.swift b/project/Projects/Domain/Entity/VO/Deregister/DeregisterReasonVO.swift new file mode 100644 index 00000000..43745b69 --- /dev/null +++ b/project/Projects/Domain/Entity/VO/Deregister/DeregisterReasonVO.swift @@ -0,0 +1,37 @@ +// +// DeregisterReasonVO.swift +// Entity +// +// Created by choijunios on 8/21/24. +// + +import Foundation + +public enum DeregisterReasonVO: CaseIterable { + case matchingIssues + case inconvenienceUsingPlatform + case noReasonToContinueUsing + case usingOtherPlatform + case lackOfDesiredFeatures + case privacyConcerns + case noLongerOperatingCenter + + public var reasonText: String { + switch self { + case .matchingIssues: + return "매칭이 잘 이루어지지 않음" + case .inconvenienceUsingPlatform: + return "플랫폼 사용에 불편함을 느낌" + case .noReasonToContinueUsing: + return "플랫폼을 더 이상 사용할 이유가 없음" + case .usingOtherPlatform: + return "다른 플랫폼을 이용하고 있음" + case .lackOfDesiredFeatures: + return "원하는 기능의 부재" + case .privacyConcerns: + return "개인정보 보호 문제" + case .noLongerOperatingCenter: + return "더 이상 센터를 운영하지 않음" + } + } +} diff --git a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift index 7c3560ed..fd8f7361 100644 --- a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift @@ -17,7 +17,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = scene as? UIWindowScene else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = UIViewController() + window?.rootViewController = DeregisterVC() window?.makeKeyAndVisible() } diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift index 8a259ef2..c988de40 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift @@ -11,14 +11,60 @@ import RxCocoa import RxSwift import Entity import DSKit +import Entity public class DeregisterVC: BaseViewController { // Init // View + let navigationBar: IdleNavigationBar = { + let bar = IdleNavigationBar(innerViews: []) + bar.titleLabel.textString = "계정 삭제" + return bar + }() + let titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Heading1) + label.textString = "정말 탈퇴하시겠어요?" + return label + }() + let subTitleLabel: IdleLabel = { + let label = IdleLabel(typography: .Body3) + label.numberOfLines = 2 + label.textAlignment = .left + label.textString = "계정을 삭제하시려는 이유를 알려주세요.\n소중한 피드백을 받아 더 나은 서비스로 보답하겠습니다." + label.attrTextColor = DSColor.gray300.color + return label + }() + var itemViewList: [CheckBoxWithLabelView] = [] + + let finalWarningLabel: IdleLabel = { + let label = IdleLabel(typography: .caption) + label.textString = "탈퇴 버튼 선택 시 모든 정보가 삭제되며, 되돌릴 수 없습니다." + label.attrTextColor = DSColor.red100.color + return label + }() + + let cancelButton: IdleThirdinaryButton = { + let button = IdleThirdinaryButton(level: .medium) + button.label.textString = "취소하기" + return button + }() + + let acceptDeregisterButton: IdlePrimaryButton = { + let button = IdlePrimaryButton(level: .mediumRed) + button.label.textString = "탈퇴하기" + return button + }() // Observable + private var selectedReasons: [DeregisterReasonVO: Bool] = { + var dict: [DeregisterReasonVO: Bool] = [:] + DeregisterReasonVO.allCases.forEach { vo in + dict[vo] = false + } + return dict + }() private let disposeBag = DisposeBag() public init() { @@ -27,16 +73,110 @@ public class DeregisterVC: BaseViewController { public required init?(coder: NSCoder) { fatalError() } + public override func viewDidLoad() { + super.viewDidLoad() + setAppearance() + setCheckListView() + setLayout() + setObservable() + } + private func setAppearance() { - + view.backgroundColor = DSColor.gray0.color } private func setLayout() { + let checkListScrollView = UIScrollView() + checkListScrollView.contentInset.top = 32 + let contentGuide = checkListScrollView.contentLayoutGuide + let frameGuide = checkListScrollView.frameLayoutGuide + let contentStack = VStack(itemViewList, spacing: 16, alignment: .fill) + checkListScrollView.addSubview(contentStack) + contentStack.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + contentStack.topAnchor.constraint(equalTo: contentGuide.topAnchor), + contentStack.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor, constant: -20), + + contentStack.leftAnchor.constraint(equalTo: frameGuide.leftAnchor, constant: 20), + contentStack.rightAnchor.constraint(equalTo: frameGuide.rightAnchor, constant: 20), + ]) + + let titleLabelStack = VStack([ + titleLabel, + subTitleLabel + ], spacing: 8, alignment: .leading) + + let buttonStack = HStack( + [ + cancelButton, + acceptDeregisterButton + ], + spacing: 8, + alignment: .center, + distribution: .fillEqually + ) + + [ + navigationBar, + titleLabelStack, + checkListScrollView, + finalWarningLabel, + buttonStack + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + } + + NSLayoutConstraint.activate([ + navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + navigationBar.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + navigationBar.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + + titleLabelStack.topAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: 24), + titleLabelStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + titleLabelStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), + + checkListScrollView.topAnchor.constraint(equalTo: titleLabelStack.bottomAnchor), + checkListScrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + checkListScrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + checkListScrollView.bottomAnchor.constraint(equalTo: finalWarningLabel.bottomAnchor), + + finalWarningLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), + finalWarningLabel.bottomAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -12), + + buttonStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + buttonStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), + buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -14), + ]) } private func setObservable() { } + + private func setCheckListView() { + + self.itemViewList = DeregisterReasonVO.allCases.map { vo in + let itemView = CheckBoxWithLabelView( + labelText: vo.reasonText + ) + + itemView + .opTap + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] state in + switch state { + case .idle: + self?.selectedReasons[vo] = false + case .checked: + self?.selectedReasons[vo] = true + } + }) + .disposed(by: disposeBag) + + return itemView + } + } } From e234a1b9fc1581af88f8b7e8e6bcd45bc49fab7f Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 14:38:45 +0900 Subject: [PATCH 11/24] =?UTF-8?q?[IDLE-000]=20=EC=B5=9C=EC=86=8C=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EC=9D=98=20=EC=9D=B4=EC=9C=A0=EB=A5=BC=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=9D=B4=20=EC=A0=84=ED=99=98=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CheckBox/CheckBoxWithLabelView.swift | 35 +++++++++------- .../Deregister/DeregisterVC.swift | 42 +++++++++++-------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift index 1473ebe6..caef8207 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/CheckBox/CheckBoxWithLabelView.swift @@ -9,15 +9,16 @@ import UIKit import RxSwift import RxCocoa -public class CheckBoxWithLabelView: HStack { +public class CheckBoxWithLabelView: HStack { public enum State { - case idle - case checked(text: String) + case idle(item: Item) + case checked(item: Item) } - var currentState: State = .idle + var currentState: State? // Init + let item: Item let labelText: String // View @@ -53,7 +54,8 @@ public class CheckBoxWithLabelView: HStack { public lazy var opTap: PublishRelay = .init() let disposeBag = DisposeBag() - public init(labelText: String) { + public init(item: Item, labelText: String) { + self.item = item self.labelText = labelText self.label.textString = labelText super.init([], spacing: 12, alignment: .center) @@ -62,7 +64,7 @@ public class CheckBoxWithLabelView: HStack { setObservable() // 초기상태 - currentState = .idle + currentState = .idle(item: item) toIdle() } public required init(coder: NSCoder) { fatalError() } @@ -106,13 +108,15 @@ public class CheckBoxWithLabelView: HStack { guard let self else { return nil } UIView.animate(withDuration: 0.2) { - switch self.currentState { - case .idle: - self.currentState = .checked(text: self.labelText) - self.toChecked() - case .checked: - self.currentState = .idle - self.toIdle() + if let currentState = self.currentState { + switch currentState { + case .idle: + self.currentState = .checked(item: self.item) + self.toChecked() + case .checked: + self.currentState = .idle(item: self.item) + self.toIdle() + } } } return self.currentState @@ -125,5 +129,8 @@ public class CheckBoxWithLabelView: HStack { @available(iOS 17.0, *) #Preview("Preview", traits: .defaultLayout) { - CheckBoxWithLabelView(labelText: "매칭매칭매칭매칭매칭매칭매칭") + CheckBoxWithLabelView( + item: "", + labelText: "매칭매칭매칭매칭매칭매칭매칭" + ) } diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift index c988de40..2a509e71 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift @@ -13,6 +13,8 @@ import Entity import DSKit import Entity + + public class DeregisterVC: BaseViewController { // Init @@ -36,7 +38,14 @@ public class DeregisterVC: BaseViewController { label.attrTextColor = DSColor.gray300.color return label }() - var itemViewList: [CheckBoxWithLabelView] = [] + var itemViewList: [CheckBoxWithLabelView] = { + DeregisterReasonVO.allCases.map { vo in + return CheckBoxWithLabelView( + item: vo, + labelText: vo.reasonText + ) + } + }() let finalWarningLabel: IdleLabel = { let label = IdleLabel(typography: .caption) @@ -54,6 +63,7 @@ public class DeregisterVC: BaseViewController { let acceptDeregisterButton: IdlePrimaryButton = { let button = IdlePrimaryButton(level: .mediumRed) button.label.textString = "탈퇴하기" + button.setEnabled(false) return button }() @@ -65,6 +75,7 @@ public class DeregisterVC: BaseViewController { } return dict }() + private let disposeBag = DisposeBag() public init() { @@ -76,7 +87,6 @@ public class DeregisterVC: BaseViewController { public override func viewDidLoad() { super.viewDidLoad() setAppearance() - setCheckListView() setLayout() setObservable() } @@ -153,29 +163,27 @@ public class DeregisterVC: BaseViewController { private func setObservable() { - } - - private func setCheckListView() { - - self.itemViewList = DeregisterReasonVO.allCases.map { vo in - let itemView = CheckBoxWithLabelView( - labelText: vo.reasonText - ) + itemViewList.forEach { itemView in itemView .opTap .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] state in + .map({ [weak self] state in + switch state { - case .idle: - self?.selectedReasons[vo] = false - case .checked: - self?.selectedReasons[vo] = true + case .idle(let item): + self?.selectedReasons[item] = false + case .checked(let item): + self?.selectedReasons[item] = true } + return state + }) + .subscribe(onNext: { [weak self] _ in + guard let self else { return } + let selectCount = selectedReasons.values.filter { $0 }.count + acceptDeregisterButton.setEnabled(selectCount > 0) }) .disposed(by: disposeBag) - - return itemView } } } From d039898022c73ca18f0ae34a7801929784819689 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 15:12:37 +0900 Subject: [PATCH 12/24] =?UTF-8?q?[IDLE-000]=20DeregisterReasonVC=EB=A5=BC?= =?UTF-8?q?=20RootFeature=EB=AA=A8=EB=93=88=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeRegisterCoordinator구현한였습니다. --- .../Coordinator/DeRegisterCoordinator.swift | 54 +++++++++++++++++++ .../Deregister/DeregisterCoordinator.swift | 18 +++++++ .../Deregister/View/DeregisterReasonVC.swift} | 30 +++++++++-- .../ViewModel/CenterDeregisterReasonsVM.swift | 28 ++++++++++ .../Setting/WorkerSettingCoordinator.swift | 30 ----------- 5 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift rename project/Projects/Presentation/Feature/{Base/Sources/View/ViewController/Deregister/DeregisterVC.swift => Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift} (88%) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/WorkerSettingCoordinator.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift new file mode 100644 index 00000000..2d11a8c5 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -0,0 +1,54 @@ +// +// DeRegisterCoordinator.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import Entity +import PresentationCore + +public class DeRegisterCoordinator: DeregisterCoordinator { + + public struct Dependency { + let userType: UserType + let navigationController: UINavigationController + } + + public var childCoordinators: [any Coordinator] = [] + + public var navigationController: UINavigationController + + var viewControllerRef: UIViewController? + let userType: UserType + + public init(dependency: Dependency) { + self.userType = dependency.userType + self.navigationController = dependency.navigationController + } + + public func start() { + var vm: DeregisterReasonVMable! + switch userType { + case .center: + vm = CenterDeregisterReasonsVM(coordinator: self) + case .worker: + fatalError() + } + + let vc = DeregisterReasonVC() + vc.bind(viewModel: vm) + viewControllerRef = vc + navigationController.pushViewController(vc, animated: true) + } + + public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { + + } + + public func showFinalPhoneAuthScreen(reasons: [Entity.DeregisterReasonVO]) { + + } + +} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift new file mode 100644 index 00000000..ee34f272 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift @@ -0,0 +1,18 @@ +// +// DeregisterCoordinator.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import PresentationCore +import Entity + +public protocol DeregisterCoordinator: ParentCoordinator { + + /// 센터관리자: 마지막으로 비밀번호를 입력합니다. + func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) + + /// 요양보호사: 마지막으로 전화번호를 입력합니다. + func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) +} diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift similarity index 88% rename from project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift index 2a509e71..678532b2 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Deregister/DeregisterVC.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift @@ -1,24 +1,31 @@ // -// DeregisterVC.swift -// BaseFeature +// DeregisterReasonVC.swift +// RootFeature // // Created by choijunios on 8/21/24. // import UIKit import PresentationCore +import BaseFeature import RxCocoa import RxSwift import Entity import DSKit import Entity +public protocol DeregisterReasonVMable { + var coordinator: DeregisterCoordinator? { get } + var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> { get } +} - -public class DeregisterVC: BaseViewController { +public class DeregisterReasonVC: BaseViewController { // Init + // Not init + var viewModel: DeregisterReasonVMable? + // View let navigationBar: IdleNavigationBar = { let bar = IdleNavigationBar(innerViews: []) @@ -186,5 +193,20 @@ public class DeregisterVC: BaseViewController { .disposed(by: disposeBag) } } + + public func bind(viewModel: DeregisterReasonVMable) { + + acceptDeregisterButton + .rx.tap + .map { [weak self] _ in + let reasons = self?.selectedReasons.filter({ (reason, isActive) in isActive}).map { (key, _) in + key + } + return reasons ?? [] + } + .bind(to: viewModel.acceptDeregisterButonClicked) + .disposed(by: disposeBag) + + } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift new file mode 100644 index 00000000..2ee3a3b2 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift @@ -0,0 +1,28 @@ +// +// CenterDeregisterReasonsVM.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import Entity +import RxSwift +import RxCocoa + +public class CenterDeregisterReasonsVM: DeregisterReasonVMable { + + public var coordinator: (any DeregisterCoordinator)? + public var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> = .init() + + let disposeBag = DisposeBag() + + public init(coordinator: DeregisterCoordinator?) { + self.coordinator = coordinator + + acceptDeregisterButonClicked + .subscribe(onNext: { [weak self] reasons in + self?.coordinator?.showFinalPasswordScreen(reasons: reasons) + }) + .disposed(by: disposeBag) + } +} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/WorkerSettingCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/WorkerSettingCoordinator.swift deleted file mode 100644 index 8d7d493c..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/WorkerSettingCoordinator.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// WorkerSettingCoordinator.swift -// RootFeature -// -// Created by choijunios on 8/19/24. -// - -import UIKit -import PresentationCore - -public class WorkerSettingCoordinator: ChildCoordinator { - - public weak var viewControllerRef: UIViewController? - - public var navigationController: UINavigationController - - public init(navigationController: UINavigationController) { - self.navigationController = navigationController - } - - public func start() { - let vc = TestSettingVC() - - navigationController.pushViewController(vc, animated: false) - } - - public func coordinatorDidFinish() { - - } -} From 37a4cd48941282529103c8a63e0b29881040755b Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 16:44:43 +0900 Subject: [PATCH 13/24] =?UTF-8?q?[IDLE-000]=20=EC=84=BC=ED=84=B0=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83/=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=ED=83=88=ED=87=B4=20API/UseCase/Repository=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/DefaultAuthRepository.swift | 21 +++ .../Data/NetworkDataSource/API/AuthAPI.swift | 15 ++ .../Auth/DefaultAuthUseCase.swift | 24 ++- .../Domain/Entity/Error/Auth/AuthError.swift | 2 +- .../Auth/Login/AuthRepository.swift | 4 +- .../UseCaseInterface/Auth/AuthUseCase.swift | 20 ++- .../TextField/IdleOneLineInputField.swift | 6 + .../ExampleApp/Sources/SceneDelegate.swift | 2 +- .../View/PasswordForDeregisterVC.swift | 154 ++++++++++++++++++ 9 files changed, 235 insertions(+), 13 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift diff --git a/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift b/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift index a91ee8c2..d214d2fe 100644 --- a/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift +++ b/project/Projects/Data/ConcreteRepository/Auth/DefaultAuthRepository.swift @@ -47,6 +47,27 @@ public extension DefaultAuthRepository { return networkService.requestDecodable(api: .centerLogin(id: id, password: password), with: .plain) .flatMap { [unowned self] in saveTokenToStore(token: $0) } } + + func signoutCenterAccount() -> RxSwift.Single { + networkService + .request(api: .signoutCenterAccount, with: .withToken) + .map { _ in } + } + + func deregisterCenterAccount(reasons: [DeregisterReasonVO], password: String) -> RxSwift.Single { + + let reasonString = reasons.map { $0.reasonText }.joined(separator: "|") + + return networkService + .request( + api: .deregisterCenterAccount( + reason: reasonString, + password: password + ), + with: .withToken + ) + .map { _ in } + } } // MARK: Worker auth diff --git a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift index cf891f1b..6f316114 100644 --- a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift +++ b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift @@ -21,6 +21,8 @@ public enum AuthAPI { case registerCenterAccount(data: Data) case centerLogin(id: String, password: String) case reissueToken(refreshToken: String) + case deregisterCenterAccount(reason: String, password: String) + case signoutCenterAccount // Worker case registerWorkerAccount(data: Data) @@ -46,6 +48,10 @@ extension AuthAPI: BaseAPI { return .post case .centerLogin: return .post + case .signoutCenterAccount: + return .post + case .deregisterCenterAccount: + return .post case .reissueToken: return .post case .registerWorkerAccount: @@ -69,6 +75,10 @@ extension AuthAPI: BaseAPI { "center/join" case .centerLogin: "center/login" + case .signoutCenterAccount: + "center/logout" + case .deregisterCenterAccount: + "center/withdraw" case .reissueToken: "center/refresh" case .registerWorkerAccount: @@ -89,6 +99,9 @@ extension AuthAPI: BaseAPI { case .centerLogin(let id, let password): params["identifier"] = id params["password"] = password + case .deregisterCenterAccount(let reason, let password): + params["reason"] = reason + params["password"] = password case .reissueToken(let refreshToken): params["refreshToken"] = refreshToken case .workerLogin(let phoneNumber, let verificationNumber): @@ -117,6 +130,8 @@ extension AuthAPI: BaseAPI { return .requestData(data) case .centerLogin: return .requestParameters(parameters: bodyParameters ?? [:], encoding: parameterEncoding) + case .deregisterCenterAccount: + return .requestParameters(parameters: bodyParameters ?? [:], encoding: parameterEncoding) case .reissueToken: return .requestParameters(parameters: bodyParameters ?? [:], encoding: parameterEncoding) case .registerWorkerAccount(let data): diff --git a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift index 65c7f854..16900150 100644 --- a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift @@ -12,14 +12,14 @@ import RxSwift import Entity public class DefaultAuthUseCase: AuthUseCase { - + let repository: AuthRepository public init(repository: AuthRepository) { self.repository = repository } - /// 센터 회원가입 실행 + // 센터 회원가입 실행 public func registerCenterAccount(registerState: CenterRegisterState) -> Single> { convert( task: repository.requestRegisterCenterAccount( @@ -31,18 +31,32 @@ public class DefaultAuthUseCase: AuthUseCase { )) } - /// 센터 로그인 실행 + // 센터 로그인 실행 public func loginCenterAccount(id: String, password: String) -> Single> { convert(task: repository.requestCenterLogin(id: id, password: password)) } - /// 요양 보호사 회원가입 실행 + // 센터 회원탈퇴 + public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { + convert( + task: repository.deregisterCenterAccount(reasons: reasons, password: password) + ) + } + + // 센터 로그아웃 + public func signoutCenterAccount() -> RxSwift.Single> { + convert( + task: repository.signoutCenterAccount() + ) + } + + // 요양 보호사 회원가입 실행 public func registerWorkerAccount(registerState: WorkerRegisterState) -> Single> { convert( task: repository.requestRegisterWorkerAccount(registerState: registerState)) } - /// 요양 보호사 로그인 실행 + // 요양 보호사 로그인 실행 public func loginWorkerAccount(phoneNumber: String, authNumber: String) -> Single> { convert( task: repository.requestWorkerLogin(phoneNumber: phoneNumber, authNumber: authNumber)) diff --git a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift b/project/Projects/Domain/Entity/Error/Auth/AuthError.swift index 8f77bc6f..ba40e0b6 100644 --- a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift +++ b/project/Projects/Domain/Entity/Error/Auth/AuthError.swift @@ -19,7 +19,7 @@ public enum AuthError: String, DomainError { case .accountAlreadyExist: "이미 존재하는 계정입니다." case .undefinedError: - "❌ 정의되지 않은 에러타입입니다. ❌" + "알 수 없는 에러가 발생했습니다." } } } diff --git a/project/Projects/Domain/RepositoryInterface/Auth/Login/AuthRepository.swift b/project/Projects/Domain/RepositoryInterface/Auth/Login/AuthRepository.swift index f73ac6da..cd37c419 100644 --- a/project/Projects/Domain/RepositoryInterface/Auth/Login/AuthRepository.swift +++ b/project/Projects/Domain/RepositoryInterface/Auth/Login/AuthRepository.swift @@ -9,10 +9,12 @@ import RxSwift import Entity public protocol AuthRepository: RepositoryBase { - + // MARK: Center func requestRegisterCenterAccount(managerName: String, phoneNumber: String, businessNumber: String, id: String, password: String) -> Single func requestCenterLogin(id: String, password: String) -> Single + func signoutCenterAccount() -> Single + func deregisterCenterAccount(reasons: [DeregisterReasonVO], password: String) -> Single // MARK: Worker func requestRegisterWorkerAccount(registerState: WorkerRegisterState) -> Single diff --git a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift index 604ab016..b1646868 100644 --- a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift @@ -13,10 +13,12 @@ import Entity /// - #1. 센터 회원가입 실행 /// - #2. 센터 로그인 실행 /// - #3. 샌터 회원 탈퇴 +/// - #4. 샌터 회원 로그아웃 /// -/// - #4. 요양보호사 회원가입 실행 -/// - #5. 요양보호사 로그인 실행 -/// - #5. 요양보호사 회원탈퇴 실행 +/// - #5. 요양보호사 회원가입 실행 +/// - #6. 요양보호사 로그인 실행 +/// - #7. 요양보호사 회원탈퇴 실행 +/// - #8. 요양보호사 로그아웃 public protocol AuthUseCase: UseCaseBase { @@ -38,13 +40,21 @@ public protocol AuthUseCase: UseCaseBase { password: String ) -> Single> + /// 센터 회원 탈퇴 + func deregisterCenterAccount( + reasons: [DeregisterReasonVO], + password: String + ) -> Single> + + /// 센터 로그아웃 + func signoutCenterAccount() -> Single> - // #4 + // #5 /// 요양 보호사 회원가입 실행 func registerWorkerAccount( registerState: WorkerRegisterState ) -> Single> - // #5 + // #6 /// 요양 보호사 로그인 실행 func loginWorkerAccount( phoneNumber: String, diff --git a/project/Projects/Presentation/DSKit/Sources/Component/TextField/IdleOneLineInputField.swift b/project/Projects/Presentation/DSKit/Sources/Component/TextField/IdleOneLineInputField.swift index 94510a33..dcd7715a 100644 --- a/project/Projects/Presentation/DSKit/Sources/Component/TextField/IdleOneLineInputField.swift +++ b/project/Projects/Presentation/DSKit/Sources/Component/TextField/IdleOneLineInputField.swift @@ -375,3 +375,9 @@ public extension IdleOneLineInputField { } extension IdleOneLineInputField: IdleKeyboardAvoidable { } + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + + IdleOneLineInputField(placeHolderText: "비밀번호를 입력해주세요") +} diff --git a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift index fd8f7361..7c3560ed 100644 --- a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift @@ -17,7 +17,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = scene as? UIWindowScene else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = DeregisterVC() + window?.rootViewController = UIViewController() window?.makeKeyAndVisible() } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift new file mode 100644 index 00000000..fb2707d0 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift @@ -0,0 +1,154 @@ +// +// PasswordForDeregisterVC.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import BaseFeature +import PresentationCore +import RxCocoa +import RxSwift +import Entity +import DSKit + +public class PasswordForDeregisterVM { + + let deregisterButtonClicked: PublishRelay = .init() + + + + init() { + + + } +} + +public class PasswordForDeregisterVC: BaseViewController { + + // Init + + // View + let navigationBar: IdleNavigationBar = { + let bar = IdleNavigationBar(innerViews: []) + bar.titleLabel.textString = "계정 삭제" + return bar + }() + let titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Heading1) + label.textAlignment = .left + label.numberOfLines = 2 + label.textString = "마지막으로\n비밀번호를 입력해주세요" + return label + }() + + let passwordField: IdleOneLineInputField = { + let field = IdleOneLineInputField(placeHolderText: "비밀번호를 입력해주세요") + return field + }() + + let finalWarningLabel: IdleLabel = { + let label = IdleLabel(typography: .caption) + label.textString = "탈퇴 버튼 선택 시 모든 정보가 삭제되며, 되돌릴 수 없습니다." + label.attrTextColor = DSColor.red100.color + return label + }() + + let cancelButton: IdleThirdinaryButton = { + let button = IdleThirdinaryButton(level: .medium) + button.label.textString = "취소하기" + return button + }() + + let acceptDeregisterButton: IdlePrimaryButton = { + let button = IdlePrimaryButton(level: .mediumRed) + button.label.textString = "탈퇴하기" + button.setEnabled(false) + return button + }() + + // Observable + private let disposeBag = DisposeBag() + + public init() { + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder: NSCoder) { fatalError() } + + public override func viewDidLoad() { + super.viewDidLoad() + setAppearance() + setLayout() + setObservable() + } + + private func setAppearance() { + view.backgroundColor = DSColor.gray0.color + } + + private func setLayout() { + + let passwordLabel = IdleLabel(typography: .Subtitle4) + passwordLabel.textString = "비밀번호" + passwordLabel.textColor = DSColor.gray500.color + passwordLabel.textAlignment = .left + + let textFieldStack = VStack( + [ + passwordLabel, + passwordField, + ], + spacing: 6, + alignment: .fill + ) + + let buttonStack = HStack( + [ + cancelButton, + acceptDeregisterButton + ], + spacing: 8, + alignment: .center, + distribution: .fillEqually + ) + + [ + navigationBar, + titleLabel, + textFieldStack, + finalWarningLabel, + buttonStack, + + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + } + + NSLayoutConstraint.activate([ + navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + navigationBar.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + navigationBar.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + + titleLabel.topAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: 24), + titleLabel.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + + textFieldStack.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 36), + textFieldStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + textFieldStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), + + finalWarningLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), + finalWarningLabel.bottomAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -12), + + buttonStack.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 20), + buttonStack.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -20), + buttonStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -14), + ]) + } + + private func setObservable() { + + } +} + From 52b1a23d1de4c0403c64823e3e09bd10b56a72e0 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 17:14:27 +0900 Subject: [PATCH 14/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 상태코드가 401인 경우가 무조건 토큰 만료가 아닐 수 있음으로, JWTError를 데이터 레이어에 구현하고 해당 에러인지 검사하도록했습니다. --- .../Service/BaseNetworkService.swift | 14 ++++++++- .../NetworkDataSource/Service/JWTError.swift | 31 +++++++++++++++++++ .../Domain/Entity/Error/Auth/AuthError.swift | 18 +++++++++-- .../{CustomError.swift => DomainError.swift} | 0 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 project/Projects/Data/NetworkDataSource/Service/JWTError.swift rename project/Projects/Domain/Entity/Error/Interface/{CustomError.swift => DomainError.swift} (100%) diff --git a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift index d9d0f9b6..e3b72c8b 100644 --- a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift +++ b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift @@ -96,7 +96,19 @@ public class BaseNetworkService { if let httpResponse = request.response { - if httpResponse.statusCode == 401 { + if httpResponse.statusCode == 401, let body = request.request?.httpBody { + + // 401이며, JWT오류인 경우만 토큰리프래쉬를 진행 + + guard let decodedError = try? JSONDecoder().decode(ErrorDTO.self, from: body), + let errorCode = decodedError.code + else { + return completion(.doNotRetry) + } + + guard let jwtError = JWTError(rawValue: errorCode) else { + return completion(.doNotRetry) + } guard let self else { fatalError() } diff --git a/project/Projects/Data/NetworkDataSource/Service/JWTError.swift b/project/Projects/Data/NetworkDataSource/Service/JWTError.swift new file mode 100644 index 00000000..abe74c41 --- /dev/null +++ b/project/Projects/Data/NetworkDataSource/Service/JWTError.swift @@ -0,0 +1,31 @@ +// +// JWTError.swift +// NetworkDataSource +// +// Created by choijunios on 8/21/24. +// + +import Foundation + +enum JWTError: String, Error { + case tokenDecodeException = "JWT-001" + case tokenNotValid = "JWT-002" + case tokenExpiredException = "JWT-003" + case tokenNotFound = "JWT-004" + case notSupportUserTokenType = "JWT-005" + + var message: String { + switch self { + case .tokenDecodeException: + return "유효하지 않은 토큰, 토큰을 디코딩할 때, JWT의 형식에 맞지 않는 경우 발생합니다." + case .tokenNotValid: + return "유효하지 않은 토큰, 토큰 내 값 검증에 실패한 경우 발생합니다. (ex. 알고리즘, 서명)" + case .tokenExpiredException: + return "토큰이 만료된 경우 발생합니다. 재 로그인이 필요합니다." + case .tokenNotFound: + return "토큰을 찾을 수 없는 경우 발생합니다." + case .notSupportUserTokenType: + return "지원하지 않는 유저 토큰 타입을 사용한 경우 발생할 수 있습니다. (carer, center 제외)" + } + } +} diff --git a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift b/project/Projects/Domain/Entity/Error/Auth/AuthError.swift index ba40e0b6..b9020532 100644 --- a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift +++ b/project/Projects/Domain/Entity/Error/Auth/AuthError.swift @@ -9,6 +9,12 @@ import Foundation public enum AuthError: String, DomainError { + // SECURITY + case unAuthorizedRequest = "SECURITY-001" + case invalidLoginRequest = "SECURITY-002" + case invalidPassword = "SECURITY-003" + case unregisteredUser = "SECURITY-004" + case accountAlreadyExist="CENTER-002" // undefinedError @@ -16,10 +22,18 @@ public enum AuthError: String, DomainError { public var message: String { switch self { + case .unAuthorizedRequest: + return "권한이 없습니다." + case .invalidLoginRequest: + return "아이디 혹은 비밀번호가 잘못됬습니다." + case .invalidPassword: + return "비밀번호가 틀렸습니다." + case .unregisteredUser: + return "등록되지 않은 사용자 입니다." case .accountAlreadyExist: - "이미 존재하는 계정입니다." + return "이미 존재하는 계정입니다." case .undefinedError: - "알 수 없는 에러가 발생했습니다." + return "알 수 없는 에러가 발생했습니다." } } } diff --git a/project/Projects/Domain/Entity/Error/Interface/CustomError.swift b/project/Projects/Domain/Entity/Error/Interface/DomainError.swift similarity index 100% rename from project/Projects/Domain/Entity/Error/Interface/CustomError.swift rename to project/Projects/Domain/Entity/Error/Interface/DomainError.swift From 6012786cafcbfb2bd27d56837bfca87c8f7ea47c Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 17:50:31 +0900 Subject: [PATCH 15/24] =?UTF-8?q?[IDLE-000]=20PasswordForDeregisterScreen?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Coordinator/DeRegisterCoordinator.swift | 28 +++++++- .../FinalPasswordAuhCoordinator.swift | 61 ++++++++++++++++ .../Deregister/DeregisterCoordinator.swift | 5 ++ .../Deregister/View/DeregisterReasonVC.swift | 5 ++ .../View/PasswordForDeregisterVC.swift | 50 ++++++++++---- .../ViewModel/CenterDeregisterReasonsVM.swift | 9 ++- .../ViewModel/PasswordForDeregisterVM.swift | 69 +++++++++++++++++++ 7 files changed, 213 insertions(+), 14 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift index 2d11a8c5..4bd6d9eb 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -8,11 +8,14 @@ import UIKit import Entity import PresentationCore +import UseCaseInterface public class DeRegisterCoordinator: DeregisterCoordinator { public struct Dependency { let userType: UserType + let parent: ParentCoordinator + let authUseCase: AuthUseCase let navigationController: UINavigationController } @@ -20,11 +23,16 @@ public class DeRegisterCoordinator: DeregisterCoordinator { public var navigationController: UINavigationController + public var parent: ParentCoordinator? + var viewControllerRef: UIViewController? let userType: UserType + let authUseCase: AuthUseCase public init(dependency: Dependency) { self.userType = dependency.userType + self.parent = dependency.parent + self.authUseCase = dependency.authUseCase self.navigationController = dependency.navigationController } @@ -43,12 +51,30 @@ public class DeRegisterCoordinator: DeregisterCoordinator { navigationController.pushViewController(vc, animated: true) } - public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { + public func flowFinished() { } + public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { + + let coordinator = FinalPasswordAuhCoordinator( + dependency: .init( + authUseCase: authUseCase, + reasons: reasons, + navigationController: navigationController + ) + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() + } + public func showFinalPhoneAuthScreen(reasons: [Entity.DeregisterReasonVO]) { } + public func coordinatorDidFinish() { + popViewController() + parent?.removeChildCoordinator(self) + } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift new file mode 100644 index 00000000..e7c39ead --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift @@ -0,0 +1,61 @@ +// +// FinalPasswordAuhCoordinator.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import PresentationCore +import UseCaseInterface +import Entity + +public class FinalPasswordAuhCoordinator: ChildCoordinator { + + public struct Dependency { + let authUseCase: AuthUseCase + let reasons: [DeregisterReasonVO] + let navigationController: UINavigationController + } + + public weak var viewControllerRef: UIViewController? + public weak var parent: DeRegisterCoordinator? + + public let navigationController: UINavigationController + let authUseCase: AuthUseCase + let reasons: [DeregisterReasonVO] + + public init( + dependency: Dependency + ) { + self.authUseCase = dependency.authUseCase + self.reasons = dependency.reasons + self.navigationController = dependency.navigationController + } + + deinit { + printIfDebug("\(String(describing: FinalPasswordAuhCoordinator.self))") + } + + public func start() { + let vc = PasswordForDeregisterVC() + let vm = PasswordForDeregisterVM( + deregisterReasons: reasons, + coordinator: self, + authUseCase: authUseCase + ) + vc.bind(viewModel: vm) + viewControllerRef = vc + navigationController.pushViewController(vc, animated: true) + } + + public func coordinatorDidFinish() { + popViewController() + parent?.removeChildCoordinator(self) + } + + public func flowFinished() { + parent?.flowFinished() + } +} + diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift index ee34f272..d0ed71f5 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift @@ -10,9 +10,14 @@ import Entity public protocol DeregisterCoordinator: ParentCoordinator { + /// 탈퇴과정이 끝났음을 알립니다. + func flowFinished() + /// 센터관리자: 마지막으로 비밀번호를 입력합니다. func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) /// 요양보호사: 마지막으로 전화번호를 입력합니다. func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) + + func coordinatorDidFinish() } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift index 678532b2..d68ced2e 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift @@ -16,6 +16,7 @@ import Entity public protocol DeregisterReasonVMable { var coordinator: DeregisterCoordinator? { get } + var exitButonClicked: PublishRelay { get } var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> { get } } @@ -207,6 +208,10 @@ public class DeregisterReasonVC: BaseViewController { .bind(to: viewModel.acceptDeregisterButonClicked) .disposed(by: disposeBag) + navigationBar.backButton + .rx.tap + .bind(to: viewModel.exitButonClicked) + .disposed(by: disposeBag) } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift index fb2707d0..3cb65ff9 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift @@ -8,27 +8,19 @@ import UIKit import BaseFeature import PresentationCore +import UseCaseInterface import RxCocoa import RxSwift import Entity import DSKit -public class PasswordForDeregisterVM { - - let deregisterButtonClicked: PublishRelay = .init() - - - - init() { - - - } -} - public class PasswordForDeregisterVC: BaseViewController { // Init + // Not init + var viewModel: PasswordForDeregisterVM? + // View let navigationBar: IdleNavigationBar = { let bar = IdleNavigationBar(innerViews: []) @@ -148,7 +140,41 @@ public class PasswordForDeregisterVC: BaseViewController { } private func setObservable() { + passwordField + .eventPublisher + .map { password in + password.count > 0 + } + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { [weak self] isValid in + self?.acceptDeregisterButton.setEnabled(isValid) + }) + .disposed(by: disposeBag) + } + + public func bind(viewModel: PasswordForDeregisterVM) { + + self.viewModel = viewModel + + // Input + acceptDeregisterButton + .rx.tap + .withLatestFrom(passwordField.eventPublisher) + .bind(to: viewModel.deregisterButtonClicked) + .disposed(by: disposeBag) + + navigationBar + .backButton.rx.tap + .bind(to: viewModel.exitButtonClicked) + .disposed(by: disposeBag) + // Output + viewModel + .alert? + .drive(onNext: { [weak self] alertVO in + self?.showAlert(vo: alertVO) + }) + .disposed(by: disposeBag) } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift index 2ee3a3b2..0acb4185 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift @@ -11,7 +11,8 @@ import RxCocoa public class CenterDeregisterReasonsVM: DeregisterReasonVMable { - public var coordinator: (any DeregisterCoordinator)? + public weak var coordinator: (any DeregisterCoordinator)? + public var exitButonClicked: RxRelay.PublishRelay = .init() public var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> = .init() let disposeBag = DisposeBag() @@ -24,5 +25,11 @@ public class CenterDeregisterReasonsVM: DeregisterReasonVMable { self?.coordinator?.showFinalPasswordScreen(reasons: reasons) }) .disposed(by: disposeBag) + + exitButonClicked + .subscribe(onNext: { [weak self] reasons in + self?.coordinator?.coordinatorDidFinish() + }) + .disposed(by: disposeBag) } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift new file mode 100644 index 00000000..c4e39bc0 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift @@ -0,0 +1,69 @@ +// +// PasswordForDeregisterVM.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import BaseFeature +import UseCaseInterface +import RxCocoa +import RxSwift +import Entity + +public class PasswordForDeregisterVM: DefaultAlertOutputable { + + public weak var coordinator: FinalPasswordAuhCoordinator? + + public let deregisterButtonClicked: PublishRelay = .init() + public let exitButtonClicked: PublishRelay = .init() + public var alert: RxCocoa.Driver? + + let authUseCase: AuthUseCase + + let disposeBag = DisposeBag() + + public init( + deregisterReasons: [DeregisterReasonVO], + coordinator: FinalPasswordAuhCoordinator, + authUseCase: AuthUseCase + ) { + self.coordinator = coordinator + self.authUseCase = authUseCase + + let deregisterResult = deregisterButtonClicked + .flatMap { [authUseCase] password in + authUseCase.deregisterCenterAccount( + reasons: deregisterReasons, + password: password + ) + } + .share() + + let deregisterSuccess = deregisterResult.compactMap { $0.value } + let deregisterFailure = deregisterResult.compactMap { $0.error } + + deregisterSuccess + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { [weak self] _ in + self?.coordinator?.flowFinished() + }) + .disposed(by: disposeBag) + + exitButtonClicked + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] _ in + self?.coordinator?.coordinatorDidFinish() + }) + .disposed(by: disposeBag) + + alert = deregisterFailure + .map { error in + DefaultAlertContentVO( + title: "회원탈퇴 실패", + message: error.message + ) + } + .asDriver(onErrorJustReturn: .default) + } +} From 8538a61fc3bacf6f6da9feb7c76a83a5ccaf6eff Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Wed, 21 Aug 2024 18:34:28 +0900 Subject: [PATCH 16/24] =?UTF-8?q?[IDLE-000]=20=EC=BD=94=EB=94=94=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/BaseNetworkService.swift | 17 +---- .../ExampleApp/Sources/SceneDelegate.swift | 13 ++-- .../Coordinator/DeRegisterCoordinator.swift | 53 ++++++++++---- .../FinalPasswordAuhCoordinator.swift | 0 .../Sub/SelectReasonCoordinator.swift | 72 +++++++++++++++++++ .../Deregister/DeregisterCoordinator.swift | 23 ------ .../Deregister/View/DeregisterReasonVC.swift | 11 ++- .../ViewModel/CenterDeregisterReasonsVM.swift | 6 +- 8 files changed, 131 insertions(+), 64 deletions(-) rename project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/{ => Sub}/FinalPasswordAuhCoordinator.swift (100%) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift diff --git a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift index e3b72c8b..d91ed342 100644 --- a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift +++ b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift @@ -96,21 +96,10 @@ public class BaseNetworkService { if let httpResponse = request.response { - if httpResponse.statusCode == 401, let body = request.request?.httpBody { + if httpResponse.statusCode == 401 { - // 401이며, JWT오류인 경우만 토큰리프래쉬를 진행 - - guard let decodedError = try? JSONDecoder().decode(ErrorDTO.self, from: body), - let errorCode = decodedError.code - else { - return completion(.doNotRetry) - } - - guard let jwtError = JWTError(rawValue: errorCode) else { - return completion(.doNotRetry) - } - - guard let self else { fatalError() } + // 401이며 + guard let self else { return completion(.doNotRetry) } guard let refreshToken = self.keyValueStore.getAuthToken()?.refreshToken else { completion(.doNotRetry) diff --git a/project/Projects/Presentation/Feature/Root/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Root/ExampleApp/Sources/SceneDelegate.swift index 71ab8589..7349c2fa 100644 --- a/project/Projects/Presentation/Feature/Root/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Root/ExampleApp/Sources/SceneDelegate.swift @@ -15,7 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - var coordinator: RegisterRecruitmentPostCoordinator! + var coordinator: DeRegisterCoordinator! func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -31,12 +31,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let nav = UINavigationController() nav.setNavigationBarHidden(true, animated: false) - self.coordinator = RegisterRecruitmentPostCoordinator( + self.coordinator = DeRegisterCoordinator( dependency: .init( - navigationController: nav, - recruitmentPostUseCase: DefaultRecruitmentPostUseCase( - repository: DefaultRecruitmentPostRepository(store) - ) + userType: .center, + authUseCase: DefaultAuthUseCase( + repository: DefaultAuthRepository() + ), + navigationController: nav ) ) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift index 4bd6d9eb..6ad7c7f9 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -10,13 +10,35 @@ import Entity import PresentationCore import UseCaseInterface -public class DeRegisterCoordinator: DeregisterCoordinator { +public protocol DeregisterCoordinatable: ParentCoordinator { + + /// 탈퇴과정이 끝났음을 알립니다. + func flowFinished() + + /// 공통: 탈퇴 이유를 선택합니다. + func showSelectReasonScreen() + + /// 센터관리자: 마지막으로 비밀번호를 입력합니다. + func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) + + /// 요양보호사: 마지막으로 전화번호를 입력합니다. + func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) + + func coordinatorDidFinish() +} + +public class DeRegisterCoordinator: DeregisterCoordinatable { public struct Dependency { let userType: UserType - let parent: ParentCoordinator let authUseCase: AuthUseCase let navigationController: UINavigationController + + public init(userType: UserType, authUseCase: AuthUseCase, navigationController: UINavigationController) { + self.userType = userType + self.authUseCase = authUseCase + self.navigationController = navigationController + } } public var childCoordinators: [any Coordinator] = [] @@ -31,30 +53,31 @@ public class DeRegisterCoordinator: DeregisterCoordinator { public init(dependency: Dependency) { self.userType = dependency.userType - self.parent = dependency.parent self.authUseCase = dependency.authUseCase self.navigationController = dependency.navigationController } public func start() { - var vm: DeregisterReasonVMable! - switch userType { - case .center: - vm = CenterDeregisterReasonsVM(coordinator: self) - case .worker: - fatalError() - } - - let vc = DeregisterReasonVC() - vc.bind(viewModel: vm) - viewControllerRef = vc - navigationController.pushViewController(vc, animated: true) + showSelectReasonScreen() } public func flowFinished() { } + public func showSelectReasonScreen() { + let coordinator: SelectReasonCoordinator = .init( + dependency: .init( + userType: userType, + authUseCase: authUseCase, + navigationController: navigationController + ) + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() + } + public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { let coordinator = FinalPasswordAuhCoordinator( diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/FinalPasswordAuhCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift new file mode 100644 index 00000000..92876027 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift @@ -0,0 +1,72 @@ +// +// SelectReasonCoordinator.swift +// RootFeature +// +// Created by choijunios on 8/21/24. +// + +import UIKit +import PresentationCore +import UseCaseInterface +import Entity + +public class SelectReasonCoordinator: ChildCoordinator { + + public struct Dependency { + let userType: UserType + let authUseCase: AuthUseCase + let navigationController: UINavigationController + + public init(userType: UserType, authUseCase: AuthUseCase, navigationController: UINavigationController) { + self.userType = userType + self.authUseCase = authUseCase + self.navigationController = navigationController + } + } + + public weak var viewControllerRef: UIViewController? + public var navigationController: UINavigationController + public weak var parent: DeregisterCoordinatable? + + let userType: UserType + let authUseCase: AuthUseCase + + public init(dependency: Dependency) { + self.userType = dependency.userType + self.authUseCase = dependency.authUseCase + self.navigationController = dependency.navigationController + } + + deinit { + printIfDebug("\(String(describing: SelectReasonCoordinator.self))") + } + + public func start() { + var vm: DeregisterReasonVMable! + switch userType { + case .center: + vm = CenterDeregisterReasonsVM(coordinator: self) + case .worker: + fatalError() + } + + let vc = DeregisterReasonVC() + vc.bind(viewModel: vm) + viewControllerRef = vc + navigationController.pushViewController(vc, animated: true) + } + + public func coordinatorDidFinish() { + popViewController() + parent?.removeChildCoordinator(self) + } + + public func showPasswordAuthScreen(reasons: [DeregisterReasonVO]) { + parent?.showFinalPasswordScreen(reasons: reasons) + } + + public func showPhoneNumberAuthScreen() { + + } +} + diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift deleted file mode 100644 index d0ed71f5..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/DeregisterCoordinator.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// DeregisterCoordinator.swift -// RootFeature -// -// Created by choijunios on 8/21/24. -// - -import PresentationCore -import Entity - -public protocol DeregisterCoordinator: ParentCoordinator { - - /// 탈퇴과정이 끝났음을 알립니다. - func flowFinished() - - /// 센터관리자: 마지막으로 비밀번호를 입력합니다. - func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) - - /// 요양보호사: 마지막으로 전화번호를 입력합니다. - func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) - - func coordinatorDidFinish() -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift index d68ced2e..01fecf65 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift @@ -15,7 +15,7 @@ import DSKit import Entity public protocol DeregisterReasonVMable { - var coordinator: DeregisterCoordinator? { get } + var coordinator: SelectReasonCoordinator? { get } var exitButonClicked: PublishRelay { get } var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> { get } } @@ -197,6 +197,8 @@ public class DeregisterReasonVC: BaseViewController { public func bind(viewModel: DeregisterReasonVMable) { + self.viewModel = viewModel + acceptDeregisterButton .rx.tap .map { [weak self] _ in @@ -208,8 +210,11 @@ public class DeregisterReasonVC: BaseViewController { .bind(to: viewModel.acceptDeregisterButonClicked) .disposed(by: disposeBag) - navigationBar.backButton - .rx.tap + Observable + .merge( + cancelButton.rx.tap.asObservable(), + navigationBar.backButton.rx.tap.asObservable() + ) .bind(to: viewModel.exitButonClicked) .disposed(by: disposeBag) } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift index 0acb4185..8e068553 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift @@ -11,18 +11,18 @@ import RxCocoa public class CenterDeregisterReasonsVM: DeregisterReasonVMable { - public weak var coordinator: (any DeregisterCoordinator)? + public weak var coordinator: SelectReasonCoordinator? public var exitButonClicked: RxRelay.PublishRelay = .init() public var acceptDeregisterButonClicked: PublishRelay<[DeregisterReasonVO]> = .init() let disposeBag = DisposeBag() - public init(coordinator: DeregisterCoordinator?) { + public init(coordinator: SelectReasonCoordinator) { self.coordinator = coordinator acceptDeregisterButonClicked .subscribe(onNext: { [weak self] reasons in - self?.coordinator?.showFinalPasswordScreen(reasons: reasons) + self?.coordinator?.showPasswordAuthScreen(reasons: reasons) }) .disposed(by: disposeBag) From 69a00eda7a680e79f0ad63e95c4009c41a58dff9 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 09:36:15 +0900 Subject: [PATCH 17/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EA=B5=AC=EC=A1=B0=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PasswordForDeregisterCoordinator.swift} | 16 ++++++++---- .../Setting}/PasswordForDeregisterVC.swift | 2 +- .../ViewModel/Setting/CenterSettingVM.swift | 9 ++++--- .../Setting}/PasswordForDeregisterVM.swift | 6 ++--- .../CenterDeregisterReasonsVM.swift | 0 .../Coordinator/DeRegisterCoordinator.swift | 20 ++------------ .../Sub/SelectReasonCoordinator.swift | 0 .../Deregister}/DeregisterReasonVC.swift | 0 .../TabBar/IdleCenterMainPage.swift | 0 .../{ => Common}/TabBar/IdleTabBar.swift | 0 .../TabBar/IdleWorkerMainPage.swift | 0 .../Setting/DeregisterCoordinatable.swift | 26 +++++++++++++++++++ 12 files changed, 48 insertions(+), 31 deletions(-) rename project/Projects/Presentation/Feature/{Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift => Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift} (70%) rename project/Projects/Presentation/Feature/{Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View => Center/Sources/View/Setting}/PasswordForDeregisterVC.swift (99%) rename project/Projects/Presentation/Feature/{Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel => Center/Sources/ViewModel/Setting}/PasswordForDeregisterVM.swift (93%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Worker/Coordinator/Setting/Deregister/ViewModel => Common/Setting/Deregister}/CenterDeregisterReasonsVM.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Worker/Coordinator => Common}/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift (79%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Worker/Coordinator => Common}/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Worker/Coordinator/Setting/Deregister/View => Common/Setting/Deregister}/DeregisterReasonVC.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{ => Common}/TabBar/IdleCenterMainPage.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{ => Common}/TabBar/IdleTabBar.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{ => Common}/TabBar/IdleWorkerMainPage.swift (100%) create mode 100644 project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift similarity index 70% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift rename to project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift index e7c39ead..b5991561 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/FinalPasswordAuhCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift @@ -1,6 +1,6 @@ // -// FinalPasswordAuhCoordinator.swift -// RootFeature +// PasswordForDeregisterCoordinator.swift +// CenterFeature // // Created by choijunios on 8/21/24. // @@ -10,16 +10,22 @@ import PresentationCore import UseCaseInterface import Entity -public class FinalPasswordAuhCoordinator: ChildCoordinator { +public class PasswordForDeregisterCoordinator: ChildCoordinator { public struct Dependency { let authUseCase: AuthUseCase let reasons: [DeregisterReasonVO] let navigationController: UINavigationController + + public init(authUseCase: AuthUseCase, reasons: [DeregisterReasonVO], navigationController: UINavigationController) { + self.authUseCase = authUseCase + self.reasons = reasons + self.navigationController = navigationController + } } public weak var viewControllerRef: UIViewController? - public weak var parent: DeRegisterCoordinator? + public weak var parent: DeregisterCoordinatable? public let navigationController: UINavigationController let authUseCase: AuthUseCase @@ -34,7 +40,7 @@ public class FinalPasswordAuhCoordinator: ChildCoordinator { } deinit { - printIfDebug("\(String(describing: FinalPasswordAuhCoordinator.self))") + printIfDebug("\(String(describing: PasswordForDeregisterCoordinator.self))") } public func start() { diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/PasswordForDeregisterVC.swift similarity index 99% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift rename to project/Projects/Presentation/Feature/Center/Sources/View/Setting/PasswordForDeregisterVC.swift index 3cb65ff9..d0ab375e 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/PasswordForDeregisterVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/PasswordForDeregisterVC.swift @@ -1,6 +1,6 @@ // // PasswordForDeregisterVC.swift -// RootFeature +// CenterFeature // // Created by choijunios on 8/21/24. // diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index fac2ee68..5e15dec3 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -45,6 +45,11 @@ public protocol CenterSettingVMable { public class CenterSettingVM: CenterSettingVMable { + // Init + weak var coordinator: CenterSettingScreenCoordinatable? + let settingUseCase: SettingScreenUseCase + let centerProfileUseCase: CenterProfileUseCase + public var viewWillAppear: RxRelay.PublishRelay = .init() public var myCenterProfileButtonClicked: RxRelay.PublishRelay = .init() public var approveToPushNotification: RxRelay.PublishRelay = .init() @@ -59,10 +64,6 @@ public class CenterSettingVM: CenterSettingVMable { let disposeBag = DisposeBag() - weak var coordinator: CenterSettingScreenCoordinatable? - let settingUseCase: SettingScreenUseCase - let centerProfileUseCase: CenterProfileUseCase - public init( coordinator: CenterSettingScreenCoordinatable?, settingUseCase: SettingScreenUseCase, diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift similarity index 93% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift rename to project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift index c4e39bc0..fc0a066c 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/PasswordForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift @@ -1,6 +1,6 @@ // // PasswordForDeregisterVM.swift -// RootFeature +// CenterFeature // // Created by choijunios on 8/21/24. // @@ -13,7 +13,7 @@ import Entity public class PasswordForDeregisterVM: DefaultAlertOutputable { - public weak var coordinator: FinalPasswordAuhCoordinator? + public weak var coordinator: PasswordForDeregisterCoordinator? public let deregisterButtonClicked: PublishRelay = .init() public let exitButtonClicked: PublishRelay = .init() @@ -25,7 +25,7 @@ public class PasswordForDeregisterVM: DefaultAlertOutputable { public init( deregisterReasons: [DeregisterReasonVO], - coordinator: FinalPasswordAuhCoordinator, + coordinator: PasswordForDeregisterCoordinator, authUseCase: AuthUseCase ) { self.coordinator = coordinator diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/CenterDeregisterReasonsVM.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/ViewModel/CenterDeregisterReasonsVM.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/CenterDeregisterReasonsVM.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift similarity index 79% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift index 6ad7c7f9..f5f67f5b 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -7,26 +7,10 @@ import UIKit import Entity +import CenterFeature import PresentationCore import UseCaseInterface -public protocol DeregisterCoordinatable: ParentCoordinator { - - /// 탈퇴과정이 끝났음을 알립니다. - func flowFinished() - - /// 공통: 탈퇴 이유를 선택합니다. - func showSelectReasonScreen() - - /// 센터관리자: 마지막으로 비밀번호를 입력합니다. - func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) - - /// 요양보호사: 마지막으로 전화번호를 입력합니다. - func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) - - func coordinatorDidFinish() -} - public class DeRegisterCoordinator: DeregisterCoordinatable { public struct Dependency { @@ -80,7 +64,7 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { public func showFinalPasswordScreen(reasons: [Entity.DeregisterReasonVO]) { - let coordinator = FinalPasswordAuhCoordinator( + let coordinator = PasswordForDeregisterCoordinator( dependency: .init( authUseCase: authUseCase, reasons: reasons, diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/DeregisterReasonVC.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/Setting/Deregister/View/DeregisterReasonVC.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/DeregisterReasonVC.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleCenterMainPage.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleCenterMainPage.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleCenterMainPage.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleCenterMainPage.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleTabBar.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleTabBar.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleTabBar.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleTabBar.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleWorkerMainPage.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleWorkerMainPage.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleWorkerMainPage.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleWorkerMainPage.swift diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift new file mode 100644 index 00000000..8e99e1fb --- /dev/null +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift @@ -0,0 +1,26 @@ +// +// asd.swift +// PresentationCore +// +// Created by choijunios on 8/25/24. +// + +import Foundation +import Entity + +public protocol DeregisterCoordinatable: ParentCoordinator { + + /// 탈퇴과정이 끝났음을 알립니다. + func flowFinished() + + /// 공통: 탈퇴 이유를 선택합니다. + func showSelectReasonScreen() + + /// 센터관리자: 마지막으로 비밀번호를 입력합니다. + func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) + + /// 요양보호사: 마지막으로 전화번호를 입력합니다. + func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) + + func coordinatorDidFinish() +} From 67d3e6d1b6c658630612f29007ef3aa6782cc774 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 10:19:07 +0900 Subject: [PATCH 18/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Main/Worker/WorkerMainCoordinator.swift | 4 +- .../DefaultAuthInputValidationUseCase.swift | 8 +- .../Auth/DefaultAuthUseCase.swift | 12 +- .../DefualtRecruitmentPostUseCase.swift | 10 +- .../DefaultCenterProfileUseCase.swift | 6 +- .../DefaultWorkerProfileUseCase.swift | 4 +- .../Domain/Entity/Error/Auth/AuthError.swift | 39 ---- .../Error/Auth/InputValidationError.swift | 39 ---- .../Domain/Entity/Error/DomainError.swift | 203 ++++++++++++++++++ .../Entity/Error/{ => Etc}/StoreError.swift | 0 .../Entity/Error/Interface/DomainError.swift | 15 -- .../RecruitmentPostError.swift | 21 -- .../Entity/Error/UserInfo/UserInfoError.swift | 28 --- .../Employ}/CenterEmployCardVO.swift | 0 .../Auth/AuthInputValidationUseCase.swift | 8 +- .../UseCaseInterface/Auth/AuthUseCase.swift | 12 +- .../RecruitmentPostUseCase.swift | 10 +- .../Domain/UseCaseInterface/UseCaseBase.swift | 16 +- .../UserInfo/CenterProfileUseCase.swift | 6 +- .../UserInfo/WorkerProfileUseCase.swift | 4 +- .../AuthInOutStreamManager+IdPassword.swift | 2 +- .../AuthInOutStreamManager+PhoneNumber.swift | 6 +- .../CheckApplicant/CheckApplicantVM.swift | 2 +- .../CenterRecruitmentPostBoardVM.swift | 4 +- .../RecruitmentPost/EditPostVM.swift | 2 +- .../RegisterRecruitmentPostVM.swift | 2 +- .../RegisterCenterInfoVM.swift | 2 +- .../Profile/WorkerMyProfileViewModel.swift | 4 +- .../Profile/WorkerProfileViewModel.swift | 2 +- .../RecruitmentPost/AppliedPostBoardVM.swift | 2 +- .../RecruitmentPost/StarredPostBoardVM.swift | 2 +- 31 files changed, 271 insertions(+), 204 deletions(-) delete mode 100644 project/Projects/Domain/Entity/Error/Auth/AuthError.swift delete mode 100644 project/Projects/Domain/Entity/Error/Auth/InputValidationError.swift create mode 100644 project/Projects/Domain/Entity/Error/DomainError.swift rename project/Projects/Domain/Entity/Error/{ => Etc}/StoreError.swift (100%) delete mode 100644 project/Projects/Domain/Entity/Error/Interface/DomainError.swift delete mode 100644 project/Projects/Domain/Entity/Error/RecruitmentPost/RecruitmentPostError.swift delete mode 100644 project/Projects/Domain/Entity/Error/UserInfo/UserInfoError.swift rename project/Projects/Domain/Entity/{Error/RecruitmentPost => VO/Employ}/CenterEmployCardVO.swift (100%) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift index d2f7e0c3..729f68a6 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift @@ -87,9 +87,7 @@ class WorkerMainCoordinator: ParentCoordinator { ) ) case .setting: - coordinator = WorkerSettingCoordinator( - navigationController: navigationController - ) + fatalError() } addChildCoordinator(coordinator) diff --git a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthInputValidationUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthInputValidationUseCase.swift index 9603fb55..7998fb36 100644 --- a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthInputValidationUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthInputValidationUseCase.swift @@ -26,7 +26,7 @@ public class DefaultAuthInputValidationUseCase: AuthInputValidationUseCase { // MARK: 전화번호 인증 - public func requestPhoneNumberAuthentication(phoneNumber: String) -> Single> { + public func requestPhoneNumberAuthentication(phoneNumber: String) -> Single> { convert(task: self.repository .requestPhoneNumberAuthentication(phoneNumber: phoneNumber) .map { _ in phoneNumber } @@ -40,7 +40,7 @@ public class DefaultAuthInputValidationUseCase: AuthInputValidationUseCase { return predicate.evaluate(with: phoneNumber) } - public func authenticateAuthNumber(phoneNumber: String, authNumber: String) -> Single> { + public func authenticateAuthNumber(phoneNumber: String, authNumber: String) -> Single> { convert(task: repository .authenticateAuthNumber(phoneNumber: phoneNumber, authNumber: authNumber) .map({ _ in phoneNumber }) @@ -48,7 +48,7 @@ public class DefaultAuthInputValidationUseCase: AuthInputValidationUseCase { } // MARK: 사업자 번호 인증 - public func requestBusinessNumberAuthentication(businessNumber: String) -> Single> { + public func requestBusinessNumberAuthentication(businessNumber: String) -> Single> { convert(task: repository .requestBusinessNumberAuthentication(businessNumber: businessNumber) .map({ vo in (businessNumber, vo) }) @@ -70,7 +70,7 @@ public class DefaultAuthInputValidationUseCase: AuthInputValidationUseCase { return predicate.evaluate(with: id) } - public func requestCheckingIdDuplication(id: String) -> Single> { + public func requestCheckingIdDuplication(id: String) -> Single> { convert(task: repository .requestCheckingIdDuplication(id: id) .map({ _ in id }) diff --git a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift index 16900150..f2a51a28 100644 --- a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift @@ -20,7 +20,7 @@ public class DefaultAuthUseCase: AuthUseCase { } // 센터 회원가입 실행 - public func registerCenterAccount(registerState: CenterRegisterState) -> Single> { + public func registerCenterAccount(registerState: CenterRegisterState) -> Single> { convert( task: repository.requestRegisterCenterAccount( managerName: registerState.name, @@ -32,32 +32,32 @@ public class DefaultAuthUseCase: AuthUseCase { } // 센터 로그인 실행 - public func loginCenterAccount(id: String, password: String) -> Single> { + public func loginCenterAccount(id: String, password: String) -> Single> { convert(task: repository.requestCenterLogin(id: id, password: password)) } // 센터 회원탈퇴 - public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { + public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { convert( task: repository.deregisterCenterAccount(reasons: reasons, password: password) ) } // 센터 로그아웃 - public func signoutCenterAccount() -> RxSwift.Single> { + public func signoutCenterAccount() -> RxSwift.Single> { convert( task: repository.signoutCenterAccount() ) } // 요양 보호사 회원가입 실행 - public func registerWorkerAccount(registerState: WorkerRegisterState) -> Single> { + public func registerWorkerAccount(registerState: WorkerRegisterState) -> Single> { convert( task: repository.requestRegisterWorkerAccount(registerState: registerState)) } // 요양 보호사 로그인 실행 - public func loginWorkerAccount(phoneNumber: String, authNumber: String) -> Single> { + public func loginWorkerAccount(phoneNumber: String, authNumber: String) -> Single> { convert( task: repository.requestWorkerLogin(phoneNumber: phoneNumber, authNumber: authNumber)) } diff --git a/project/Projects/Domain/ConcreteUseCase/RecruitmentPost/DefualtRecruitmentPostUseCase.swift b/project/Projects/Domain/ConcreteUseCase/RecruitmentPost/DefualtRecruitmentPostUseCase.swift index d78b8046..295653ae 100644 --- a/project/Projects/Domain/ConcreteUseCase/RecruitmentPost/DefualtRecruitmentPostUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/RecruitmentPost/DefualtRecruitmentPostUseCase.swift @@ -19,7 +19,7 @@ public class DefaultRecruitmentPostUseCase: RecruitmentPostUseCase { self.repository = repository } - public func registerRecruitmentPost(inputs: RegisterRecruitmentPostBundle) -> Single> { + public func registerRecruitmentPost(inputs: RegisterRecruitmentPostBundle) -> Single> { // 마감기간이 지정되지 않는 경우 현재로 부터 한달 후로 설정 if inputs.applicationDetail.applyDeadlineType == .untilApplicationFinished { @@ -34,7 +34,7 @@ public class DefaultRecruitmentPostUseCase: RecruitmentPostUseCase { ) } - public func editRecruitmentPost(id: String, inputs: Entity.RegisterRecruitmentPostBundle) -> RxSwift.Single> { + public func editRecruitmentPost(id: String, inputs: Entity.RegisterRecruitmentPostBundle) -> RxSwift.Single> { if inputs.applicationDetail.applyDeadlineType == .untilApplicationFinished { let oneMonthLater = Calendar.current.date(byAdding: .month, value: 1, to: Date()) @@ -49,15 +49,15 @@ public class DefaultRecruitmentPostUseCase: RecruitmentPostUseCase { ) } - public func getPostDetailForCenter(id: String) -> RxSwift.Single> { + public func getPostDetailForCenter(id: String) -> RxSwift.Single> { convert(task: repository.getPostDetailForCenter(id: id)) } - public func getPostDetailForWorker(id: String) -> RxSwift.Single> { + public func getPostDetailForWorker(id: String) -> RxSwift.Single> { convert(task: repository.getPostDetailForWorker(id: id)) } - public func getPostListForWorker(request: PostPagingRequestForWorker, postCount: Int) -> Single> { + public func getPostListForWorker(request: PostPagingRequestForWorker, postCount: Int) -> Single> { let stream: Single! diff --git a/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultCenterProfileUseCase.swift b/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultCenterProfileUseCase.swift index 5d1fe9ef..5ab4355b 100644 --- a/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultCenterProfileUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultCenterProfileUseCase.swift @@ -19,11 +19,11 @@ public class DefaultCenterProfileUseCase: CenterProfileUseCase { self.repository = repository } - public func getProfile(mode: ProfileMode) -> Single> { + public func getProfile(mode: ProfileMode) -> Single> { convert(task: repository.getCenterProfile(mode: mode)) } - public func updateProfile(phoneNumber: String?, introduction: String?, imageInfo: ImageUploadInfo?) -> Single> { + public func updateProfile(phoneNumber: String?, introduction: String?, imageInfo: ImageUploadInfo?) -> Single> { var updateText: Single! var updateImage: Single! @@ -85,7 +85,7 @@ public class DefaultCenterProfileUseCase: CenterProfileUseCase { return convert(task: task) } - public func registerCenterProfile(state: CenterProfileRegisterState) -> Single> { + public func registerCenterProfile(state: CenterProfileRegisterState) -> Single> { var registerImage: Single! diff --git a/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultWorkerProfileUseCase.swift b/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultWorkerProfileUseCase.swift index faa488b3..9207dd2c 100644 --- a/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultWorkerProfileUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/UserInfo/DefaultWorkerProfileUseCase.swift @@ -19,11 +19,11 @@ public class DefaultWorkerProfileUseCase: WorkerProfileUseCase { self.repository = repository } - public func getProfile(mode: ProfileMode) -> Single> { + public func getProfile(mode: ProfileMode) -> Single> { convert(task: repository.getWorkerProfile(mode: mode)) } - public func updateProfile(stateObject: WorkerProfileStateObject, imageInfo: ImageUploadInfo?) -> Single> { + public func updateProfile(stateObject: WorkerProfileStateObject, imageInfo: ImageUploadInfo?) -> Single> { var updateText: Single! var updateImage: Single! diff --git a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift b/project/Projects/Domain/Entity/Error/Auth/AuthError.swift deleted file mode 100644 index b9020532..00000000 --- a/project/Projects/Domain/Entity/Error/Auth/AuthError.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// AuthError.swift -// Entity -// -// Created by choijunios on 7/10/24. -// - -import Foundation - -public enum AuthError: String, DomainError { - - // SECURITY - case unAuthorizedRequest = "SECURITY-001" - case invalidLoginRequest = "SECURITY-002" - case invalidPassword = "SECURITY-003" - case unregisteredUser = "SECURITY-004" - - case accountAlreadyExist="CENTER-002" - - // undefinedError - case undefinedError="Err-000" - - public var message: String { - switch self { - case .unAuthorizedRequest: - return "권한이 없습니다." - case .invalidLoginRequest: - return "아이디 혹은 비밀번호가 잘못됬습니다." - case .invalidPassword: - return "비밀번호가 틀렸습니다." - case .unregisteredUser: - return "등록되지 않은 사용자 입니다." - case .accountAlreadyExist: - return "이미 존재하는 계정입니다." - case .undefinedError: - return "알 수 없는 에러가 발생했습니다." - } - } -} diff --git a/project/Projects/Domain/Entity/Error/Auth/InputValidationError.swift b/project/Projects/Domain/Entity/Error/Auth/InputValidationError.swift deleted file mode 100644 index 151b312d..00000000 --- a/project/Projects/Domain/Entity/Error/Auth/InputValidationError.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// InputValidationError.swift -// Entity -// -// Created by choijunios on 7/10/24. -// - -import Foundation - -public enum InputValidationError: String, DomainError { - - case InvalidSmsVerificationNumber="SMS-001" - case SmsVerificationNumberNotFound="SMS-002" - case ClientException="SMS-003" - - case ExternalApiException="CLIENT-001" - case CompanyNotFoundException="CLIENT-002" - - // undefinedError - case undefinedError="Err-000" - - public var message: String { - switch self { - case .InvalidSmsVerificationNumber: - "전화번호 인증 시, 잘못된 인증번호를 입력한 경우 발생합니다." - case .SmsVerificationNumberNotFound: - "전화번호 인증 시, 인증번호가 만료되었거나 존재하지 않는 경우 발생합니다." - case .ClientException: - "SMS 문자 발송에 실패한 경우 발생합니다." - case .ExternalApiException: - "외부 API에서 알 수 없는 문제가 발생한 경우를 모두 포함합니다." - case .CompanyNotFoundException: - "사업자 등록번호의 조회 결과가 없는 경우 발생합니다." - // MARK: undefinedError - case .undefinedError: - "❌ 정의되지 않은 에러타입입니다. ❌" - } - } -} diff --git a/project/Projects/Domain/Entity/Error/DomainError.swift b/project/Projects/Domain/Entity/Error/DomainError.swift new file mode 100644 index 00000000..c465cd9b --- /dev/null +++ b/project/Projects/Domain/Entity/Error/DomainError.swift @@ -0,0 +1,203 @@ +// +// DomainError.swift +// Entity +// +// Created by choijunios on 8/25/24. +// + +import Foundation + +public enum DomainError: Error { + + // API + case invalidParameter + + // SECURITY + case unAuthorizedRequest + case invalidLoginRequest + case invalidPassword + case unregisteredUser + + // System + case internalServerError + + // JWT + case tokenDecodeException + /// 리프래쉬필요 + case tokenNotValid + /// 재로그인 필요 + case tokenExpiredException + case tokenNotFound + case notSupportUserTokenType + + // User + case invalidVerificationNumber + case verificationNumberNotFound + case imageUploadNotCompleted + + // Center + case duplicateIdentifier + case alreadyExistCenterManager + case alreadyExistCenter + case centerNotFoundException + + // Carer + case alreadyExistCarer + + // Persistence + case resourceNotFound + + // SMS + case clientException + + // Business Registration + case businessCodeNotFound + + // Geocoding + case geoCodingFailure + + // undefinedError + case undefinedCode + case undefinedError + + public init(code: String) { + switch code { + case "API-001": + self = .invalidParameter + + case "SECURITY-001": + self = .unAuthorizedRequest + case "SECURITY-002": + self = .invalidLoginRequest + case "SECURITY-003": + self = .invalidPassword + case "SECURITY-004": + self = .unregisteredUser + + case "SYSTEM-001": + self = .internalServerError + + case "JWT-001": + self = .tokenDecodeException + case "JWT-002": + self = .tokenNotValid + case "JWT-003": + self = .tokenExpiredException + case "JWT-004": + self = .tokenNotFound + case "JWT-005": + self = .notSupportUserTokenType + + case "USER-001": + self = .invalidVerificationNumber + case "USER-002": + self = .verificationNumberNotFound + case "USER-003": + self = .imageUploadNotCompleted + + case "CENTER-001": + self = .duplicateIdentifier + case "CENTER-002": + self = .alreadyExistCenterManager + case "CENTER-003": + self = .alreadyExistCenter + case "CENTER-004": + self = .centerNotFoundException + + case "CARER-001": + self = .alreadyExistCarer + + case "PERSISTENCE-001": + self = .resourceNotFound + + case "SMS-001": + self = .clientException + + case "BUSINESS-REGISTRATION-001": + self = .businessCodeNotFound + + case "GeoCode-001": + self = .geoCodingFailure + + default: + self = .undefinedError + } + } + + // MARK: 오류 메세지 + public var message: String { + switch self { + case .invalidParameter: + return "요청하신 API에서 잘못된 파라미터가 입력되었습니다. 입력값을 다시 확인해주세요." + + case .unAuthorizedRequest: + return "접근 권한이 없습니다. 로그인이 필요한 API에 접근하려면 로그인을 먼저 해주세요." + + case .invalidLoginRequest: + return "로그인 실패: 입력하신 ID 또는 비밀번호가 잘못되었습니다. 존재하지 않는 ID로 로그인 시도 시에도 발생할 수 있습니다." + + case .invalidPassword: + return "비밀번호가 일치하지 않습니다. 정확한 비밀번호를 입력해주세요. (예: 회원 탈퇴 시 비밀번호 입력 단계에서 발생)" + + case .unregisteredUser: + return "등록되지 않은 사용자입니다. 회원가입이 필요합니다." + + case .internalServerError: + return "서버 내부에서 문제가 발생했습니다. 잠시 후 다시 시도해주세요." + + case .tokenDecodeException: + return "토큰 해석에 실패했습니다. 토큰의 형식이 올바른지 확인해주세요." + + case .tokenNotValid: + return "유효하지 않은 토큰입니다. 토큰의 값이 올바른지 다시 확인해주세요." + + case .tokenExpiredException: + return "토큰이 만료되었습니다. 다시 로그인해주세요." + + case .tokenNotFound: + return "토큰을 찾을 수 없습니다. 요청을 다시 확인해주세요." + + case .notSupportUserTokenType: + return "지원되지 않는 사용자 토큰 유형입니다. 사용 가능한 토큰 유형을 확인해주세요." + + case .invalidVerificationNumber: + return "잘못된 인증번호입니다. 다시 입력해주세요." + + case .verificationNumberNotFound: + return "인증번호가 만료되었거나 존재하지 않습니다. 새로운 인증번호를 요청해주세요." + + case .imageUploadNotCompleted: + return "이미지 업로드가 완료되지 않았습니다. 다시 시도해주세요." + + case .duplicateIdentifier: + return "이미 사용 중인 센터 ID입니다. 다른 ID를 사용해주세요." + + case .alreadyExistCenterManager: + return "해당 센터 관리자 계정이 이미 존재합니다." + + case .alreadyExistCenter: + return "이미 등록된 센터입니다. 다른 정보를 입력해주세요." + + case .centerNotFoundException: + return "해당 센터를 찾을 수 없습니다. 조회 조건을 확인해주세요." + + case .alreadyExistCarer: + return "이미 가입된 요양 보호사 정보가 존재합니다. 다른 정보를 입력해주세요." + + case .resourceNotFound: + return "요청하신 리소스를 찾을 수 없습니다. 요청이 올바른지 다시 확인해주세요." + + case .clientException: + return "SMS 발송에 실패했습니다. 입력된 정보를 다시 확인하거나 나중에 다시 시도해주세요." + + case .businessCodeNotFound: + return "사업자 등록번호를 찾을 수 없습니다. 정확한 정보를 입력했는지 확인해주세요." + + case .geoCodingFailure: + return "입력된 주소로 지리 정보를 찾을 수 없습니다. 주소를 다시 확인해주세요." + + case .undefinedCode, .undefinedError: + return "예기치 않은 오류가 발생했습니다. 잠시 후 다시 시도해주세요." + } + } +} diff --git a/project/Projects/Domain/Entity/Error/StoreError.swift b/project/Projects/Domain/Entity/Error/Etc/StoreError.swift similarity index 100% rename from project/Projects/Domain/Entity/Error/StoreError.swift rename to project/Projects/Domain/Entity/Error/Etc/StoreError.swift diff --git a/project/Projects/Domain/Entity/Error/Interface/DomainError.swift b/project/Projects/Domain/Entity/Error/Interface/DomainError.swift deleted file mode 100644 index 751afc15..00000000 --- a/project/Projects/Domain/Entity/Error/Interface/DomainError.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// DomainError.swift -// Entity -// -// Created by choijunios on 7/14/24. -// - -import Foundation - -public protocol DomainError: RawRepresentable, Error where RawValue == String { - - var message: String { get } - - static var undefinedError: Self { get } -} diff --git a/project/Projects/Domain/Entity/Error/RecruitmentPost/RecruitmentPostError.swift b/project/Projects/Domain/Entity/Error/RecruitmentPost/RecruitmentPostError.swift deleted file mode 100644 index 6dc45d1b..00000000 --- a/project/Projects/Domain/Entity/Error/RecruitmentPost/RecruitmentPostError.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// RecruitmentPostError.swift -// Entity -// -// Created by choijunios on 8/9/24. -// - -import Foundation - -public enum RecruitmentPostError: String, DomainError { - - // undefinedError - case undefinedError="Err-000" - - public var message: String { - switch self { - case .undefinedError: - "알 수 없는 오류가 발생했습니다." - } - } -} diff --git a/project/Projects/Domain/Entity/Error/UserInfo/UserInfoError.swift b/project/Projects/Domain/Entity/Error/UserInfo/UserInfoError.swift deleted file mode 100644 index 137dee3c..00000000 --- a/project/Projects/Domain/Entity/Error/UserInfo/UserInfoError.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// UserInfoError.swift -// Entity -// -// Created by choijunios on 7/20/24. -// - -import Foundation - -public enum UserInfoError: String, DomainError { - - case textUpdateFailed = "Err-001" - case imageUpdateFailed = "Err-002" - - // undefinedError - case undefinedError="Err-000" - - public var message: String { - switch self { - case .undefinedError: - "❌ \(String(describing: Self.self)) 정의되지 않은 에러타입입니다. ❌" - case .imageUpdateFailed: - "이미지 업로드에 실패했습니다." - case .textUpdateFailed: - "프로필 업로드에 실패했습니다." - } - } -} diff --git a/project/Projects/Domain/Entity/Error/RecruitmentPost/CenterEmployCardVO.swift b/project/Projects/Domain/Entity/VO/Employ/CenterEmployCardVO.swift similarity index 100% rename from project/Projects/Domain/Entity/Error/RecruitmentPost/CenterEmployCardVO.swift rename to project/Projects/Domain/Entity/VO/Employ/CenterEmployCardVO.swift diff --git a/project/Projects/Domain/UseCaseInterface/Auth/AuthInputValidationUseCase.swift b/project/Projects/Domain/UseCaseInterface/Auth/AuthInputValidationUseCase.swift index 72119d3a..2917d6c4 100644 --- a/project/Projects/Domain/UseCaseInterface/Auth/AuthInputValidationUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/Auth/AuthInputValidationUseCase.swift @@ -27,7 +27,7 @@ public protocol AuthInputValidationUseCase: UseCaseBase { /// - PhoneNumber : "000-0000-0000" /// - returns: /// - Observable - func requestPhoneNumberAuthentication(phoneNumber: String) -> Single> + func requestPhoneNumberAuthentication(phoneNumber: String) -> Single> // #2. /// 전화번호 유효성 로직 @@ -44,7 +44,7 @@ public protocol AuthInputValidationUseCase: UseCaseBase { /// - authNumber : String 예시 "000000" /// - returns: /// - Observable - func authenticateAuthNumber(phoneNumber: String, authNumber: String) -> Single> + func authenticateAuthNumber(phoneNumber: String, authNumber: String) -> Single> // #4. /// 사업자 번호로 해당 사업장 정보 조회 @@ -54,7 +54,7 @@ public protocol AuthInputValidationUseCase: UseCaseBase { /// - Observable // MARK: 사업자 번호 조회 - func requestBusinessNumberAuthentication(businessNumber: String) -> Single> + func requestBusinessNumberAuthentication(businessNumber: String) -> Single> // #5. /// 사업자 번호 유효성 로직 @@ -78,7 +78,7 @@ public protocol AuthInputValidationUseCase: UseCaseBase { /// - id : "idle1234" /// - returns: /// - Bool, true: 가능, flase: 증복 - func requestCheckingIdDuplication(id: String) -> Single> + func requestCheckingIdDuplication(id: String) -> Single> // #8. /// 아이디 유효성확인 로직 diff --git a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift index b1646868..316763cc 100644 --- a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift @@ -28,7 +28,7 @@ public protocol AuthUseCase: UseCaseBase { /// - registerState: CenterRegisterState func registerCenterAccount( registerState: CenterRegisterState - ) -> Single> + ) -> Single> // #2. /// 센터 로그인 실행 @@ -38,26 +38,26 @@ public protocol AuthUseCase: UseCaseBase { func loginCenterAccount( id: String, password: String - ) -> Single> + ) -> Single> /// 센터 회원 탈퇴 func deregisterCenterAccount( reasons: [DeregisterReasonVO], password: String - ) -> Single> + ) -> Single> /// 센터 로그아웃 - func signoutCenterAccount() -> Single> + func signoutCenterAccount() -> Single> // #5 /// 요양 보호사 회원가입 실행 func registerWorkerAccount( registerState: WorkerRegisterState - ) -> Single> + ) -> Single> // #6 /// 요양 보호사 로그인 실행 func loginWorkerAccount( phoneNumber: String, authNumber: String - ) -> Single> + ) -> Single> } diff --git a/project/Projects/Domain/UseCaseInterface/RecruitmentPost/RecruitmentPostUseCase.swift b/project/Projects/Domain/UseCaseInterface/RecruitmentPost/RecruitmentPostUseCase.swift index e782bb3c..05d4ae56 100644 --- a/project/Projects/Domain/UseCaseInterface/RecruitmentPost/RecruitmentPostUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/RecruitmentPost/RecruitmentPostUseCase.swift @@ -14,13 +14,13 @@ public protocol RecruitmentPostUseCase: UseCaseBase { // MARK: Center /// 센터측이 공고를 등록하는 액션입니다. - func registerRecruitmentPost(inputs: RegisterRecruitmentPostBundle) -> Single> + func registerRecruitmentPost(inputs: RegisterRecruitmentPostBundle) -> Single> /// 센터측이 공고를 수정하는 액션입니다. - func editRecruitmentPost(id: String, inputs: RegisterRecruitmentPostBundle) -> Single> + func editRecruitmentPost(id: String, inputs: RegisterRecruitmentPostBundle) -> Single> /// 센터측이 공고를 조회하는 액션입니다. - func getPostDetailForCenter(id: String) -> Single> + func getPostDetailForCenter(id: String) -> Single> // MARK: Worker @@ -30,8 +30,8 @@ public protocol RecruitmentPostUseCase: UseCaseBase { /// - 공고상세정보(센터와 달리 고객 이름 배제) /// - 근무지 위치(위경도) /// - 센터정보(센터 id, 이름, 도로명 주소) - func getPostDetailForWorker(id: String) -> Single> + func getPostDetailForWorker(id: String) -> Single> /// 요양보호사가 메인화면에 사용할 공고리스트를 호출합니다. - func getPostListForWorker(request: PostPagingRequestForWorker, postCount: Int) -> Single> + func getPostListForWorker(request: PostPagingRequestForWorker, postCount: Int) -> Single> } diff --git a/project/Projects/Domain/UseCaseInterface/UseCaseBase.swift b/project/Projects/Domain/UseCaseInterface/UseCaseBase.swift index 8f644b70..26ca6512 100644 --- a/project/Projects/Domain/UseCaseInterface/UseCaseBase.swift +++ b/project/Projects/Domain/UseCaseInterface/UseCaseBase.swift @@ -14,7 +14,7 @@ public protocol UseCaseBase: AnyObject { } public extension UseCaseBase { /// Repository로 부터 전달받은 언어레벨의 에러를 도메인 특화 에러로 변경하고, error를 Result의 Failure로, 성공을 Success로 변경합니다. - func convert(task: Single) -> Single> { + func convert(task: Single) -> Single> { Single.create { single in let disposable = task .subscribe { success in @@ -27,13 +27,21 @@ public extension UseCaseBase { } // MARK: InputValidationError - private func toDomainError(error: Error) -> T { + private func toDomainError(error: Error) -> DomainError { if let httpError = error as? HTTPResponseException { if let code = httpError.rawCode { - return T.init(rawValue: code) ?? T.undefinedError + let domainError = DomainError(code: code) + + if domainError == .undefinedCode { +#if DEBUG + print("‼️ 정의되지 않은 에러코드가 발견되었습니다. 노션을 확인해주세요") +#endif + } + + return domainError } #if DEBUG @@ -41,6 +49,6 @@ public extension UseCaseBase { #endif } - return T.undefinedError + return DomainError.undefinedError } } diff --git a/project/Projects/Domain/UseCaseInterface/UserInfo/CenterProfileUseCase.swift b/project/Projects/Domain/UseCaseInterface/UserInfo/CenterProfileUseCase.swift index d6544ec9..42cbf4c2 100644 --- a/project/Projects/Domain/UseCaseInterface/UserInfo/CenterProfileUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/UserInfo/CenterProfileUseCase.swift @@ -20,13 +20,13 @@ public protocol CenterProfileUseCase: UseCaseBase { /// 1. 나의 센터/다른 센터 프로필 정보 조회 /// 6. 특정 센터의 프로필 불러오기 - func getProfile(mode: ProfileMode) -> Single> + func getProfile(mode: ProfileMode) -> Single> /// 2. 센터 프로필 정보 업데이트(전화번호, 센터소개글) /// 3. 센터 프로필 정보 업데이트(이미지, pre-signed-url) /// 4. 센터 프로필 정보 업데이트(이미지, pre-signed-url-callback) - func updateProfile(phoneNumber: String?, introduction: String?, imageInfo: ImageUploadInfo?) -> Single> + func updateProfile(phoneNumber: String?, introduction: String?, imageInfo: ImageUploadInfo?) -> Single> /// 5. 센터 프로필 최초 등록 - func registerCenterProfile(state: CenterProfileRegisterState) -> Single> + func registerCenterProfile(state: CenterProfileRegisterState) -> Single> } diff --git a/project/Projects/Domain/UseCaseInterface/UserInfo/WorkerProfileUseCase.swift b/project/Projects/Domain/UseCaseInterface/UserInfo/WorkerProfileUseCase.swift index 86672f4c..86c4ee25 100644 --- a/project/Projects/Domain/UseCaseInterface/UserInfo/WorkerProfileUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/UserInfo/WorkerProfileUseCase.swift @@ -19,12 +19,12 @@ public protocol WorkerProfileUseCase: UseCaseBase { /// 1. 나의(요보) 프로필 정보 조회 /// 5. 특정 요양보호사의 프로필 불러오기 - func getProfile(mode: ProfileMode) -> Single> + func getProfile(mode: ProfileMode) -> Single> /// 2. 나의(요보) 프로필 정보 업데이트(텍스트 데이터) /// 3. 나의(요보) 프로필 정보 업데이트(이미지, pre-signed-url) /// 4. 나의(요보) 프로필 정보 업데이트(이미지, pre-signed-url-callback) - func updateProfile(stateObject: WorkerProfileStateObject, imageInfo: ImageUploadInfo?) -> Single> + func updateProfile(stateObject: WorkerProfileStateObject, imageInfo: ImageUploadInfo?) -> Single> } diff --git a/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+IdPassword.swift b/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+IdPassword.swift index e58fb4da..f7180156 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+IdPassword.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+IdPassword.swift @@ -43,7 +43,7 @@ extension AuthInOutStreamManager { print("✅ 디버그모드에서 아이디 중복검사 미실시") // ☑️ 상태추적 ☑️ stateTracker(id) - return Single.just(Result.success(id)) + return Single.just(Result.success(id)) #endif return useCase.requestCheckingIdDuplication(id: id) diff --git a/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+PhoneNumber.swift b/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+PhoneNumber.swift index 5b58f8f0..1655fcee 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+PhoneNumber.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/ViewModel/Center/AuthInOutStreamManager/AuthInOutStreamManager+PhoneNumber.swift @@ -50,7 +50,7 @@ extension AuthInOutStreamManager { let formatted = Self.formatPhoneNumber(phoneNumber: input.editingPhoneNumber.value) #if DEBUG print("✅ 디버그모드에서 번호인증 요청 무조건 통과") - return Single.just(Result.success(formatted)) + return Single.just(Result.success(formatted)) #endif return useCase.requestPhoneNumberAuthentication(phoneNumber: formatted) } @@ -111,7 +111,7 @@ extension AuthInOutStreamManager { #if DEBUG // 디버그시 인증번호 무조건 통과 print("✅ 디버그모드에서 번호인증 무조건 통과") - return Single.just(Result.success(phoneNumber)) + return Single.just(Result.success(phoneNumber)) #endif return useCase.authenticateAuthNumber(phoneNumber: phoneNumber, authNumber: authNumber) @@ -141,7 +141,7 @@ extension AuthInOutStreamManager { #if DEBUG // 디버그시 인증번호 무조건 통과 print("✅ 디버그모드에서 번호인증 무조건 통과") - return Single.just(Result.success(phoneNumber)) + return Single.just(Result.success(phoneNumber)) #endif return useCase.authenticateAuthNumber(phoneNumber: phoneNumber, authNumber: authNumber) diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/Board/CheckApplicant/CheckApplicantVM.swift b/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/Board/CheckApplicant/CheckApplicantVM.swift index e514744a..73e916b8 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/Board/CheckApplicant/CheckApplicantVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/Board/CheckApplicant/CheckApplicantVM.swift @@ -77,7 +77,7 @@ public class CheckApplicantVM: CheckApplicantViewModelable { .init(vo: vo, coordinator: coorindator) } - func publishPostApplicantVOMocks() -> Single> { + func publishPostApplicantVOMocks() -> Single> { .just(.success((0...10).map { _ in PostApplicantVO.mock })) } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift index b92b49d6..65b049f6 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift @@ -69,11 +69,11 @@ public class CenterRecruitmentPostBoardVM: CenterRecruitmentPostBoardViewModelab .asDriver(onErrorJustReturn: .default) } - func publishOngoingPostMocks() -> Single> { + func publishOngoingPostMocks() -> Single> { return .just(.success((0...10).map { _ in CenterEmployCardVO.mock })) } - func publishClosedPostMocks() -> Single> { + func publishClosedPostMocks() -> Single> { return .just(.success((0...10).map { _ in CenterEmployCardVO.mock })) } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift index 189fdcf1..f8f797f8 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift @@ -389,7 +389,7 @@ public class EditPostVM: EditPostViewModelable { let inputValidationFailure = inputValidationResult.compactMap { $0 } let editingRequestResult = inputValidationSuccess - .flatMap { [weak self] _ -> Single> in + .flatMap { [weak self] _ -> Single> in guard let self else { return .never() } return recruitmentPostUseCase.editRecruitmentPost( diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/RegisterRecruitmentPostVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/RegisterRecruitmentPostVM.swift index b64439ec..c63f41b6 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/RegisterRecruitmentPostVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/RegisterRecruitmentPostVM.swift @@ -460,7 +460,7 @@ public class RegisterRecruitmentPostVM: RegisterRecruitmentPostViewModelable { // MARK: ----------------- let registerPostResult = registerButtonClicked - .flatMap { [weak self] _ -> Single> in + .flatMap { [weak self] _ -> Single> in guard let self else { return .never() } // 공고를 등록합니다. diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RegisterCenterInfo/RegisterCenterInfoVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RegisterCenterInfo/RegisterCenterInfoVM.swift index bb02cf59..7a8e43da 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RegisterCenterInfo/RegisterCenterInfoVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RegisterCenterInfo/RegisterCenterInfoVM.swift @@ -115,7 +115,7 @@ public class RegisterCenterInfoVM: RegisterCenterInfoViewModelable { let profileRegisterResult = self.completeButtonPressed .flatMap { [useCase, stateObject] _ in #if DEBUG - return Single>.just(.success(())) + return Single>.just(.success(())) #endif return useCase.registerCenterProfile(state: stateObject) } diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerMyProfileViewModel.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerMyProfileViewModel.swift index 5cb5a7d3..04c4dff9 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerMyProfileViewModel.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerMyProfileViewModel.swift @@ -188,12 +188,12 @@ public class WorkerMyProfileViewModel: WorkerProfileEditViewModelable { profileRenderObject = rederingState.asDriver(onErrorRecover: { _ in fatalError() }) } - private func fetchProfileVO() -> Single> { + private func fetchProfileVO() -> Single> { workerProfileUseCase .getProfile(mode: .myProfile) } - public func requestUpload(editObject: WorkerProfileStateObject) -> Single> { + public func requestUpload(editObject: WorkerProfileStateObject) -> Single> { var submitObject: WorkerProfileStateObject = .init() diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerProfileViewModel.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerProfileViewModel.swift index 0a07f9be..51770537 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerProfileViewModel.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/Profile/WorkerProfileViewModel.swift @@ -106,7 +106,7 @@ public class WorkerProfileViewModel: OtherWorkerProfileViewModelable { profileRenderObject = rederingState.asDriver(onErrorRecover: { _ in fatalError() }) } - private func fetchProfileVO() -> Single> { + private func fetchProfileVO() -> Single> { workerProfileUseCase .getProfile(mode: .otherProfile(id: self.workerId)) } diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/AppliedPostBoardVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/AppliedPostBoardVM.swift index 0cd453e6..e0b4a477 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/AppliedPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/AppliedPostBoardVM.swift @@ -70,7 +70,7 @@ public class AppliedPostBoardVM: WorkerStaticPostBoardVMable { } - func publishAppliedPostMocks() -> Single> { + func publishAppliedPostMocks() -> Single> { return .just(.success((0..<10).map { _ in .mock })) } } diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/StarredPostBoardVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/StarredPostBoardVM.swift index 4070ff96..ab4c359f 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/StarredPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/StarredPostBoardVM.swift @@ -69,7 +69,7 @@ public class StarredPostBoardVM: WorkerStaticPostBoardVMable { } - func publishStarredPostMocks() -> Single> { + func publishStarredPostMocks() -> Single> { return .just(.success((0..<10).map { _ in .mock })) } } From 73f29d0dd48acf3918dc5eb7deda07934d3a005b Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 10:24:22 +0900 Subject: [PATCH 19/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EB=A7=8C=EB=A3=8C=EC=8B=9C=20=EC=9E=AC=EC=8B=9C?= =?UTF-8?q?=EB=8F=84=20=ED=9A=9F=EC=88=98=201=EB=B2=88=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=9E=AC=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 토큰을 재발급하고도 401이 발생한다면, 토큰 만료 이외에 다른 문제가 발생했다는 의미이다 Ex(비밀번호 오류) --- .../Data/NetworkDataSource/Service/BaseNetworkService.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift index d91ed342..3be3c9f4 100644 --- a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift +++ b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift @@ -96,9 +96,8 @@ public class BaseNetworkService { if let httpResponse = request.response { - if httpResponse.statusCode == 401 { + if httpResponse.statusCode == 401, request.retryCount < 1 { - // 401이며 guard let self else { return completion(.doNotRetry) } guard let refreshToken = self.keyValueStore.getAuthToken()?.refreshToken else { From 407c43ba6d83894a7b99829a8fefb2f2fdf29866 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 10:54:31 +0900 Subject: [PATCH 20/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20API=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(=EC=84=BC=ED=84=B0,=20=EC=9A=94=EC=96=91=EB=B3=B4?= =?UTF-8?q?=ED=98=B8=EC=82=AC=20=ED=86=B5=EC=9D=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/NetworkDataSource/API/AuthAPI.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift index 6f316114..3cff9482 100644 --- a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift +++ b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift @@ -67,10 +67,16 @@ extension AuthAPI: BaseAPI { "common/send" case .checkAuthNumber: "common/confirm" + case .reissueToken: + "common/refresh" + + case .authenticateBusinessNumber(let businessNumber): "center/authentication/\(businessNumber)" case .checkIdDuplication(id: let id): "center/validation/\(id)" + + case .registerCenterAccount: "center/join" case .centerLogin: @@ -79,12 +85,12 @@ extension AuthAPI: BaseAPI { "center/logout" case .deregisterCenterAccount: "center/withdraw" - case .reissueToken: - "center/refresh" + + case .registerWorkerAccount: - "auth/carer/join" + "carer/join" case .workerLogin: - "auth/carer/login" + "carer/login" } } From a3a98e08783649a980b27338d15082ca2e7eb5c9 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 12:17:46 +0900 Subject: [PATCH 21/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DI/Assembly/DomainAssembly.swift | 3 + .../Main/Center /CenterMainCoordinator.swift | 12 ++-- .../RegisterPostCoordinator.swift | 12 ---- .../CenterProfileRegisterCoordinator.swift | 1 - .../CenterSettingCoordinator.swift | 52 ++++++++++++++ .../RecruitmentManagementCoordinator.swift | 34 ++++------ .../AppliedAndLikedBoardCoordinator.swift | 37 +++++----- .../WorkerRecruitmentBoardCoordinator.swift | 46 ++++++------- .../Main/Worker/WorkerMainCoordinator.swift | 12 ++-- .../RootCoordinator/RootCoordinator.swift | 2 + .../RegisterRecruitmentPostCoordinator.swift | 6 +- .../CenterSettingScreenCoordinator.swift | 68 +++++++++++++++++++ .../PasswordForDeregisterCoordinator.swift | 9 ++- .../ViewModel/Setting/CenterSettingVM.swift | 14 +--- .../Setting/PasswordForDeregisterVM.swift | 8 ++- .../CheckApplicantCoordinator.swift | 4 +- .../Setting/CenterSettingCoordinator.swift | 30 -------- .../Center/View/RecuitmentManagementVC.swift | 64 ----------------- .../Coordinator/DeRegisterCoordinator.swift | 12 ++-- .../Sub/SelectReasonCoordinator.swift | 0 .../View}/DeregisterReasonVC.swift | 0 .../CenterDeregisterReasonsVM.swift | 0 .../TabBar/IdleCenterMainPage.swift | 0 .../{Common => }/TabBar/IdleTabBar.swift | 0 .../TabBar/IdleWorkerMainPage.swift | 0 .../View/MainBoard/ RecruitmentBoardVC.swift | 34 ---------- .../View/MainBoard/ApplyManagementVC.swift | 34 ---------- .../Screen/Worker/View/TestSettingVC.swift | 34 ---------- .../CenterSettingScreenCoordinatable.swift | 13 ++++ .../DeregisterCoordinatable.swift | 8 +-- 30 files changed, 227 insertions(+), 322 deletions(-) delete mode 100644 project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/RegisterPostCoordinator.swift rename project/Projects/App/Sources/RootCoordinator/Main/Center /{OtherCoordinator => SubCoordinator}/CenterProfileRegisterCoordinator.swift (99%) create mode 100644 project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift rename project/Projects/{Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost => App/Sources/RootCoordinator/Main/Center /SubCoordinator}/RecruitmentManagementCoordinator.swift (72%) rename project/Projects/{Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/AppliedAndLiked => App/Sources/RootCoordinator/Main/Worker/SubCoordinator}/AppliedAndLikedBoardCoordinator.swift (60%) rename project/Projects/{Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/MainBoard => App/Sources/RootCoordinator/Main/Worker/SubCoordinator}/WorkerRecruitmentBoardCoordinator.swift (53%) rename project/Projects/Presentation/Feature/{Root/Sources/Screen/Center => Center/Sources}/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift (97%) create mode 100644 project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift rename project/Projects/Presentation/Feature/Root/Sources/Screen/Center/{Coordinator/RecruitmentPost => }/CheckApplicantCoordinator.swift (96%) delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/Setting/CenterSettingCoordinator.swift delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Center/View/RecuitmentManagementVC.swift rename project/Projects/Presentation/Feature/Root/Sources/Screen/Common/{Setting => }/Deregister/Coordinator/DeRegisterCoordinator.swift (94%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/Common/{Setting => }/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/Common/{Setting/Deregister => Deregister/View}/DeregisterReasonVC.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/Common/{Setting/Deregister => Deregister/ViewModel}/CenterDeregisterReasonsVM.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Common => }/TabBar/IdleCenterMainPage.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Common => }/TabBar/IdleTabBar.swift (100%) rename project/Projects/Presentation/Feature/Root/Sources/Screen/{Common => }/TabBar/IdleWorkerMainPage.swift (100%) delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ RecruitmentBoardVC.swift delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ApplyManagementVC.swift delete mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift create mode 100644 project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/CenterSettingScreenCoordinatable.swift rename project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/{ => Deregister}/DeregisterCoordinatable.swift (83%) diff --git a/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift b/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift index a75b3866..b90da006 100644 --- a/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift +++ b/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift @@ -45,5 +45,8 @@ public struct DomainAssembly: Assembly { return DefaultWorkerProfileUseCase(repository: repository) } + container.register(SettingScreenUseCase.self) { resolver in + return DefaultSettingUseCase() + } } } diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /CenterMainCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /CenterMainCoordinator.swift index ec28832f..34a1061e 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Center /CenterMainCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /CenterMainCoordinator.swift @@ -8,6 +8,7 @@ import UIKit import DSKit import PresentationCore +import CenterFeature import RootFeature import UseCaseInterface @@ -74,15 +75,18 @@ class CenterMainCoordinator: CenterMainCoordinatable { coordinator = RecruitmentManagementCoordinator( dependency: .init( parent: self, - navigationController: navigationController, - workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self), - recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) + injector: injector, + navigationController: navigationController ) ) case .setting: coordinator = CenterSettingCoordinator( - navigationController: navigationController + dependency: .init( + parent: self, + injector: injector, + navigationController: navigationController + ) ) } addChildCoordinator(coordinator) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/RegisterPostCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/RegisterPostCoordinator.swift deleted file mode 100644 index 71de734b..00000000 --- a/project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/RegisterPostCoordinator.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// RegisterRecruitmentPostCoordinator.swift -// Idle-iOS -// -// Created by choijunios on 8/5/24. -// - -import UIKit -import DSKit -import PresentationCore -import CenterFeature -import UseCaseInterface diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/CenterProfileRegisterCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterProfileRegisterCoordinator.swift similarity index 99% rename from project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/CenterProfileRegisterCoordinator.swift rename to project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterProfileRegisterCoordinator.swift index d10f28d4..d499aeab 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Center /OtherCoordinator/CenterProfileRegisterCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterProfileRegisterCoordinator.swift @@ -45,7 +45,6 @@ class CenterProfileRegisterCoordinator: CenterProfileRegisterCoordinatable { public func registerFinished() { clearChildren() - parent?.removeChildCoordinator(self) } } diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift new file mode 100644 index 00000000..88cae60e --- /dev/null +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift @@ -0,0 +1,52 @@ +// +// CenterSettingCoordinator.swift +// Idle-iOS +// +// Created by choijunios on 8/25/24. +// + +import UIKit +import CenterFeature +import PresentationCore +import UseCaseInterface + +class CenterSettingCoordinator: CenterSettingScreenCoordinatable { + + struct Dependency { + let parent: CenterMainCoordinatable + let injector: Injector + let navigationController: UINavigationController + } + + var childCoordinators: [any PresentationCore.Coordinator] = [] + + weak var parent: CenterMainCoordinatable? + + weak var viewControllerRef: UIViewController? + + var navigationController: UINavigationController + let injector: Injector + + init(dependency: Dependency) { + self.navigationController = dependency.navigationController + self.injector = dependency.injector + self.parent = dependency.parent + } + + public func start() { + let coordinator = CenterSettingScreenCoordinator( + dependency: .init( + navigationController: navigationController, + settingUseCase: injector.resolve(SettingScreenUseCase.self), + centerProfileUseCase: injector.resolve(CenterProfileUseCase.self) + ) + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() + } + + public func startRemoveCenterAccountFlow() { + + } +} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RecruitmentManagementCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift similarity index 72% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RecruitmentManagementCoordinator.swift rename to project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift index 90bde115..4256825b 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RecruitmentManagementCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift @@ -1,11 +1,12 @@ // // RecruitmentManagementCoordinator.swift -// RootFeature +// Idle-iOS // // Created by choijunios on 7/25/24. // import UIKit +import RootFeature import CenterFeature import PresentationCore import UseCaseInterface @@ -15,21 +16,14 @@ import Entity public class RecruitmentManagementCoordinator: RecruitmentManagementCoordinatable { public struct Dependency { - weak var parent: CenterMainCoordinatable? + let parent: CenterMainCoordinatable + let injector: Injector let navigationController: UINavigationController - let workerProfileUseCase: WorkerProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase - public init( - parent: CenterMainCoordinatable? = nil, - navigationController: UINavigationController, - workerProfileUseCase: WorkerProfileUseCase, - recruitmentPostUseCase: RecruitmentPostUseCase - ) { + init(parent: CenterMainCoordinatable, injector: Injector, navigationController: UINavigationController) { self.parent = parent + self.injector = injector self.navigationController = navigationController - self.workerProfileUseCase = workerProfileUseCase - self.recruitmentPostUseCase = recruitmentPostUseCase } } @@ -37,20 +31,16 @@ public class RecruitmentManagementCoordinator: RecruitmentManagementCoordinatabl public weak var viewControllerRef: UIViewController? - public var navigationController: UINavigationController - public weak var parent: CenterMainCoordinatable? - - let workerProfileUseCase: WorkerProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase + let injector: Injector + public var navigationController: UINavigationController public init( dependency: Dependency ) { self.parent = dependency.parent + self.injector = dependency.injector self.navigationController = dependency.navigationController - self.workerProfileUseCase = dependency.workerProfileUseCase - self.recruitmentPostUseCase = dependency.recruitmentPostUseCase } public func start() { @@ -74,7 +64,7 @@ public extension RecruitmentManagementCoordinator { dependency: .init( navigationController: navigationController, centerEmployCardVO: .mock, - workerProfileUseCase: workerProfileUseCase + workerProfileUseCase: injector.resolve(WorkerProfileUseCase.self) ) ) addChildCoordinator(coordinator) @@ -89,7 +79,7 @@ public extension RecruitmentManagementCoordinator { postId: postId, applicantCount: applicantCount, navigationController: navigationController, - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) ) addChildCoordinator(coordinator) @@ -101,7 +91,7 @@ public extension RecruitmentManagementCoordinator { let vm = EditPostVM( id: postId, - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) let coordinator = EditPostCoordinator( dependency: .init( diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/AppliedAndLiked/AppliedAndLikedBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift similarity index 60% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/AppliedAndLiked/AppliedAndLikedBoardCoordinator.swift rename to project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift index 5f6c552f..92457d08 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/AppliedAndLiked/AppliedAndLikedBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/AppliedAndLikedBoardCoordinator.swift @@ -12,44 +12,41 @@ import CenterFeature import PresentationCore import UseCaseInterface -public class AppliedAndLikedBoardCoordinator: WorkerRecruitmentBoardCoordinatable { +class AppliedAndLikedBoardCoordinator: WorkerRecruitmentBoardCoordinatable { - public struct Dependency { + struct Dependency { + let parent: WorkerMainCoordinator + let injector: Injector let navigationController: UINavigationController - let centerProfileUseCase: CenterProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase - public init(navigationController: UINavigationController, centerProfileUseCase: CenterProfileUseCase, recruitmentPostUseCase: RecruitmentPostUseCase) { + init(parent: WorkerMainCoordinator, injector: Injector, navigationController: UINavigationController) { + self.parent = parent + self.injector = injector self.navigationController = navigationController - self.centerProfileUseCase = centerProfileUseCase - self.recruitmentPostUseCase = recruitmentPostUseCase } } - public var childCoordinators: [any PresentationCore.Coordinator] = [] + var childCoordinators: [any PresentationCore.Coordinator] = [] - public weak var viewControllerRef: UIViewController? - - public var navigationController: UINavigationController + weak var viewControllerRef: UIViewController? + var navigationController: UINavigationController weak var parent: ParentCoordinator? - - let centerProfileUseCase: CenterProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase + let injector: Injector public init(depedency: Dependency) { + self.parent = depedency.parent self.navigationController = depedency.navigationController - self.centerProfileUseCase = depedency.centerProfileUseCase - self.recruitmentPostUseCase = depedency.recruitmentPostUseCase + self.injector = depedency.injector } public func start() { let vc = StarredAndAppliedVC() let appliedVM = AppliedPostBoardVM( - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) let starredVM = StarredPostBoardVM( - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) vc.bind( appliedPostVM: appliedVM, @@ -72,7 +69,7 @@ extension AppliedAndLikedBoardCoordinator { postId: postId, parent: self, navigationController: navigationController, - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) ) addChildCoordinator(coodinator) @@ -82,7 +79,7 @@ extension AppliedAndLikedBoardCoordinator { let coordinator = CenterProfileCoordinator( dependency: .init( mode: .otherProfile(id: centerId), - profileUseCase: centerProfileUseCase, + profileUseCase: injector.resolve(CenterProfileUseCase.self), navigationController: navigationController ) ) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/MainBoard/WorkerRecruitmentBoardCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift similarity index 53% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/MainBoard/WorkerRecruitmentBoardCoordinator.swift rename to project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift index 623d9d61..2239c985 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/Coordinator/MainBoard/WorkerRecruitmentBoardCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/SubCoordinator/WorkerRecruitmentBoardCoordinator.swift @@ -1,6 +1,6 @@ // // WorkerRecruitmentBoardCoordinator.swift -// RootFeature +// Idle-iOS // // Created by choijunios on 7/25/24. // @@ -12,52 +12,44 @@ import CenterFeature import PresentationCore import UseCaseInterface -public class WorkerRecruitmentBoardCoordinator: WorkerRecruitmentBoardCoordinatable { +class WorkerRecruitmentBoardCoordinator: WorkerRecruitmentBoardCoordinatable { - public struct Dependency { + struct Dependency { + let parent: WorkerMainCoordinator + let injector: Injector let navigationController: UINavigationController - let centerProfileUseCase: CenterProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase - public init(navigationController: UINavigationController, centerProfileUseCase: CenterProfileUseCase, recruitmentPostUseCase: RecruitmentPostUseCase) { + init(parent: WorkerMainCoordinator, injector: Injector, navigationController: UINavigationController) { + self.parent = parent + self.injector = injector self.navigationController = navigationController - self.centerProfileUseCase = centerProfileUseCase - self.recruitmentPostUseCase = recruitmentPostUseCase } } - public var childCoordinators: [any PresentationCore.Coordinator] = [] + var childCoordinators: [any PresentationCore.Coordinator] = [] - public weak var viewControllerRef: UIViewController? - - public var navigationController: UINavigationController + weak var viewControllerRef: UIViewController? + var navigationController: UINavigationController weak var parent: ParentCoordinator? + let injector: Injector - let centerProfileUseCase: CenterProfileUseCase - let recruitmentPostUseCase: RecruitmentPostUseCase - - public init(depedency: Dependency) { + init(depedency: Dependency) { self.navigationController = depedency.navigationController - self.centerProfileUseCase = depedency.centerProfileUseCase - self.recruitmentPostUseCase = depedency.recruitmentPostUseCase + self.parent = depedency.parent + self.injector = depedency.injector } - public func start() { + func start() { let vc = WorkerRecruitmentPostBoardVC() let vm = WorkerRecruitmentPostBoardVM( coordinator: self, - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) vc.bind(viewModel: vm) viewControllerRef = vc navigationController.pushViewController(vc, animated: false) } - - public func coordinatorDidFinish() { - popViewController() - parent?.removeChildCoordinator(self) - } } extension WorkerRecruitmentBoardCoordinator { @@ -67,7 +59,7 @@ extension WorkerRecruitmentBoardCoordinator { postId: postId, parent: self, navigationController: navigationController, - recruitmentPostUseCase: recruitmentPostUseCase + recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) ) ) addChildCoordinator(coodinator) @@ -77,7 +69,7 @@ extension WorkerRecruitmentBoardCoordinator { let coordinator = CenterProfileCoordinator( dependency: .init( mode: .otherProfile(id: centerId), - profileUseCase: centerProfileUseCase, + profileUseCase: injector.resolve(CenterProfileUseCase.self), navigationController: navigationController ) ) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift index 729f68a6..e78acdde 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Worker/WorkerMainCoordinator.swift @@ -73,17 +73,17 @@ class WorkerMainCoordinator: ParentCoordinator { case .home: coordinator = WorkerRecruitmentBoardCoordinator( depedency: .init( - navigationController: navigationController, - centerProfileUseCase: injector.resolve(CenterProfileUseCase.self), - recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) + parent: self, + injector: injector, + navigationController: navigationController ) ) case .preferredPost: coordinator = AppliedAndLikedBoardCoordinator( depedency: .init( - navigationController: navigationController, - centerProfileUseCase: injector.resolve(CenterProfileUseCase.self), - recruitmentPostUseCase: injector.resolve(RecruitmentPostUseCase.self) + parent: self, + injector: injector, + navigationController: navigationController ) ) case .setting: diff --git a/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift index e0e9da8e..635f73a5 100644 --- a/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift @@ -28,6 +28,8 @@ class RootCoordinator: ParentCoordinator { func start() { navigationController.setNavigationBarHidden(true, animated: false) + // 빈화면 VC + VM 구현 예정, 로직에 따라 Auth, CenterMain, WorkerMain을 display + centerMain() } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift similarity index 97% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift rename to project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift index ca84ed61..bb0a87db 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/RegisterRecruitmentPostCoordinator.swift @@ -1,6 +1,6 @@ // -// asd.swift -// RootFeature +// RegisterRecruitmentPostCoordinator.swift +// CenterFeature // // Created by choijunios on 8/14/24. // @@ -9,8 +9,6 @@ import UIKit import PresentationCore import UseCaseInterface import Entity -import CenterFeature -import WorkerFeature import BaseFeature diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift new file mode 100644 index 00000000..ffd8a112 --- /dev/null +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift @@ -0,0 +1,68 @@ +// +// CenterSettingScreenCoordinator.swift +// CenterFeature +// +// Created by choijunios on 8/25/24. +// + +import UIKit +import PresentationCore +import UseCaseInterface +import Entity + +public class CenterSettingScreenCoordinator: ChildCoordinator { + + public struct Dependency { + let navigationController: UINavigationController + let settingUseCase: SettingScreenUseCase + let centerProfileUseCase: CenterProfileUseCase + + public init(navigationController: UINavigationController, settingUseCase: SettingScreenUseCase, centerProfileUseCase: CenterProfileUseCase) { + self.navigationController = navigationController + self.settingUseCase = settingUseCase + self.centerProfileUseCase = centerProfileUseCase + } + } + + public weak var viewControllerRef: UIViewController? + public weak var parent: CenterSettingScreenCoordinatable? + + public let navigationController: UINavigationController + let settingUseCase: SettingScreenUseCase + let centerProfileUseCase: CenterProfileUseCase + + public init( + dependency: Dependency + ) { + self.navigationController = dependency.navigationController + self.settingUseCase = dependency.settingUseCase + self.centerProfileUseCase = dependency.centerProfileUseCase + } + + deinit { + printIfDebug("\(String(describing: CenterSettingScreenCoordinator.self))") + } + + public func start() { + let vc = CenterSettingVC() + let vm = CenterSettingVM( + coordinator: self, + settingUseCase: settingUseCase, + centerProfileUseCase: centerProfileUseCase + ) + vc.bind(viewModel: vm) + viewControllerRef = vc + navigationController.pushViewController(vc, animated: false) + } + + public func coordinatorDidFinish() { + parent?.removeChildCoordinator(self) + popViewController() + } + + public func popToRoot() { + + /// Root까지 네비게이션을 제거합니다. + } +} + diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift index b5991561..de92ccca 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift @@ -60,8 +60,13 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { parent?.removeChildCoordinator(self) } - public func flowFinished() { - parent?.flowFinished() + public func cancelDeregister() { + parent?.cancelDeregister() + } + + public func popToRoot() { + + /// Root까지 네비게이션을 제거합니다. } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index 5e15dec3..01862f67 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -15,14 +15,6 @@ import DSKit import UseCaseInterface import UserNotifications -public protocol CenterSettingScreenCoordinatable: ParentCoordinator { - /// 로그인및 회원가입 화면으로 이동합니다. - func showAuthScreen() - - /// 시설 관리자 계정을 지우는 작업을 시작합니다. - func startRemoveCenterAccountFlow() -} - public protocol CenterSettingVMable { // Input @@ -46,7 +38,7 @@ public protocol CenterSettingVMable { public class CenterSettingVM: CenterSettingVMable { // Init - weak var coordinator: CenterSettingScreenCoordinatable? + weak var coordinator: CenterSettingScreenCoordinator? let settingUseCase: SettingScreenUseCase let centerProfileUseCase: CenterProfileUseCase @@ -65,7 +57,7 @@ public class CenterSettingVM: CenterSettingVMable { let disposeBag = DisposeBag() public init( - coordinator: CenterSettingScreenCoordinatable?, + coordinator: CenterSettingScreenCoordinator?, settingUseCase: SettingScreenUseCase, centerProfileUseCase: CenterProfileUseCase ) @@ -145,7 +137,7 @@ public class CenterSettingVM: CenterSettingVMable { // ‼️ ‼️ 로컬에 저장된 계정 정보 삭제 ‼️ ‼️ - self?.coordinator?.showAuthScreen() + self?.coordinator?.popToRoot() }) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift index fc0a066c..0d4da69e 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift @@ -46,14 +46,18 @@ public class PasswordForDeregisterVM: DefaultAlertOutputable { deregisterSuccess .observe(on: MainScheduler.asyncInstance) .subscribe(onNext: { [weak self] _ in - self?.coordinator?.flowFinished() + + // ‼️ ‼️ 로컬에 저장된 계정 정보 삭제 ‼️ ‼️ + + // RootCoordinator로 이동 + self?.coordinator?.popToRoot() }) .disposed(by: disposeBag) exitButtonClicked .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] _ in - self?.coordinator?.coordinatorDidFinish() + self?.coordinator?.cancelDeregister() }) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/CheckApplicantCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/CheckApplicantCoordinator.swift similarity index 96% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/CheckApplicantCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Center/CheckApplicantCoordinator.swift index e1cde0fa..9f99a774 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/RecruitmentPost/CheckApplicantCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/CheckApplicantCoordinator.swift @@ -8,9 +8,9 @@ import UIKit import PresentationCore import UseCaseInterface -import Entity import CenterFeature import WorkerFeature +import Entity import BaseFeature public class CheckApplicantCoordinator: CheckApplicantCoordinatable { @@ -45,7 +45,7 @@ public class CheckApplicantCoordinator: CheckApplicantCoordinatable { } deinit { - printIfDebug("\(String(describing: RegisterRecruitmentCoordinator.self))") + printIfDebug("\(String(describing: CheckApplicantCoordinator.self))") } public func start() { diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/Setting/CenterSettingCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/Setting/CenterSettingCoordinator.swift deleted file mode 100644 index 7a0eaa67..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/Coordinator/Setting/CenterSettingCoordinator.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// CenterSettingCoordinator.swift -// RootFeature -// -// Created by choijunios on 8/19/24. -// - -import UIKit -import PresentationCore - -public class CenterSettingCoordinator: ChildCoordinator { - - public weak var viewControllerRef: UIViewController? - - public var navigationController: UINavigationController - - public init(navigationController: UINavigationController) { - self.navigationController = navigationController - } - - public func start() { - let vc = TestSettingVC() - - navigationController.pushViewController(vc, animated: false) - } - - public func coordinatorDidFinish() { - - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/View/RecuitmentManagementVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/View/RecuitmentManagementVC.swift deleted file mode 100644 index 4532a107..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Center/View/RecuitmentManagementVC.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// RecuitmentManagementVC.swift -// RootFeature -// -// Created by choijunios on 7/25/24. -// - -import UIKit -import RxCocoa -import RxSwift - -public class RecuitmentManagementVC: UIViewController { - - weak var coordinator: RecruitmentManagementCoordinator? - - public init(coordinator: RecruitmentManagementCoordinator) { - self.coordinator = coordinator - - super.init(nibName: nil, bundle: nil) - - setAppearacne() - } - - public required init?(coder: NSCoder) { fatalError() } - - let dispoesBag = DisposeBag() - - private func setAppearacne() { - view.backgroundColor = .white - - let label = UILabel() - label.text = "공고 관리" - - let button1 = UIButton() - button1.setTitle("센터정보 등록", for: .normal) - button1.setTitleColor(.black, for: .normal) - button1.isUserInteractionEnabled = true - - let button2 = UIButton() - button2.setTitle("공고 등록", for: .normal) - button2.setTitleColor(.black, for: .normal) - button2.isUserInteractionEnabled = true - - [ - label, - button1, - button2, - ].forEach { - $0.translatesAutoresizingMaskIntoConstraints = false - view.addSubview($0) - } - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: view.centerXAnchor), - label.centerYAnchor.constraint(equalTo: view.centerYAnchor), - - button1.centerXAnchor.constraint(equalTo: view.centerXAnchor), - button1.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 15), - - button2.centerXAnchor.constraint(equalTo: view.centerXAnchor), - button2.topAnchor.constraint(equalTo: button1.bottomAnchor, constant: 15), - ]) - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift similarity index 94% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift index f5f67f5b..3b163457 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -1,14 +1,14 @@ // // DeRegisterCoordinator.swift -// RootFeature +// BaseFeature // // Created by choijunios on 8/21/24. // import UIKit import Entity -import CenterFeature import PresentationCore +import CenterFeature import UseCaseInterface public class DeRegisterCoordinator: DeregisterCoordinatable { @@ -45,10 +45,6 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { showSelectReasonScreen() } - public func flowFinished() { - - } - public func showSelectReasonScreen() { let coordinator: SelectReasonCoordinator = .init( dependency: .init( @@ -80,8 +76,8 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { } - public func coordinatorDidFinish() { - popViewController() + public func cancelDeregister() { + clearChildren() parent?.removeChildCoordinator(self) } } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/DeregisterReasonVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/DeregisterReasonVC.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/View/DeregisterReasonVC.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/CenterDeregisterReasonsVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Setting/Deregister/CenterDeregisterReasonsVM.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/ViewModel/CenterDeregisterReasonsVM.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleCenterMainPage.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleCenterMainPage.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleCenterMainPage.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleCenterMainPage.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleTabBar.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleTabBar.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleTabBar.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleTabBar.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleWorkerMainPage.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleWorkerMainPage.swift similarity index 100% rename from project/Projects/Presentation/Feature/Root/Sources/Screen/Common/TabBar/IdleWorkerMainPage.swift rename to project/Projects/Presentation/Feature/Root/Sources/Screen/TabBar/IdleWorkerMainPage.swift diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ RecruitmentBoardVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ RecruitmentBoardVC.swift deleted file mode 100644 index be5270f5..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ RecruitmentBoardVC.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RecruitmentBoardVC.swift -// RootFeature -// -// Created by choijunios on 7/25/24. -// - -import UIKit - -public class RecruitmentBoardVC: UIViewController { - - public init() { - super.init(nibName: nil, bundle: nil) - - setAppearacne() - } - - public required init?(coder: NSCoder) { fatalError() } - - private func setAppearacne() { - view.backgroundColor = .white - - let label = UILabel() - label.text = "채용공고 화면" - - label.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: view.centerXAnchor), - label.centerYAnchor.constraint(equalTo: view.centerYAnchor), - ]) - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ApplyManagementVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ApplyManagementVC.swift deleted file mode 100644 index 240da75b..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/MainBoard/ApplyManagementVC.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// ApplyManagementVC.swift -// RootFeature -// -// Created by choijunios on 7/25/24. -// - -import UIKit - -public class ApplyManagementVC: UIViewController { - - public init() { - super.init(nibName: nil, bundle: nil) - - setAppearacne() - } - - public required init?(coder: NSCoder) { fatalError() } - - private func setAppearacne() { - view.backgroundColor = .white - - let label = UILabel() - label.text = "공고 관리 화면" - - label.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: view.centerXAnchor), - label.centerYAnchor.constraint(equalTo: view.centerYAnchor), - ]) - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift deleted file mode 100644 index c067e316..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Worker/View/TestSettingVC.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// SettingVC.swift -// RootFeature -// -// Created by choijunios on 7/25/24. -// - -import UIKit - -public class TestSettingVC: UIViewController { - - public init() { - super.init(nibName: nil, bundle: nil) - - setAppearacne() - } - - public required init?(coder: NSCoder) { fatalError() } - - private func setAppearacne() { - view.backgroundColor = .white - - let label = UILabel() - label.text = "세팅 화면" - - label.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: view.centerXAnchor), - label.centerYAnchor.constraint(equalTo: view.centerYAnchor), - ]) - } -} diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/CenterSettingScreenCoordinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/CenterSettingScreenCoordinatable.swift new file mode 100644 index 00000000..d1222f61 --- /dev/null +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/CenterSettingScreenCoordinatable.swift @@ -0,0 +1,13 @@ +// +// asd.swift +// PresentationCore +// +// Created by choijunios on 8/25/24. +// + +import Foundation + +public protocol CenterSettingScreenCoordinatable: ParentCoordinator { + /// 시설 관리자 계정을 지우는 작업을 시작합니다. + func startRemoveCenterAccountFlow() +} diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift similarity index 83% rename from project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift rename to project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift index 8e99e1fb..08dc10a2 100644 --- a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/DeregisterCoordinatable.swift +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Setting/Deregister/DeregisterCoordinatable.swift @@ -10,17 +10,15 @@ import Entity public protocol DeregisterCoordinatable: ParentCoordinator { - /// 탈퇴과정이 끝났음을 알립니다. - func flowFinished() - /// 공통: 탈퇴 이유를 선택합니다. func showSelectReasonScreen() + /// 공통: 탈퇴를 취소합니다. + func cancelDeregister() + /// 센터관리자: 마지막으로 비밀번호를 입력합니다. func showFinalPasswordScreen(reasons: [DeregisterReasonVO]) /// 요양보호사: 마지막으로 전화번호를 입력합니다. func showFinalPhoneAuthScreen(reasons: [DeregisterReasonVO]) - - func coordinatorDidFinish() } From 7b02d85916bfe65923ddb0a1c0ca36279ebffb07 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 12:30:18 +0900 Subject: [PATCH 22/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=EC=BD=94?= =?UTF-8?q?=EB=94=94=EB=84=A4=EC=9D=B4=ED=84=B0=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecruitmentManagementCoordinator.swift | 11 ++-- ...ecruitmentPostBoardScreenCoordinator.swift | 54 +++++++++++++++++++ .../DetailVC/PostDetailForCenterVC.swift | 5 +- .../CenterRecruitmentPostBoardVM.swift | 8 +-- 4 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/CenterRecruitmentPostBoardScreenCoordinator.swift diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift index 4256825b..5d41b4c0 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/RecruitmentManagementCoordinator.swift @@ -44,11 +44,12 @@ public class RecruitmentManagementCoordinator: RecruitmentManagementCoordinatabl } public func start() { - let vc = CenterRecruitmentPostBoardVC() - let vm = CenterRecruitmentPostBoardVM(coordinator: self) - vc.bind(viewModel: vm) - viewControllerRef = vc - navigationController.pushViewController(vc, animated: false) + let coordinator = CenterRecruitmentPostBoardScreenCoordinator( + navigationController: navigationController + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() } public func coordinatorDidFinish() { diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/CenterRecruitmentPostBoardScreenCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/CenterRecruitmentPostBoardScreenCoordinator.swift new file mode 100644 index 00000000..29439471 --- /dev/null +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/CenterRecruitmentPostBoardScreenCoordinator.swift @@ -0,0 +1,54 @@ +// +// CenterRecruitmentPostBoardScreenCoordinator.swift +// CenterFeature +// +// Created by choijunios on 8/25/24. +// + +import UIKit +import PresentationCore +import UseCaseInterface +import Entity + +public class CenterRecruitmentPostBoardScreenCoordinator: ChildCoordinator { + + public weak var viewControllerRef: UIViewController? + public weak var parent: RecruitmentManagementCoordinatable? + public let navigationController: UINavigationController + + public init( + navigationController: UINavigationController + ) { + self.navigationController = navigationController + } + + deinit { + printIfDebug("\(String(describing: RegisterRecruitmentCoordinator.self))") + } + + public func start() { + let vc = CenterRecruitmentPostBoardVC() + let vm = CenterRecruitmentPostBoardVM(coordinator: self) + vc.bind(viewModel: vm) + viewControllerRef = vc + navigationController.pushViewController(vc, animated: false) + } + + public func coordinatorDidFinish() { + popViewController() + parent?.removeChildCoordinator(self) + } + + public func showCheckingApplicantScreen(postId: String) { + parent?.showCheckingApplicantScreen(postId: postId) + } + + public func showPostDetailScreenForCenter(postId: String, applicantCount: Int?) { + parent?.showPostDetailScreenForCenter(postId: postId, applicantCount: applicantCount) + } + + public func showEditScreen(postId: String) { + parent?.showEditScreen(postId: postId) + } +} + diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/DetailVC/PostDetailForCenterVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/DetailVC/PostDetailForCenterVC.swift index bd4283e4..5f610202 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/DetailVC/PostDetailForCenterVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/RecruitmentPost/DetailVC/PostDetailForCenterVC.swift @@ -241,10 +241,7 @@ public class PostDetailForCenterVC: BaseViewController { } private func setObservable() { - // 지도뷰 풀스크린 - // 재사용률이 떨어져 ViewController에 직접 삽입합니다. - let fullMapVC = WorkPlaceAndWorkerLocationFullVC() - navigationController?.pushViewController(fullMapVC, animated: true) + } public func bind(viewModel: PostDetailViewModelable) { diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift index 65b049f6..7c53cc79 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift @@ -21,7 +21,7 @@ public protocol CenterRecruitmentPostBoardViewModelable: OnGoingPostViewModelabl public class CenterRecruitmentPostBoardVM: CenterRecruitmentPostBoardViewModelable { - weak var coordinator: RecruitmentManagementCoordinatable? + weak var coordinator: CenterRecruitmentPostBoardScreenCoordinator? public var requestOngoingPost: PublishRelay = .init() public var requestClosedPost: PublishRelay = .init() @@ -31,7 +31,7 @@ public class CenterRecruitmentPostBoardVM: CenterRecruitmentPostBoardViewModelab public var alert: Driver? - public init(coordinator: RecruitmentManagementCoordinatable?) { + public init(coordinator: CenterRecruitmentPostBoardScreenCoordinator?) { self.coordinator = coordinator let requestOngoingPostResult = requestOngoingPost @@ -88,7 +88,7 @@ public class CenterRecruitmentPostBoardVM: CenterRecruitmentPostBoardViewModelab // MARK: 카드 뷰에 사용될 ViewModel class CenterEmployCardVM: CenterEmployCardViewModelable { - weak var coordinator: RecruitmentManagementCoordinatable? + weak var coordinator: CenterRecruitmentPostBoardScreenCoordinator? // Init let id: String @@ -104,7 +104,7 @@ class CenterEmployCardVM: CenterEmployCardViewModelable { let disposeBag = DisposeBag() - init(vo: CenterEmployCardVO, coordinator: RecruitmentManagementCoordinatable? = nil) { + init(vo: CenterEmployCardVO, coordinator: CenterRecruitmentPostBoardScreenCoordinator?) { self.id = vo.postId self.coordinator = coordinator From 1f7f5fec030ddc3961a56d4f8b5848b08849a750 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 15:04:47 +0900 Subject: [PATCH 23/24] =?UTF-8?q?[IDLE-000]=20Refactor,=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=95=84=EC=9B=83=20&=20=ED=9A=8C=EC=9B=90=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EC=84=B1=EA=B3=B5=EC=8B=9C=20=EC=B5=9C=EC=B4=88?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/DI/Assembly/DomainAssembly.swift | 4 +- .../RootCoordinator+Extension.swift | 8 ++- .../RootCoordinator/RootCoordinator.swift | 11 ++++- .../Data/NetworkDataSource/API/AuthAPI.swift | 4 ++ .../Service/BaseNetworkService.swift | 11 ++++- .../Auth/DefaultAuthUseCase.swift | 14 ------ .../Setting/DefaultSettingUseCase.swift | 22 ++++++++- .../UseCaseInterface/Auth/AuthUseCase.swift | 9 ---- .../Setting/SettingScreenUseCase .swift | 12 ++++- .../Alert /IdleBigAlertController.swift | 21 ++++---- .../CenterSettingScreenCoordinator.swift | 1 + .../PasswordForDeregisterCoordinator.swift | 12 ++--- .../View/Setting/CenterSettingVC.swift | 7 --- .../CenterRecruitmentPostBoardVM.swift | 6 +-- .../ViewModel/Setting/CenterSettingVM.swift | 27 ++++++++-- .../Setting/PasswordForDeregisterVM.swift | 10 ++-- .../Coordinator/DeRegisterCoordinator.swift | 16 +++--- .../Sub/SelectReasonCoordinator.swift | 10 ++-- .../InitialScreen/InitialScreenVC.swift | 49 +++++++++++++++++++ .../InitialScreen/InitialScreenVM.swift | 33 +++++++++++++ .../LikedAndApplied/StarredAndAppliedVC.swift | 1 - .../Interface/Root/RootCoorinatable.swift | 15 ++++++ .../CoordinatingNotifications.swift | 13 +++++ 23 files changed, 235 insertions(+), 81 deletions(-) create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVC.swift create mode 100644 project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVM.swift create mode 100644 project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Root/RootCoorinatable.swift create mode 100644 project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Notification/CoordinatingNotifications.swift diff --git a/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift b/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift index b90da006..72a4cc75 100644 --- a/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift +++ b/project/Projects/App/Sources/DI/Assembly/DomainAssembly.swift @@ -46,7 +46,9 @@ public struct DomainAssembly: Assembly { } container.register(SettingScreenUseCase.self) { resolver in - return DefaultSettingUseCase() + let repository = resolver.resolve(AuthRepository.self)! + + return DefaultSettingUseCase(repository: repository) } } } diff --git a/project/Projects/App/Sources/RootCoordinator/RootCoordinator+Extension.swift b/project/Projects/App/Sources/RootCoordinator/RootCoordinator+Extension.swift index 3e032225..780c009d 100644 --- a/project/Projects/App/Sources/RootCoordinator/RootCoordinator+Extension.swift +++ b/project/Projects/App/Sources/RootCoordinator/RootCoordinator+Extension.swift @@ -7,8 +7,9 @@ import Foundation import AuthFeature +import PresentationCore -extension RootCoordinator { +extension RootCoordinator: RootCoorinatable { /// 로그인및 회원가입을 실행합니다. func auth() { @@ -59,4 +60,9 @@ extension RootCoordinator { coordinator.start() } + /// 루트 VC까지 모든 네비게이션을 제거합니다, 이후 코디네이터도 제거합니다. + func popToRoot() { + navigationController.popToRootViewController(animated: true) + childCoordinators.removeAll() + } } diff --git a/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift index 635f73a5..e2db0068 100644 --- a/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/RootCoordinator.swift @@ -7,8 +7,9 @@ import UIKit import PresentationCore +import RootFeature -class RootCoordinator: ParentCoordinator { +class RootCoordinator { struct Dependency { let navigationController: UINavigationController @@ -28,7 +29,13 @@ class RootCoordinator: ParentCoordinator { func start() { navigationController.setNavigationBarHidden(true, animated: false) - // 빈화면 VC + VM 구현 예정, 로직에 따라 Auth, CenterMain, WorkerMain을 display + // Root VC + let vc = InitialScreenVC() + let vm = InitialScreenVM(coordinator: self) + + vc.bind(viewModel: vm) + + navigationController.pushViewController(vc, animated: false) centerMain() } diff --git a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift index 3cff9482..cdc430c7 100644 --- a/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift +++ b/project/Projects/Data/NetworkDataSource/API/AuthAPI.swift @@ -126,6 +126,10 @@ extension AuthAPI: BaseAPI { } } + public var validationType: ValidationType { + .successCodes + } + public var task: Task { switch self { case .startPhoneNumberAuth: diff --git a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift index 3be3c9f4..4a863302 100644 --- a/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift +++ b/project/Projects/Data/NetworkDataSource/Service/BaseNetworkService.swift @@ -98,7 +98,9 @@ public class BaseNetworkService { if httpResponse.statusCode == 401, request.retryCount < 1 { - guard let self else { return completion(.doNotRetry) } + guard let self else { + return completion(.doNotRetry) + } guard let refreshToken = self.keyValueStore.getAuthToken()?.refreshToken else { completion(.doNotRetry) @@ -114,6 +116,11 @@ public class BaseNetworkService { provider.rx .request(.reissueToken(refreshToken: refreshToken)) + .catch({ error in + // 토큰 리프래쉬 실패 -> 재로그인 필요 + completion(.doNotRetryWithError(error)) + return .error(error) + }) .subscribe { [weak self] response in guard let self else { fatalError() } @@ -141,7 +148,7 @@ public class BaseNetworkService { .disposed(by: self.disposeBag) } else { - completion(.doNotRetry) + completion(.doNotRetryWithError(error)) } } } diff --git a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift index f2a51a28..cbb6ca45 100644 --- a/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Auth/DefaultAuthUseCase.swift @@ -36,20 +36,6 @@ public class DefaultAuthUseCase: AuthUseCase { convert(task: repository.requestCenterLogin(id: id, password: password)) } - // 센터 회원탈퇴 - public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { - convert( - task: repository.deregisterCenterAccount(reasons: reasons, password: password) - ) - } - - // 센터 로그아웃 - public func signoutCenterAccount() -> RxSwift.Single> { - convert( - task: repository.signoutCenterAccount() - ) - } - // 요양 보호사 회원가입 실행 public func registerWorkerAccount(registerState: WorkerRegisterState) -> Single> { convert( diff --git a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift index 0259d9a9..dcaa9115 100644 --- a/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift +++ b/project/Projects/Domain/ConcreteUseCase/Setting/DefaultSettingUseCase.swift @@ -9,10 +9,16 @@ import Foundation import RxSwift import UserNotifications import UseCaseInterface +import RepositoryInterface +import Entity public class DefaultSettingUseCase: SettingScreenUseCase { - public init() { } + let repository: AuthRepository + + public init(repository: AuthRepository) { + self.repository = repository + } public func checkPushNotificationApproved() -> Single { Single.create { single in @@ -71,4 +77,18 @@ public class DefaultSettingUseCase: SettingScreenUseCase { public func getApplicationPolicyUrl() -> URL { URL(string: "")! } + + // 센터 회원탈퇴 + public func deregisterCenterAccount(reasons: [Entity.DeregisterReasonVO], password: String) -> RxSwift.Single> { + convert( + task: repository.deregisterCenterAccount(reasons: reasons, password: password) + ) + } + + // 센터 로그아웃 + public func signoutCenterAccount() -> RxSwift.Single> { + convert( + task: repository.signoutCenterAccount() + ) + } } diff --git a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift index 316763cc..69e985bb 100644 --- a/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift +++ b/project/Projects/Domain/UseCaseInterface/Auth/AuthUseCase.swift @@ -40,15 +40,6 @@ public protocol AuthUseCase: UseCaseBase { password: String ) -> Single> - /// 센터 회원 탈퇴 - func deregisterCenterAccount( - reasons: [DeregisterReasonVO], - password: String - ) -> Single> - - /// 센터 로그아웃 - func signoutCenterAccount() -> Single> - // #5 /// 요양 보호사 회원가입 실행 func registerWorkerAccount( diff --git a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift index 4890118e..6d3942ca 100644 --- a/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift +++ b/project/Projects/Domain/UseCaseInterface/Setting/SettingScreenUseCase .swift @@ -7,6 +7,7 @@ import Foundation import RxSwift +import Entity public enum NotificationApproveAction: Equatable { case openSystemSetting @@ -14,7 +15,7 @@ public enum NotificationApproveAction: Equatable { case error(message: String) } -public protocol SettingScreenUseCase { +public protocol SettingScreenUseCase: UseCaseBase { /// 현재 알람수신 동의 여부를 확인합니다. func checkPushNotificationApproved() -> Single @@ -27,4 +28,13 @@ public protocol SettingScreenUseCase { /// 어플리케이션 이용약관을 가져옵니다. func getApplicationPolicyUrl() -> URL + + /// 센터 회원 탈퇴 + func deregisterCenterAccount( + reasons: [DeregisterReasonVO], + password: String + ) -> Single> + + /// 센터 로그아웃 + func signoutCenterAccount() -> Single> } diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift index 74984abb..1abc7052 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/Alert /IdleBigAlertController.swift @@ -16,6 +16,7 @@ public protocol IdleAlertViewModelable { var cancelButtonClicked: PublishRelay { get } var acceptButtonLabelText: String { get } var cancelButtonLabelText: String { get } + var dismiss: Driver? { get } var title: String { get } var description: String { get } } @@ -62,7 +63,6 @@ public class IdleBigAlertController: UIViewController { setAppearance() setAutoLayout() - setObservable() } public required init?(coder: NSCoder) { fatalError() } @@ -154,16 +154,6 @@ public class IdleBigAlertController: UIViewController { ]) } - private func setObservable() { - cancelButton.rx.tap - .subscribe(onNext: { [weak self] _ in - guard let self else { return } - modalPresentationStyle = .custom - dismiss(animated: true) - }) - .disposed(by: disposeBag) - } - public func bind(viewModel vm: IdleAlertViewModelable) { titleLabel.textString = vm.title @@ -172,6 +162,15 @@ public class IdleBigAlertController: UIViewController { acceptButton.label.textString = vm.acceptButtonLabelText cancelButton.label.textString = vm.cancelButtonLabelText + vm.dismiss? + .drive(onNext: { [weak self] in + + guard let self else { return } + modalPresentationStyle = .custom + dismiss(animated: true) + }) + .disposed(by: disposeBag) + acceptButton.rx.tap .bind(to: vm.acceptButtonClicked) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift index ffd8a112..19045a02 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift @@ -63,6 +63,7 @@ public class CenterSettingScreenCoordinator: ChildCoordinator { public func popToRoot() { /// Root까지 네비게이션을 제거합니다. + NotificationCenter.default.post(name: .popToInitialVC, object: nil) } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift index de92ccca..e7722279 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift @@ -13,12 +13,12 @@ import Entity public class PasswordForDeregisterCoordinator: ChildCoordinator { public struct Dependency { - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase let reasons: [DeregisterReasonVO] let navigationController: UINavigationController - public init(authUseCase: AuthUseCase, reasons: [DeregisterReasonVO], navigationController: UINavigationController) { - self.authUseCase = authUseCase + public init(settingUseCase: SettingScreenUseCase, reasons: [DeregisterReasonVO], navigationController: UINavigationController) { + self.settingUseCase = settingUseCase self.reasons = reasons self.navigationController = navigationController } @@ -28,13 +28,13 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { public weak var parent: DeregisterCoordinatable? public let navigationController: UINavigationController - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase let reasons: [DeregisterReasonVO] public init( dependency: Dependency ) { - self.authUseCase = dependency.authUseCase + self.settingUseCase = dependency.settingUseCase self.reasons = dependency.reasons self.navigationController = dependency.navigationController } @@ -48,7 +48,7 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { let vm = PasswordForDeregisterVM( deregisterReasons: reasons, coordinator: self, - authUseCase: authUseCase + settingUseCase: settingUseCase ) vc.bind(viewModel: vm) viewControllerRef = vc diff --git a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift index 5763f20d..98b19f48 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/View/Setting/CenterSettingVC.swift @@ -66,9 +66,6 @@ public class CenterSettingVC: BaseViewController { }() - // Observable - private let signOutComfirmed: PublishRelay = .init() - private let disposeBag = DisposeBag() public init() { @@ -100,10 +97,6 @@ public class CenterSettingVC: BaseViewController { .bind(to: viewModel.approveToPushNotification) .disposed(by: disposeBag) - signOutComfirmed - .bind(to: viewModel.signOutButtonComfirmed) - .disposed(by: disposeBag) - removeAccountButton.rx.tap .bind(to: viewModel.removeAccountButtonClicked) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift index 7c53cc79..da6b6ead 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift @@ -119,7 +119,7 @@ class CenterEmployCardVM: CenterEmployCardViewModelable { .subscribe(onNext: { [weak self] _ in guard let self else { return } - coordinator?.showCheckingApplicantScreen(postId: id) + self.coordinator?.showCheckingApplicantScreen(postId: id) }) .disposed(by: disposeBag) @@ -127,7 +127,7 @@ class CenterEmployCardVM: CenterEmployCardViewModelable { .subscribe(onNext: { [weak self] _ in guard let self else { return } - coordinator?.showPostDetailScreenForCenter(postId: id, applicantCount: vo.applicantCount) + self.coordinator?.showPostDetailScreenForCenter(postId: id, applicantCount: vo.applicantCount) }) .disposed(by: disposeBag) @@ -135,7 +135,7 @@ class CenterEmployCardVM: CenterEmployCardViewModelable { .subscribe(onNext: { [weak self] _ in guard let self else { return } - coordinator?.showEditScreen(postId: id) + self.coordinator?.showEditScreen(postId: id) }) .disposed(by: disposeBag) } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index 01862f67..8898c9d1 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -62,6 +62,7 @@ public class CenterSettingVM: CenterSettingVMable { centerProfileUseCase: CenterProfileUseCase ) { + self.coordinator = coordinator self.settingUseCase = settingUseCase self.centerProfileUseCase = centerProfileUseCase @@ -131,8 +132,16 @@ public class CenterSettingVM: CenterSettingVMable { } .asDriver(onErrorJustReturn: ("재가요양센터", "대한민국")) - // MARK: 로그아웃 확인됨 - signOutButtonComfirmed + // MARK: 로그아웃 + let signOutRequestResult = signOutButtonComfirmed.flatMap({ [settingUseCase] _ in + settingUseCase.signoutCenterAccount() + }) + .share() + + let signOutSuccess = signOutRequestResult.compactMap { $0.value } + let signOutFailure = signOutRequestResult.compactMap { $0.error } + + signOutSuccess .subscribe(onNext: { [weak self] _ in // ‼️ ‼️ 로컬에 저장된 계정 정보 삭제 ‼️ ‼️ @@ -145,7 +154,8 @@ public class CenterSettingVM: CenterSettingVMable { // MARK: Alert alert = Observable.merge( approveRequestError.map { _ in "알람수신 동의 실패" }, - fetchCenterProfileFailure.map { $0.message } + fetchCenterProfileFailure.map { $0.message }, + signOutFailure.map { $0.message } ) .map({ message in AlertWithCompletionVO( @@ -174,13 +184,15 @@ public class CenterSettingVM: CenterSettingVMable { } class CenterSingOutVM: IdleAlertViewModelable { - + var acceptButtonLabelText: String var cancelButtonLabelText: String var acceptButtonClicked: RxRelay.PublishRelay = .init() var cancelButtonClicked: RxRelay.PublishRelay = .init() + var dismiss: RxCocoa.Driver? + var title: String var description: String @@ -194,5 +206,12 @@ class CenterSingOutVM: IdleAlertViewModelable { self.description = description self.acceptButtonLabelText = acceptButtonLabelText self.cancelButtonLabelText = cancelButtonLabelText + + dismiss = Observable + .merge( + acceptButtonClicked.asObservable(), + cancelButtonClicked.asObservable() + ) + .asDriver(onErrorDriveWith: .never()) } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift index 0d4da69e..a0ef8bce 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift @@ -19,21 +19,21 @@ public class PasswordForDeregisterVM: DefaultAlertOutputable { public let exitButtonClicked: PublishRelay = .init() public var alert: RxCocoa.Driver? - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase let disposeBag = DisposeBag() public init( deregisterReasons: [DeregisterReasonVO], coordinator: PasswordForDeregisterCoordinator, - authUseCase: AuthUseCase + settingUseCase: SettingScreenUseCase ) { self.coordinator = coordinator - self.authUseCase = authUseCase + self.settingUseCase = settingUseCase let deregisterResult = deregisterButtonClicked - .flatMap { [authUseCase] password in - authUseCase.deregisterCenterAccount( + .flatMap { [settingUseCase] password in + settingUseCase.deregisterCenterAccount( reasons: deregisterReasons, password: password ) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift index 3b163457..cd97ef53 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/DeRegisterCoordinator.swift @@ -1,6 +1,6 @@ // // DeRegisterCoordinator.swift -// BaseFeature +// RootFeature // // Created by choijunios on 8/21/24. // @@ -15,12 +15,12 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { public struct Dependency { let userType: UserType - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase let navigationController: UINavigationController - public init(userType: UserType, authUseCase: AuthUseCase, navigationController: UINavigationController) { + public init(userType: UserType, settingUseCase: SettingScreenUseCase, navigationController: UINavigationController) { self.userType = userType - self.authUseCase = authUseCase + self.settingUseCase = settingUseCase self.navigationController = navigationController } } @@ -33,11 +33,11 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { var viewControllerRef: UIViewController? let userType: UserType - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase public init(dependency: Dependency) { self.userType = dependency.userType - self.authUseCase = dependency.authUseCase + self.settingUseCase = dependency.settingUseCase self.navigationController = dependency.navigationController } @@ -49,7 +49,7 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { let coordinator: SelectReasonCoordinator = .init( dependency: .init( userType: userType, - authUseCase: authUseCase, + settingUseCase: settingUseCase, navigationController: navigationController ) ) @@ -62,7 +62,7 @@ public class DeRegisterCoordinator: DeregisterCoordinatable { let coordinator = PasswordForDeregisterCoordinator( dependency: .init( - authUseCase: authUseCase, + settingUseCase: settingUseCase, reasons: reasons, navigationController: navigationController ) diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift index 92876027..189f0d1f 100644 --- a/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/Common/Deregister/Coordinator/Sub/SelectReasonCoordinator.swift @@ -14,12 +14,12 @@ public class SelectReasonCoordinator: ChildCoordinator { public struct Dependency { let userType: UserType - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase let navigationController: UINavigationController - public init(userType: UserType, authUseCase: AuthUseCase, navigationController: UINavigationController) { + public init(userType: UserType, settingUseCase: SettingScreenUseCase, navigationController: UINavigationController) { self.userType = userType - self.authUseCase = authUseCase + self.settingUseCase = settingUseCase self.navigationController = navigationController } } @@ -29,11 +29,11 @@ public class SelectReasonCoordinator: ChildCoordinator { public weak var parent: DeregisterCoordinatable? let userType: UserType - let authUseCase: AuthUseCase + let settingUseCase: SettingScreenUseCase public init(dependency: Dependency) { self.userType = dependency.userType - self.authUseCase = dependency.authUseCase + self.settingUseCase = dependency.settingUseCase self.navigationController = dependency.navigationController } diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVC.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVC.swift new file mode 100644 index 00000000..b6f52bc5 --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVC.swift @@ -0,0 +1,49 @@ +// +// InitialScreenVC.swift +// RootFeature +// +// Created by choijunios on 8/25/24. +// + +import UIKit +import BaseFeature +import PresentationCore +import RxCocoa +import RxSwift +import Entity +import DSKit + +public class InitialScreenVC: BaseViewController { + + var viewModel: InitialScreenVM? + + // Init + + // View + + // Observable + private let disposeBag = DisposeBag() + + public init() { + super.init(nibName: nil, bundle: nil) + } + + public required init?(coder: NSCoder) { fatalError() } + + private func setAppearance() { + view.backgroundColor = DSColor.gray0.color + } + + private func setLayout() { + + } + + private func setObservable() { + } + + public func bind(viewModel: InitialScreenVM) { + self.viewModel = viewModel + + } +} + diff --git a/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVM.swift b/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVM.swift new file mode 100644 index 00000000..b6aaab8c --- /dev/null +++ b/project/Projects/Presentation/Feature/Root/Sources/Screen/InitialScreen/InitialScreenVM.swift @@ -0,0 +1,33 @@ +// +// InitialScreenVM.swift +// RootFeature +// +// Created by choijunios on 8/25/24. +// + +import RxSwift +import Foundation +import PresentationCore + +public class InitialScreenVM { + + weak var coordinator: RootCoorinatable? + + let disposeBag = DisposeBag() + + public init(coordinator: RootCoorinatable? = nil) { + self.coordinator = coordinator + + + // MARK: 로그아웃, 회원탈퇴시 + NotificationCenter.default.rx.notification(.popToInitialVC) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] _ in + + guard let self else { return } + + self.coordinator?.popToRoot() + }) + .disposed(by: disposeBag) + } +} diff --git a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/LikedAndApplied/StarredAndAppliedVC.swift b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/LikedAndApplied/StarredAndAppliedVC.swift index 70c620a3..c0e7857a 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/LikedAndApplied/StarredAndAppliedVC.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/View/RecruitmentPost/LikedAndApplied/StarredAndAppliedVC.swift @@ -12,7 +12,6 @@ import RxCocoa import RxSwift import Entity import DSKit -import CenterFeature public protocol WorkerStaticPostBoardVMable { diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Root/RootCoorinatable.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Root/RootCoorinatable.swift new file mode 100644 index 00000000..060707e1 --- /dev/null +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Interface/Root/RootCoorinatable.swift @@ -0,0 +1,15 @@ +// +// asd.swift +// PresentationCore +// +// Created by choijunios on 8/25/24. +// + +import Foundation + +public protocol RootCoorinatable: ParentCoordinator { + func auth() + func workerMain() + func centerMain() + func popToRoot() +} diff --git a/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Notification/CoordinatingNotifications.swift b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Notification/CoordinatingNotifications.swift new file mode 100644 index 00000000..a10550e3 --- /dev/null +++ b/project/Projects/Presentation/PresentationCore/Sources/ScreenCoordinating/Notification/CoordinatingNotifications.swift @@ -0,0 +1,13 @@ +// +// CoordinatingNotifications.swift +// PresentationCore +// +// Created by choijunios on 8/25/24. +// + +import Foundation + +public extension Notification.Name { + + static let popToInitialVC: Notification.Name = .init("popToInitialVC") +} From 448c1df21554668592ec346ddf9ed4cfb823d6c6 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Sun, 25 Aug 2024 15:23:50 +0900 Subject: [PATCH 24/24] =?UTF-8?q?[IDLE-000]=20=EC=BD=94=EB=94=94=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SubCoordinator/CenterSettingCoordinator.swift | 12 +++++++++++- .../Setting/CenterSettingScreenCoordinator.swift | 4 ++++ .../Setting/PasswordForDeregisterCoordinator.swift | 4 ---- .../Sources/ViewModel/Setting/CenterSettingVM.swift | 9 +++++++++ .../ViewModel/Setting/PasswordForDeregisterVM.swift | 2 +- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift index 88cae60e..8142e34a 100644 --- a/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift +++ b/project/Projects/App/Sources/RootCoordinator/Main/Center /SubCoordinator/CenterSettingCoordinator.swift @@ -7,6 +7,7 @@ import UIKit import CenterFeature +import RootFeature import PresentationCore import UseCaseInterface @@ -47,6 +48,15 @@ class CenterSettingCoordinator: CenterSettingScreenCoordinatable { } public func startRemoveCenterAccountFlow() { - + let coordinator = DeRegisterCoordinator( + dependency: .init( + userType: .center, + settingUseCase: injector.resolve(SettingScreenUseCase.self), + navigationController: navigationController + ) + ) + addChildCoordinator(coordinator) + coordinator.parent = self + coordinator.start() } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift index 19045a02..cf5c6e39 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/CenterSettingScreenCoordinator.swift @@ -65,5 +65,9 @@ public class CenterSettingScreenCoordinator: ChildCoordinator { /// Root까지 네비게이션을 제거합니다. NotificationCenter.default.post(name: .popToInitialVC, object: nil) } + + public func startRemoveCenterAccountFlow() { + parent?.startRemoveCenterAccountFlow() + } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift index e7722279..537121cb 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/Setting/PasswordForDeregisterCoordinator.swift @@ -60,10 +60,6 @@ public class PasswordForDeregisterCoordinator: ChildCoordinator { parent?.removeChildCoordinator(self) } - public func cancelDeregister() { - parent?.cancelDeregister() - } - public func popToRoot() { /// Root까지 네비게이션을 제거합니다. diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift index 8898c9d1..a92db919 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/CenterSettingVM.swift @@ -151,6 +151,15 @@ public class CenterSettingVM: CenterSettingVMable { .disposed(by: disposeBag) + // MARK: 회원 탈퇴 + removeAccountButtonClicked + .subscribe(onNext: { [weak self] _ in + + self?.coordinator?.startRemoveCenterAccountFlow() + }) + .disposed(by: disposeBag) + + // MARK: Alert alert = Observable.merge( approveRequestError.map { _ in "알람수신 동의 실패" }, diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift index a0ef8bce..531638c7 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/Setting/PasswordForDeregisterVM.swift @@ -57,7 +57,7 @@ public class PasswordForDeregisterVM: DefaultAlertOutputable { exitButtonClicked .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] _ in - self?.coordinator?.cancelDeregister() + self?.coordinator?.coordinatorDidFinish() }) .disposed(by: disposeBag)